Сопоставление шаблонов в частном конструкторе данных

Я пишу простой ADT для оси сетки. В моем приложении сетка может быть как регулярной (с постоянным шагом между координатами), так и нерегулярной (иначе). Конечно, регулярная сетка — это всего лишь частный случай нерегулярной, но в некоторых ситуациях может быть целесообразно различать их (например, для выполнения некоторых оптимизаций). Итак, я объявляю свой ADT следующим образом:

data GridAxis = RegularAxis (Float, Float) Float -- (min, max) delta
              | IrregularAxis [Float]            -- [xs]

Но я не хочу, чтобы пользователь создавал искаженные оси с max < min или с неупорядоченным списком xs. Итак, я добавляю "более умные" конструкторские функции, которые выполняют некоторые базовые проверки:

regularAxis :: (Float, Float) -> Float -> GridAxis
regularAxis (a, b) dx = RegularAxis (min a b, max a b) (abs dx)

irregularAxis :: [Float] -> GridAxis
irregularAxis xs = IrregularAxis (sort xs)

Я не хочу, чтобы пользователь создавал сетки напрямую, поэтому я не добавляю GridAxis конструкторов данных в список экспорта модуля:

module GridAxis (
    GridAxis,
    regularAxis,
    irregularAxis,
    ) where

Но оказалось, что после этого я больше не могу использовать сопоставление с образцом на GridAxis. Попытка использовать это

import qualified GridAxis as GA

test :: GA.GridAxis -> Bool
test axis = case axis of
              GA.RegularAxis -> True
              GA.IrregularAxis -> False  

дает следующую ошибку компилятора:

src/Physics/ImplicitEMC.hs:7:15:
    Not in scope: data constructor `GA.RegularAxis'

src/Physics/ImplicitEMC.hs:8:15:
    Not in scope: data constructor `GA.IrregularAxis'

Есть ли что-то, чтобы обойти это?


person firegurafiku    schedule 15.11.2015    source источник


Ответы (2)


Вы можете определить синонимы шаблона конструктора. Это позволяет использовать одно и то же имя для интеллектуального построения и «тупого» сопоставления с образцом.

{-# LANGUAGE PatternSynonyms #-}

module GridAxis (GridAxis, pattern RegularAxis, pattern IrregularAxis) where
import Data.List

data GridAxis = RegularAxis_ (Float, Float) Float -- (min, max) delta
              | IrregularAxis_ [Float]            -- [xs]

-- The line with "<-" defines the matching behavior
-- The line with "=" defines the constructor behavior
pattern RegularAxis minmax delta <- RegularAxis_ minmax delta where
  RegularAxis (a, b) dx = RegularAxis_ (min a b, max a b) (abs dx)

pattern IrregularAxis xs <- IrregularAxis_ xs where
  IrregularAxis xs = IrregularAxis_ (sort xs)

Теперь вы можете сделать:

module Foo
import GridAxis

foo :: GridAxis -> a
foo (RegularAxis (a, b) d) = ...
foo (IrregularAxis xs) = ...

А также используйте RegularAxis и IrregularAxis в качестве умных конструкторов.

person András Kovács    schedule 15.11.2015

Это выглядит как пример использования шаблона синонимы.

В основном вы не экспортируете настоящий конструктор, а только «умный»

{-# LANGUAGE PatternSynonyms #-}
module M(T(), SmartCons, smartCons) where

data T = RealCons Int

-- the users will construct T using this
smartCons :: Int -> T
smartCons n = if even n then RealCons n else error "wrong!"

-- ... and destruct T using this
pattern SmartCons n <- RealCons n

Затем другой модуль, импортирующий M, может использовать

case someTvalue of
   SmartCons n -> use n

и например

let value = smartCons 23 in ...

но не может использовать RealCons напрямую.


Если вы предпочитаете оставаться на базовом языке Haskell без расширений, вы можете использовать «тип представления».

module M(T(), smartCons, Tview(..), toView) where
data T = RealCons Int
-- the users will construct T using this
smartCons :: Int -> T
smartCons n = if even n then RealCons n else error "wrong!"

-- ... and destruct T using this
data Tview = Tview Int
toView :: T -> Tview
toView (RealCons n) = Tview n

Здесь пользователи имеют полный доступ к типу представления, который может быть создан/уничтожен свободно, но имеют только ограниченный начальный конструктор для фактического типа T. Уничтожение фактического типа T возможно путем перехода к типу представления

case toView someTvalue of
  Tview n -> use n

Для вложенных шаблонов все становится более громоздким, если вы не включите другие расширения, такие как ViewPatterns.

person chi    schedule 15.11.2015