#haskell
#haskell
Вопрос:
Я пытаюсь выяснить, как прочитать следующее определение типа класса:
Prelude Data.Functor.Identity Control.Monad.Reader> :i ask
class Monad m => MonadReader r (m :: * -> *) | m -> r where
ask :: m r
...
m
это тип более высокого уровня, и он должен быть monad
.
Но что это m -> r
означает?
Пытаюсь немного поиграть с ask
следующим:
Prelude Data.Functor.Identity Control.Monad.Reader> ask "Hello"
"Hello"
Почему я могу передать аргумент в ask
? Смотрим на сигнатуру типа:
ask :: m r
Я не могу распознать, что я могу передать аргумент ask
.
Комментарии:
1. Я думаю , идея заключается в том, что при попытке вызвать
ask
наString
, вывод типа позволяетm
быть унифицированным с функцией monad, так что в итоге вы получаетеask :: String -> String
.2.
m -> r
это функциональная зависимость; знанияm
достаточно, чтобы сделать вывод, чтоr
это. По сути, это говорит о том, что вы не можете определить несколькоMonadReader
экземпляров для одной и той же монады, но с разнымиr
именами.3. У меня нет знаний, чтобы дать подробный ответ, но вкратце:
m -> r
это функциональная зависимость . А функции сами по себе являются экземпляром монады, так что это та монада, которую выбирает Haskell при выполненииask "Hello"
. (На самом деле, это просто сама монада Reader.)
Ответ №1:
m -> r
Это функциональная зависимость, которая грубо указывает, что при попытке выбрать экземпляр MonadReader
для использования, знания m
достаточно для того, чтобы знать r
. Другими словами, вы не можете определить два отдельных экземпляра с одинаковым, m
но разным r
значением.
Теперь, чтобы определить, какое определение ask
использовать, мы переходим к выводу типа. Из его определения мы знаем, что он ask
имеет тип MonadReader r m => m r
. Из его использования в ask "Hello"
мы знаем, что он также должен иметь тип like a -> b
; более конкретно, мы знаем, что он a
объединяется с String
, поскольку это тип "Hello"
. Итак, наша задача — объединить MonadReader r m => m r
с String -> b
.
Это довольно просто. Переписывая String -> b
в префиксной нотации и используя явные круглые скобки, мы можем объединить их вместе:
MonadReader r m => m r
((->) String) b
Так m ~ ((->) String)
и r ~ b
(хотя мы все еще не знаем, что r/b
должно быть). Просматривая доступные экземпляры MonadReader
, мы находим (уникальный) экземпляр для m ~ (->) String
(или, в более общем смысле, (->) r
):
instance MonadReader r ((->) r) where
ask = id
local f m = m . f
Итак, теперь мы знаем, что для нашего выбора m
, ask = id
. (Это также позволяет нам увидеть это r ~ b ~ String
.)
И так, ask "Hello" == id "Hello" == "Hello"
.
Обратите внимание, что ask
не обязательно должно быть функцией. Это также может быть значением типа Reader r a
, и в этом случае runReader
необходимо использовать для извлечения функции.
> :t runReader ask
runReader ask :: a -> a
> runReader ask "Hello"
"Hello"
Это также может быть более сложная монада, включающая ReaderT
:
> :t runReaderT ask
runReaderT ask :: Monad m => a -> m a
> runReaderT ask "Hello" :: Maybe String
Just "Hello"
> runReaderT ask "Hello" :: Either String String
Right "Hello"
Комментарии:
1. Я также не понимаю
ask :: MonadReader r m => m r
подпись типа. Почему подпись типа включает=>
? Что это значит?2. Это означает, что
ask
это значение типаm r
для любой монадыm
и типаr
, для которого существуетMonadReader
экземпляр.3. Означает ли это, экземпляр monad типа
r
?4. Нет,
r
это тип, «обернутый» монадойm
. По большей части экземплярыMonadReader
являются (грубо говоря) либо функциямиReader
(сами по себе просто оболочкой вокруг функции), либо монадами, созданными с использованиемReaderT
. Это означает, чтоask
почти всегда есть значение, которое является функцией или из которого может быть извлечено.