Что означает синтаксис "просто" в Haskell?


я прочесал интернет для фактического объяснения того, что делает это ключевое слово. Каждый учебник Haskell, который я посмотрел, просто начинает использовать его случайным образом и никогда не объясняет, что он делает (и я смотрел на многих).

вот основной кусок кода из Реальный Мир Хаскелл использует Just. Я понимаю, что код делает, но я не понимаю, что цель или функция Just есть.

lend amount balance = let reserve    = 100
                      newBalance = balance - amount
                  in if balance < reserve
                     then Nothing
                     else Just newBalance

из того, что я наблюдал, это связанные с Maybe печатать, но это все, что мне удалось узнать.

хорошее объяснение чего Just значит будет очень ценится.

5 98

5 ответов:

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

что может быть, конструктивно

определение выглядит примерно так:

data Maybe a = Just a
             | Nothing

это объявление определяет тип,Maybe a, который параметризуется переменной типа a, что просто означает, что вы можете использовать его с любым типом вместо a.

Строительство и разрушение

тип имеет два конструктора, Just a и Nothing. Если тип имеет несколько конструкторов, это означает, что значение типа должно быть построено только с одним из возможных конструкторов. Для этого типа значение было либо построено через Just или Nothing, нет никаких других (без ошибок) возможностей.

С Nothing не имеет типа параметра, когда он используется в качестве конструктора он называет a постоянное значение, которое является членом типа Maybe a для всех типов a. Но это Just конструктор имеет параметр типа, что означает, что при использовании в качестве конструктора он действует как функция от типа a до Maybe a, т. е. он имеет вид a -> Maybe a

Итак, конструкторы типа строят значение этого типа; другая сторона вещей-это когда вы хотели бы использовать это значение, и именно там сопоставление шаблонов вступает в игру. В отличие от функций, конструкторы могут быть используется в выражениях привязки шаблонов, и это способ, которым вы можете сделать case analysis значений, принадлежащих типам с несколькими конструкторами.

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

case maybeVal of
    Nothing   -> "There is nothing!"
    Just val  -> "There is a value, and it is " ++ (show val)

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

Что Может Означать

может быть, вы уже были знакомы с тем, как это работает; на самом деле нет никакой магии Maybe значения, это просто нормальный алгебраический тип данных Хаскелла (ADT). Но он используется совсем немного, потому что он эффективно "поднимает" или расширяет тип, такой как Integer из вашего примера, в новый контекст, в котором он имеет дополнительное значение (Nothing) это означает отсутствие ценности! Затем система типов требует, чтобы вы проверили это дополнительное значение, прежде чем оно позволит вам получить Integer это может быть там. Это предотвращает значительное количество ошибок.

многие языки сегодня обрабатывают такое значение "без значения" с помощью нулевых ссылок. Тони хор, выдающийся компьютерный ученый (он изобрел Quicksort и является лауреатом премии Тьюринга), владеет до этого как его "ошибка на миллиард долларов". Тип "может быть" - это не единственный способ исправить это, но он оказался эффективным способом сделать это.

может быть, как функтор

идея преобразования одного типа в другой такой, что операции над старым типом могут и преобразование для работы над новым типом-это концепция, лежащая в основе класса типа Haskell под названием Functor, который Maybe a имеет полезный экземпляр.

Functor обеспечивает метод называется fmap, который отображает функции, которые варьируются по значениям из базового типа (например,Integer) к функциям, которые варьируются по значениям от поднятого типа (например,Maybe Integer). Функция, преобразованная с помощью fmap для работы на Maybe значение работает следующим образом:

case maybeVal of
  Nothing  -> Nothing         -- there is nothing, so just return Nothing
  Just val -> Just (f val)    -- there is a value, so apply the function to it

так что если у вас есть Maybe Integer стоимостью m_x и Int -> Int функции f, вы можете сделать fmap f m_x чтобы применить функцию f непосредственно Maybe Integer не беспокоясь, если это на самом деле есть значение или нет. На самом деле, вы могли бы применить целую цепочку поднятых Integer -> Integer функции Maybe Integer значения и только должны беспокоиться о явной проверке Nothing один раз, когда вы закончите.

может быть, как монада

я не знаю, насколько вы знакомы с понятием Monad пока нет, но вы хотя бы использовали IO a раньше, и подпись типа IO a выглядит удивительно похожим на Maybe a. Хотя IO в том, что он не подвергайте его конструкторы для вас и, таким образом, могут быть "запущены" только системой времени выполнения Haskell, это все еще Functor кроме того,Monad. На самом деле, есть важный смысл, в котором Monad - это просто особый вид Functor С некоторыми дополнительными функциями, но это не место для разговора об этом.

