Как получить значение Maybe в Хаскеле - PullRequest
34 голосов
/ 09 февраля 2011

Я относительно новичок в Haskell и начал читать "Real World Haskell".

Я просто наткнулся на тип Maybe и у меня есть вопрос о том, как получить действительное значение из Just 1 дляпример.

Я написал следующий код:

combine a b c = (eliminate a, eliminate b, eliminate c)
                where eliminate (Just a) = a
                      eliminate Nothing = 0

Это прекрасно работает, если я использую:

combine (Just 1) Nothing (Just 2)

Но если я изменю, например, 1 наa String это не работает.

Я думаю, я знаю почему: потому что eliminate должен возвращать один тип, который, в данном случае, Int.Но как я могу изменить eliminate, чтобы иметь дело хотя бы со строками (или, может быть, со всеми типами)?

Ответы [ 5 ]

43 голосов
/ 09 февраля 2011

Из стандарта Prelude,

maybe :: b -> (a -> b) -> Maybe a -> b
maybe n _ Nothing = n
maybe _ f (Just x) = f x

Если задано значение по умолчанию и функция, примените функцию к значению в Maybe или верните значение по умолчанию.

Ваш eliminate может быть записан maybe 0 id, например, применить функцию идентификации или вернуть 0.

Из стандарта Data.Maybe,

fromJust :: Maybe a -> a
fromJust Nothing = error "Maybe.fromJust: Nothing"
fromJust (Just x) = x

Это частичная функция (не возвращает значение для каждого входа, в отличие от функции total , которая делает), но извлекает значение, когда это возможно.

23 голосов
/ 09 февраля 2011

[редактировать от автора, 6 лет спустя] Это излишне длинный ответ, и я не уверен, почему он был принят.Используйте maybe или Data.Maybe.fromMaybe, как предложено в ответе с наибольшим количеством голосов.Далее следует скорее мысленный эксперимент, а не практический совет.

Итак, вы пытаетесь создать функцию, которая работает для группы разных типов.Это хорошее время, чтобы сделать класс.Если вы программировали на Java или C ++, класс в Haskell является своего рода интерфейсом в этих языках.

class Nothingish a where
    nada :: a

Этот класс определяет значение nada, которое должно быть эквивалентным классуNothing.Теперь самое интересное: создание экземпляров этого класса!

instance Nothingish (Maybe a) where
    nada = Nothing

Для значения типа Maybe a значение типа Nothing равно ну, Nothing!Это будет странный пример через минуту.Но до этого давайте сделаем списки экземпляром этого класса тоже.

instance Nothingish [a] where
    nada = []

Пустой список вроде как Ничего, верно?Таким образом, для String (который является списком Char) будет возвращена пустая строка, "".

Числа также являются простой реализацией.Вы уже указали, что 0, очевидно, представляет «Ничто» для чисел.

instance (Num a) => Nothingish a where
    nada = 0

Это на самом деле не будет работать, если вы не поместите специальную строку вверху вашего файла

{-# LANGUAGE FlexibleInstances, UndecidableInstances, OverlappingInstances #-}

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

Итак, теперь у вас есть этот класс и его экземпляры ... теперь давайте просто переписаем вашу функцию для использованияих!

eliminate :: (Nothingish a) => Maybe a -> a
eliminate (Just a) = a
eliminate Nothing  = nada

Обратите внимание, я только изменил 0 на nada, а остальное тоже самое.Давайте попробуем!

ghci> eliminate (Just 2)
2
ghci> eliminate (Just "foo")
"foo"
ghci> eliminate (Just (Just 3))
Just 3
ghci> eliminate (Just Nothing)
Nothing
ghci> :t eliminate
eliminate :: (Nothingish t) => Maybe t -> t
ghci> eliminate Nothing
error! blah blah blah...**Ambiguous type variable**

Отлично смотрится на ценности и прочее.Заметьте, что (Просто Ничто) превращается в Ничто, понимаете?Это был странный пример, «Может быть, в Возможно».В любом случае ... как насчет eliminate Nothing?Ну, результирующий тип неоднозначен.Он не знает, что мы ожидаем.Таким образом, мы должны сказать ему, какой тип нам нужен.

ghci> eliminate Nothing :: Int
0

Попробуйте и попробуйте его для других типов;вы увидите, что он получает nada для каждого.Итак, теперь, когда вы используете эту функцию с вашей combine функцией, вы получаете следующее:

ghci> let combine a b c = (eliminate a, eliminate b, eliminate c)
ghci> combine (Just 2) (Just "foo") (Just (Just 3))
(2,"foo",Just 3)
ghci> combine (Just 2) Nothing (Just 4)
error! blah blah Ambiguous Type blah blah

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

ghci> combine (Just 2) (Nothing :: Maybe Int) (Just 4)
(2,0,4)
ghci> combine (Just 2) Nothing (Just 4) :: (Int, Int, Int)
(2,0,4)

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

combine :: (Nothingish a) => Maybe a -> Maybe a -> Maybe a -> (a,a,a)
combine a b c = (eliminate a, eliminate b, eliminate c)

Теперь она работает, только если все три вещи Maybe одного типа.Таким образом, это будет означать, что Nothing того же типа, что и другие.

ghci> combine (Just 2) Nothing (Just 4)
(2,0,4)

Нет двусмысленности, ура!Но теперь ошибочно смешивать и сопоставлять, как мы делали раньше.

ghci> combine (Just 2) (Just "foo") (Just (Just 3))
error! blah blah  Couldn't match expected type  blah blah
blah blah blah    against inferred type         blah blah

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

3 голосов
/ 26 февраля 2014

Я тоже новичок в Haskell, так что я не знаю, существует ли он еще на платформе (я уверен, что он есть), но как насчет функции "get or else", чтобы получить значение, если оно существует? , иначе вернуть значение по умолчанию?

getOrElse::Maybe a -> a -> a
getOrElse (Just v) d = v
getOrElse Nothing d  = d
2 голосов
/ 29 августа 2016

Это ответ, который я искал, когда пришел к этому вопросу:

https://hackage.haskell.org/package/base-4.9.0.0/docs/Data-Maybe.html#v:fromJust

... и аналогично для Either:

https://hackage.haskell.org/package/either-unwrap-1.1/docs/Data-Either-Unwrap.html

Они предоставляют функции, которые я бы написал сам, которые разворачивают значение из его контекста.

1 голос
/ 09 февраля 2011

Сигнатура типа функции eliminate:

eliminate :: Maybe Int -> Int

Это потому, что она возвращает 0 для Nothing, заставляя компилятор предполагать, что a :: Int в вашей функции eliminate.Следовательно, компилятор выводит сигнатуру типа функции combine следующим образом:

combine :: Maybe Int -> Maybe Int -> Maybe Int -> (Int, Int, Int)

, и именно поэтому она не работает, когда вы передаете ей строку.

ЕслиВы написали это как:

combine a b c = (eliminate a, eliminate b, eliminate c)
                where eliminate (Just a) = a
                      eliminate Nothing = undefined

тогда это работало бы со String или любым другим типом.Причина заключается в том, что undefined :: a, что делает eliminate полиморфным и применимым к типам, отличным от Int.

Конечно, это не является целью вашего кода, т. Е. Сделать функцию объединения общей.

Действительно, даже если применение combine к некоторым аргументам Nothing будет успешным (это потому, что Haskell по умолчанию ленив), как только вы попытаетесь оценить результаты, вы получите ошибку времени выполнения как undefined нельзя оценить чем-то полезным (проще говоря).

...