Понимание монады читателя

Я читаю Purescript на примере и попал в часть, представляющую монаду Reader. Пример выглядит так:

createUser :: Reader Permissions (Maybe User)
createUser = do
  permissions <- ask
  if hasPermission "admin" permissions
    then map Just newUser
    else pure Nothing

Запутанная часть для меня это ask функция. Подпись:

ask   :: forall r. Reader r r

Похоже, что он создает читателя из воздуха

Когда я читал о State монада, он имел ту же концепцию с его get функция. И текст объяснил:

состояние реализовано как аргумент функции, скрытый конструктором данных монады State, поэтому нет явной ссылки для передачи.

Я предполагаю, что это ключ, и то же самое происходит здесь с Reader, но я не понимаю, как это работает...

Когда приведенный выше пример запускается через runReaderКаким образом предоставленное значение внезапно появляется в результате ask? Документы на Haskell для ask говорят: извлекает среду монады. Но мое замешательство откуда? Как я вижу, значение передается runReader, хранится где-то, а чтобы получить - звоните ask... но это не имеет смысла.

В то время как пример Purescript, я предполагаю, что любой грамотный человек на Haskell также сможет ответить, отсюда и тэг Haskell.

3 ответа

Решение

В настоящее время у меня нет среды PureScript, поэтому я постараюсь ответить с точки зрения Haskell и надеюсь, что это поможет.

Reader - это всего лишь "обертка" вокруг функции, поэтому, когда вы получаете Reader r rВы действительно получаете только читателя из r в r; другими словами, функция r -> r,

Вы можете вызывать функции из воздуха, потому что, если вы платонист, я полагаю, они всегда существуют...

Когда вы используете do нотация, вы "внутри монады", поэтому контекст r неявно. Другими словами, вы вызываете функцию, которая возвращает r значение, и когда вы используете <- стрелка, вы просто получите этот контекст.

Вы можете убедить себя, что это работает, выполнив несколько замен. Сначала посмотрите на подпись createUser, Давайте "развернем" определение Reader:

createUser :: Reader Permissions (Maybe User)
{- definition of Reader -}
createUser :: ReaderT Permissions Identity (Maybe User)

ReaderT Тип имеет только один конструктор данных: ReaderT (r -> m a), что значит createUser это термин, который оценивает значение типа ReaderT (Permissions -> Identity (Maybe User)), Как видите, это просто функция, помеченная ReaderT, Он не должен создавать что-либо из воздуха, но получит значение типа Permissions когда эта функция вызывается.

Теперь давайте посмотрим на линию, с которой у вас проблемы. Вы знаете, что do обозначение просто синтаксический сахар, а выражение:

do permissions <- ask
   if hasPermission "admin" permissions
     then map Just newUser
     else pure Nothing

Desugars для

ask >>= \permissions -> 
  if hasPermission "admin" permissions
  then map Just newUser
  else pure Nothing

Чтобы понять, что это делает, вам придется поискать определение ask, >>= а также pure за ReaderT, Давайте выполним еще один раунд замен:

ask >>= \permissions -> ...
{- definition of ask for ReaderT -}
ReaderT pure >>= \permissions -> ...
{- definition of >>= for ReaderT -}
ReaderT \r ->
  pure r >>= \a -> case (\permissions -> ...) a of ReaderT f -> f r
{- function application -}
ReaderT \r ->
  pure r >>= \a -> 
    case (if hasPermission "admin" a
          then map Just newUser
          else pure Nothing) of ReaderT f -> f r
{- definition of pure for Identity -}
ReaderT \r ->
  Identity r >>= \a -> 
    case (if hasPermission "admin" a
          then map Just newUser
          else pure Nothing) of ReaderT f -> f r
{- definition of >>= for Identity -}
ReaderT \r ->
  (\a -> 
    case (if hasPermission "admin" a
          then map Just newUser
          else pure Nothing) of ReaderT f -> f r) r
{- function application -}
ReaderT \r ->
  case (if hasPermission "admin" r
        then map Just newUser
        else pure Nothing) of ReaderT f -> f r

Как вы видете, createUser это явно просто функция, обернутая ReaderT это пропускает значение ("окружение") через ваши выражения. runReader разворачивает функцию и вызывает ее с указанным аргументом:

runReader :: forall r a. Reader r a -> r -> a
runReader (ReaderT f) r = f r

Частичный тип функции является функтором, т.е. r->aявляется контейнером для любого типа aList aразмера два эквивалентна функции Bool->a). Более того, это еще и монада

      instance Monad ((->) r) where
    f >>= k = \ r -> k (f r) r

Он удовлетворяет MonadReaderкласс типа и называется простой монадой чтения, и ему может быть присвоен синоним типа Reader r.

askвозвращает эту монаду (->) r) rприменяется к тому же типу r, который мы затем можем связать.

Понимание частичного типа функции (->) rлучше.

Другие вопросы по тегам