В исходный код GHC.BaseApplicative Maybe
определяется как:
instance Applicative Maybe where
pure = Just
...
Мне интересно, почему чистое определение игнорирует Nothing
?
Согласно этому определению, я ожидаю, что
pure Nothing
должен уменьшиться до Just Nothing
, потому что pure = Just
и
Prelude> Just Nothing
Just Nothing
а не на самом деле:
Prelude> pure Nothing
Nothing
Почему это волшебство? Что я не так? Спасибо!
Он действительно нет игнорирует Nothing
, но вам нужно указать для которыйApplicative
, что вы запускаете функцию pure
, если мы запустим это с -XTypeApplications
, мы можем указать тип Applicative
, и тогда мы получим:
$ ghci -XTypeApplications
GHCi, version 8.0.2: http://www.haskell.org/ghc/ :? for help
Prelude> pure @Maybe Nothing
Just Nothing
Если вы не укажете тип, интерпретатор будет "по умолчанию" использовать определенный вариант Applicative
(в данном случае IO
), так что это означает, что он вернет значение, а стандартное поведение интерпретатора - распечатать значение. «завернутый» в оболочку IO
.
Причина, по которой Applicative
принимает pure = Just
, заключается в том, что он должен соответствовать экземпляру Functor
. Этот экземпляр функтора определяется как:
instance Functor Maybe where fmap f (Just x) = Just (f x) fmap _ Nothing = Nothing
Теперь один из законов, который необходимо выполнить:
fmap f x = pure f <*> x
Итак, если мы определим pure _ = Nothing
, это будет означать fmap f x = Nothing <*> x
, что означает, что мы «потеряли информацию» о f
. В результате, вероятно, единственным разумным решением было бы то, что fmap
всегда возвращает Nothing
, но это не имеет большого смысла в качестве функтора. Кроме того, это нарушит другое ограничение на уровне функтора, которое гласит, что fmap id x
всегда должен возвращать x
.
pure
перегружен. Когда вы просто набираете pure Nothing
отдельно, вы не указываете, какую версию pure
вызывать. Сам по себе он имеет тип
pure Nothing :: Applicative f => f (Maybe a)
Если вы конкретно скажете, что хотите Maybe (Maybe a)
, вы получите то, что ожидаете:
ghci> pure Nothing :: Maybe (Maybe a)
Just Nothing
Однако, когда вы вводите pure Nothing
в GHCi, он фактически выбирает создание IO (Maybe a)
. Это потому, что GHCi пытается выполнить запустить любое действие IO
, которое он может. Когда он выполняет действие pure Nothing :: IO (Maybe a)
, вы получаете обратно Nothing
. Вы также можете указать подпись типа:
ghci> pure Nothing :: IO (Maybe a)
Nothing
То есть вы никогда не называли версию pure = Just
pure
. Вы вызвали другую функцию, также названную pure
. Вот почему у вас такое интересное поведение.