Извините, если вопрос очень элементарный, я все еще очень новичок в Haskell. Допустим, у меня есть функция, которая может работать только с двумя числами, находящимися в золотом рационе (1.618), как я могу определить типы myfun x y, чтобы брать только золотые отношения. Что произойдет, если я вызову myfun без чисел золотого номера из моей программы (ошибка компиляции?)? Что произойдет, если вызов без номеров золотого отношения будет выполнен во время выполнения через пользовательский ввод?
Использование типов Haskell для замены утверждений assert или проверки на других языках
Ответ 1
Вам может понадобиться ADT, который может быть создан только с золотыми номерами чисел, а затем напишите myfun, чтобы принять этот тип данных.
Я предположил Integer как базовый тип, но вы могли бы использовать другие (например: Double или Float) или даже быть полиморфными.
1) Сделайте ADT
module Golden (Gold, getGold, buildGold) where
data Gold = G Integer Integer
getGold :: Gold -> (Integer, Integer)
getGold (G x y) = (x, y)
buildGold :: Integer -> Integer -> Maybe Gold
buildGold x y
| isGolden x y = Just (G x y)
| otherwise = Nothing
Обратите внимание, что этот модуль экспортирует тип Gold
, но не конструктор (а именно, не G
). Таким образом, единственный способ получить значение типа Gold
- с buildGold
, который выполняет проверку времени выполнения, но только один - поэтому значения Gold могут использоваться и считаться золотым коэффициентом для всех потребителей без проверка.
2) Используйте ADT для сборки myfun
myfun :: Gold -> ???
myfun g = expr
where (x, y) = getGold g
Теперь, если вы попытаетесь вызвать myfun
с не-золотым номером (значение не типа Gold
), вы получите ошибку времени компиляции.
Резюме
Для создания функции золотых чисел buildGold
должна использоваться функция, которая заставляет проверять число.
Обратите внимание, что проверяется, когда! У вас есть гарантия времени компиляции, что myfun
и все другие функции, которые вы хотите использовать с Gold
, всегда предоставляются золотыми коэффициентами. Ввод программы (от пользователя, сети или где-либо еще) все еще требует проверки времени выполнения и того, что предоставляет buildGold
; очевидно, что никогда не будет такой программы, которая может пообещать, что человек не будет вводить что-то нежелательное.
Альтернативы, приведенные в комментариях к вашему вопросу, также заслуживают рассмотрения. ADT немного тяжелый вес, если вам нужна только одна функция, myfun
, которая может выйти из строя, а затем просто myfun :: (Integer, Integer) -> Maybe ???
.
Ответ 2
Самый простой способ - использовать интеллектуальные конструкторы, которые используют функцию от Int до GoldenInt, которая проверяет, ваши значения находятся в требуемых соотношениях.
С большим усилием вы можете использовать тип номера уровня, чтобы гарантировать, что проверка времени выполнения не требуется, однако, учитывая, что вы новичок, Я бы придерживался метода интеллектуального конструктора.
Том, приведенный выше, является примером этой идиомы.
Ответ 3
Лучшее, что вы можете сделать, это проверка времени выполнения. Может быть какое-то исчисление уровня на уровне, о котором я не знаю (см. Комментарий luqui), но это не имеет значения в Haskell.
Вы можете использовать assert, который вы хотите заменить,
checker :: a -> b -> Bool
checker x y = x * 1.618 `approxEqual` y
unsafeMyfun :: a -> b -> c
unsafeMyfun x y = assert (checker x y) (doRealThingWith a b)
или верните a Maybe a
(или Either err a
), чтобы избежать исключений, которые невозможно поймать в чистых функциях,
myfun :: a -> b -> Maybe c
myfun x y = do
guard $ checker x y
return $ doRealThingWith x y
или использовать пользовательский тип контракта, как в ответе Тома и т.д. В любом случае невозможно проверить ограничение во время компиляции. Фактически, из-за монады IO любое ограничение времени компиляции не может быть точным.