во всяком случае, монады любят IO сопоставьте типы с новыми типами, которые представляют "вычисления, которые приводят к значениям", и вы можете поднять функции в Monad типы через очень fmap-как функция называется liftM что превращает регулярную функцию в " вычисление, которое приводит к значению, полученному при оценке функции."

вы, наверное, догадались (если вы читали это далеко), что Maybe тоже Monad. Он представляет собой "вычисления, которые могут не возвращать значение". Так же, как и с fmap например, это позволяет выполнять целую кучу вычислений без явной проверки ошибок после каждого шага. И в самом деле так элемент Monad экземпляр построен, вычисление на Maybe значения остановка как только a Nothing встречается, так что это похоже на немедленное прерывание или бесполезное возвращение в середине вычисления.

Вы Могли Бы Написать, Может Быть,

как я уже говорил, нет ничего присущего Maybe тип, который выпекается в синтаксисе языка или системы. Если Haskell не предоставил его по умолчанию, вы можете предоставить все его функциональность себя! На самом деле, вы можете написать его снова самостоятельно, с разными именами, и получить ту же функциональность.

надеюсь, вы понимаете Maybe тип и его конструкторы сейчас, но если есть еще что-то непонятное, дайте мне знать!

большинство текущих ответов являются сугубо техническими объяснениями того, как Just и друзья работают; я подумал, что могу попробовать объяснить, для чего это нужно.

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

Haskell решает эту проблему, заставляя вас явно отмечать места, где вы можете иметь Nothing (его версия null). В принципе, если ваша функция обычно возвращает тип Foo, он вместо этого должен возвращать тип Maybe Foo. Если вы хотите указать, что нет никакого значения, верните Nothing. Если вы хотите вернуть значение bar, следует вернуть Just bar.

так что в принципе, если вы не можете иметь Nothing, вам не нужно Just. Если вы может быть Nothing, вы Just.

в этом нет ничего волшебного Maybe; он построен на системе типа Haskell. Это означает, что вы можете использовать все обычные Хаскелл шаблоны трюки с ним.

дали типа t, стоимостью Just t существующее значение типа t, где Nothing представляет собой неспособность достичь значения или случай, когда наличие значения было бы бессмысленным.

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

для другого примера, это может быть использовано в делении, определяя функцию деления, которая принимает a и b, и возвращает Just a/b если b не равно нулю, а Nothing в противном случае. Он часто используется таким образом, как удобная альтернатива исключениям или, как ваш предыдущий пример, для замены значений, которые не имеют смысла.

общая функция a - >b может найти значение типа b для каждого возможного значения типа a.

в Haskell не все функции полной. В данном конкретном случае функция lend не является полным - он не определен для случая, когда баланс меньше резерва (хотя, на мой вкус, было бы более разумно не допускать, чтобы newBalance был меньше резерва - как есть, вы можете заимствовать 101 из баланса 100).

другие проекты, которые имеют дело с общей функции:

  • исключение броска при проверке входного значения не соответствует диапазону
  • возвращает специальное значение (примитивный тип): favourite choice-это отрицательное значение для целочисленных функций, которые должны возвращать натуральные числа (например, String.indexOf-когда подстрока не найдена, возвращаемый индекс обычно считается отрицательным)
  • возвращает специальное значение (указатель): NULL или некоторые такие
  • молча вернуться без делать что угодно: например,lend может быть написано, чтобы вернуть старый баланс, если условие для кредитования не выполняется
  • возвращает специальное значение: Nothing (или Left wrapping some error description object)

это необходимые ограничения дизайна в языках, которые не могут обеспечить полноту функций (например, Agda может, но это приводит к другим осложнениям, таким как становление Turing-неполным).

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

проблема с молчаливым отбрасыванием сбоя также очевидна-вы ограничиваете то, что вызывающий может сделать с функцией. Например, если lend возвращенный старый баланс, вызывающий абонент не имеет возможности узнать, изменился ли баланс. Это может быть или не быть проблемой, в зависимости от намеченной цели.

решение Хаскелла заставляет вызывающего частичную функцию дело с типом, как Maybe a или Either error a из-за возвращаемого типа функции.

таким образом lend Как определено, это функция, которая не всегда вычисляет новый баланс - для некоторых обстоятельств новый баланс не определен. Мы сигнализируем об этом обстоятельстве вызывающему абоненту либо возвращая специальное значение Nothing, либо обертывая новый баланс в Just. Теперь у абонента есть свобода выбора: либо обрабатывать отказ в кредитовании особым образом, либо игнорировать и использовать старый баланс - например, maybe oldBalance id $ lend amount oldBalance.

функции if (cond :: Bool) then (ifTrue :: a) else (ifFalse :: a) должны иметь тот же тип ifTrue и ifFalse.

Итак, когда мы пишем then Nothing, мы должны использовать Maybe a тип else f

if balance < reserve
       then (Nothing :: Maybe nb)         -- same type
       else (Just newBalance :: Maybe nb) -- same type