Я новичок в Haskell и изучаю Monad Transformer. Я обнаружил, что lift можно опустить при работе с внутренней монадой в стеке монад. Например:
type Foo = ReaderT String (WriterT String Identity) Int
foo :: Int -> Foo
foo x = do
env <- ask
tell $ env ++ "in foo" -- actually, it should be `lift $ tell $ env ++ "in foo"` intuitively
return x
Я думаю, возможно, это не языковая функция, а специальное расширение ghc (верно?). И мне интересно, как это реализовано.





Подпись tell — Tell :: MonadWriter w m => w -> m () [Хакер].
Таким образом, это реализуется для любого m, являющегося членом MonadWriter, и одним из этих экземпляров является:
instance MonadWriter w m => MonadWriter w (ReaderT r m) where
-- …Если внутренняя монада ReaderT, таким образом, является MonadWriter (и WriterT это предлагает), то ReaderT также является членом MonadWriter. Он реализует, как вы, наверное, сами догадались, скажем как [Haskell-src]:
instance MonadWriter w m => MonadWriter w (ReaderT r m) where writer = lift . writer tell = lift . tell listen = mapReaderT listen pass = mapReaderT pass
поэтому он сам выполняет lift просто потому, что instance Haskell выберет для него. Идея состоит в том, что для каждого трансформера монады человек определяет, где возможен подъем, вы могли бы реализовать это, чтобы было более прозрачно, куда будет «направлен» tell.
Я думаю, что существующий ответ охватывает это неявно, но я думаю, что также стоит уточнить: это не языковая функция и не специальное расширение GHC (если только вы не хотите считать
MultiParamTypeClassesспециальным). Скорее, это библиотечная функцияmtl.