Использование типов Haskell для замены операторов assert или проверки на других языках - PullRequest
13 голосов
/ 10 июля 2010

Извините, если вопрос очень элементарный, я все еще очень плохо знаком с Хаскеллом. Допустим, у меня есть функция, которая может работать только с двумя числами, которые находятся в золотом рационе (1.618), как мне определить типы myfun xy , чтобы принимать только числа с золотым отношением , Что произойдет, если я вызову myfun без цифр золотого сечения из моей программы (ошибка компиляции?)? Что произойдет, если вызов без номеров с золотым сечением будет выполнен во время выполнения через пользовательский ввод?

Ответы [ 3 ]

18 голосов
/ 10 июля 2010

Возможно, вы захотите 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), вы получите ошибку времени компиляции.

Recap Для построения золотых чисел должна использоваться функция buildGold, которая заставляет проверять число.

Обратите внимание, что проверяется, когда! У вас есть гарантия времени компиляции, что myfun и все другие функции, которые вы хотите использовать с Gold, всегда имеют золотые отношения. Ввод программы (от пользователя, сети или где-либо еще) все еще нуждается в проверках во время выполнения, и это то, что обеспечивает buildGold; очевидно, никогда не будет программы, которая может обещать, что человек не будет печатать что-то нежелательное.

Альтернативы, приведенные в комментариях к вашему вопросу, также заслуживают рассмотрения. ADT имеет небольшой вес, если вам нужна только одна функция, myfun, которая может дать сбой, тогда просто наберите myfun :: (Integer, Integer) -> Maybe ???.

10 голосов
/ 11 июля 2010

Самым простым способом является использование интеллектуальных конструкторов , которые используют функцию от Int до GoldenInt, которая проверяет, что ваши значения находятся в требуемых соотношениях.

Прилагая больше усилий, вы можете использовать номера уровней типа , чтобы гарантировать, что проверка во время выполнения не требуется, однако, учитывая, что вы новичок, я бы придерживался метода умного конструктора.

Tom'sОтвет выше является примером этой идиомы.

1 голос
/ 10 июля 2010

Лучшее, что вы можете сделать на практике, - это проверка во время выполнения. Могло быть какое-то исчисление уровня типа, которого я не знаю (см. Комментарий Люки), но это не практично в 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)

или вернуть Maybe a (или Either err a), чтобы избежать исключений, которые не могут быть перехвачены в чистых функциях,

myfun :: a -> b -> Maybe c
myfun x y = do
              guard $ checker x y
              return $ doRealThingWith x y

или используйте пользовательский тип контракта, как в ответе Тома и т. Д. В любом случае, невозможно проверить ограничение во время компиляции . Фактически, из-за монады ввода-вывода любое ограничение времени компиляции не может быть точным.

...