Что означает синтаксис "просто" в 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 ответов:
на самом деле это просто обычный конструктор типа, который определяется в прелюдия, которая является стандартной библиотекой, которая автоматически импортируется в каждый модуль.
что может быть, конструктивно
определение выглядит примерно так:
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значения остановка как только aNothingвстречается, так что это похоже на немедленное прерывание или бесполезное возвращение в середине вычисления.Вы Могли Бы Написать, Может Быть,
как я уже говорил, нет ничего присущего
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.