Подтвердить что ты не робот

Входные проверки в конструкторах данных Haskell

Как я могу добавить проверки ввода в конструкторы данных Haskell? Скажем, у меня есть

import Data.Time.Calendar

data SchedulePeriod = SchedulePeriod { startDate :: Day
    , endDate :: Day
    , accrualStart :: Day
    , accrualEnd :: Day
    , resetDate :: Day
    , paymentDate :: Day
    , fraction :: Double }
    deriving (Show)

и я хочу наложить ограничение startDate < endDate. Есть ли способ сделать это, не создавая абстрактный тип данных?

4b9b3361

Ответ 1

Стандартный способ - использовать интеллектуальный конструктор, который проверяет предварительное условие перед созданием значения и не экспортирует используемый им конструктор. Конечно, это создает абстрактный тип данных, как вы сказали.

Единственный способ добиться этого без умного конструктора - это действительно злой хакерство типа системы (и вы не сможете использовать стандартный тип Day).

Ответ 2

Примите ответ. Я просто пишу это, поэтому я могу объяснить умные деструкторы, о которых я упоминал в комментарии, и я не могу подправить объяснение в комментарии.

Скажем, что у вас есть тип:

data T x y z = A | B x | C y z

ehird уже объяснил, как абстрагироваться от конструктора, который предназначен только для создания "умных" конструкторов. Как вы уже упоминали, это требует скрытия конструкторов, и вы не можете использовать их для сопоставления шаблонов. Однако вы можете решить это с помощью "умного" деструктора, что эквивалентно сопоставлению шаблонов со всеми возможными конструкторами.

Чтобы объяснить это, позвольте сначала начать с того, как мы будем писать функцию типа T, если бы были открыты конструкторы:

myFunction :: T x y z -> d
myFunction t = case t of
    A     -> f1
    B x   -> f2 x
    C y z -> f3 y z

Из сигнатуры типа функции мы знаем, что типы f1, f2 и f3 должны быть:

f1 :: d
f2 :: x -> d
f3 :: y -> z -> d

Итак, если бы мы обобщали myFunction как умный деструктор, мы просто передаем f1, f2 и f3 в качестве параметров для него:

smartDestructor :: d -> (x -> d) -> (y -> z -> d) -> t -> d
smartDestructor f1 f2 f3 t = case t of
    A     -> f1
    B x   -> f2 x
    C y z -> f3 y z

Итак, если вы экспортируете smartDestructor, тогда люди могут в основном сопоставлять шаблоны с вашим типом, не требуя доступа к конструкторам.

Если раньше вы использовали функции maybe или either, тогда вы использовали интеллектуальный деструктор, хотя в тех случаях конструкторы не скрыты, поэтому они в основном предоставляются как удобные функции:

maybe :: b -> (a -> b) -> Maybe a -> b
maybe f1 f2 m = case m of
    Nothing -> f1
    Just  a -> f2 x

either :: (a -> c) -> (b -> c) -> Either a b -> c
either f1 f2 e = case e of
    Left  a -> f1 a
    Right b -> f2 b

В вашем случае цель интеллектуального деструктора заключается в том, что вы можете скрыть конструкторы и не выставлять конструкторы.