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

Использование типов Haskell для замены утверждений assert или проверки на других языках

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

4b9b3361

Ответ 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 любое ограничение времени компиляции не может быть точным.