Ответ 1
[edit from Author, 6 лет спустя] Это бесполезно длинный ответ, и я не уверен, почему он был принят. Используйте maybe
или Data.Maybe.fromMaybe
, как указано в самом высоком ответе. Далее следует скорее мысленный эксперимент, чем практический совет.
Итак, вы пытаетесь создать функцию, которая работает для группы разных типов. Это подходящее время для занятий. Если вы запрограммировали на Java или С++, класс в Haskell похож на интерфейс на этих языках.
class Nothingish a where
nada :: a
Этот класс определяет значение nada
, которое предполагается эквивалентным классу Nothing
. Теперь самое интересное: создание экземпляров этого класса!
instance Nothingish (Maybe a) where
nada = Nothing
Для значения типа Maybe a
Ничто-подобное значение, ну, Nothing
! Это будет странный пример через минуту. Но до этого пусть также создаст список экземпляров этого класса.
instance Nothingish [a] where
nada = []
Пустой список вроде как ничего, не так ли? Итак, для String (который является списком Char), он вернет пустую строку ""
.
Числа также являются легкой реализацией. Вы уже указали, что 0 явно представляет "Nothingness" для чисел.
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
Обратите внимание, что вам все равно нужно указать тип вашего "ничего" или указать, какой тип возврата вы ожидаете.
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)
Теперь он работает только в том случае, если все три вещи, которые могут быть одного типа. Таким образом, он выведет, что 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
Ну, я думаю, это был достаточно длинный и раздутый ответ. Наслаждайтесь.