3

There is a asks function for reader monad, which defined exactly as reader function, why it exists as a separate function with a definition the same as a reader? why not always use reader?

class Monad m => MonadReader r m | m -> r where

    -- | Retrieves the monad environment.
    ask   :: m r
    ask = reader id

    -- | Executes a computation in a modified environment.
    local :: (r -> r) -- ^ The function to modify the environment.
          -> m a      -- ^ @Reader@ to run in the modified environment.
          -> m a

    -- | Retrieves a function of the current environment.
    reader :: (r -> a) -- ^ The selector function to apply to the environment.
           -> m a
    reader f = do
      r <- ask
      return (f r)

-- | Retrieves a function of the current environment.
asks :: MonadReader r m
    => (r -> a) -- ^ The selector function to apply to the environment.
    -> m a
asks = reader
Evg
  • 2,978
  • 5
  • 43
  • 58
  • 1
    For compatibility with other monad transformers: https://hackage.haskell.org/package/transformers-0.5.6.2/docs/Control-Monad-Trans-RWS-CPS.html#v:asks – Willem Van Onsem Dec 30 '20 at 18:11
  • 2
    And also a matter of self-documentation. `reader` reads as "I've got a function that I want to treat as an instance of some monad". `asks` reads as "I want to get the environment and apply something to it". They have the same effect, but they read differently to humans. – Silvio Mayolo Dec 30 '20 at 18:15
  • 3
    @WillemVanOnsem I'm not sure that really answers the question. The obvious followup to "weird behavior so it's compatible with X" is "why does X have the weird behavior?". In this instance: okay, so `MonadReader` has two identical functions because `RWST` has two identical functions; but why does `RWST` have two identical functions? – Daniel Wagner Dec 30 '20 at 19:05

1 Answers1

3

I found the patches that introduced this redundancy to the transformers package and the mtl package. The patch/commit descriptions are... not super enlightening. However, in both cases, asks predates reader, and in both cases, the same change introduced the state and writer primitives.

So, some speculation:

  1. It was observed that it's handy to have the core semantic thing that the transformer/monad class does as a concept represented in the library.
  2. For predictability, that new primitives were named after the transformer that supplied that primitive and nothing else (StateT -> state; WriterT -> writer; ReaderT -> reader). This parallelism makes it easier for users to remember what the thing they want is called.
  3. Since asks already existed, it was kept around for a modicum of backwards-compatibility.

If we wanted a definitive answer, we might have to ask Ed Kmett or Twan van Laarhoven, the apparent originators of the changes.

Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
  • 1
    Looking at this `transformers` patch, it looks like it generalized an existing `reader` for the `Reader` type only. This `reader` was introduced in an [earlier patch](https://hub.darcs.net/ross/transformers/patch/a4eda76b9280c4ed8bf89fb09b06469d1113e7ff), which was perhaps trying to add back "constructors" that were lost when `Reader` and friends switched from their own `newtype`s to `type Reader = ReaderT Identity` which happened [even earlier](https://hub.darcs.net/ross/transformers/patch/58a3fe7c79e3a9a20f0e1aaeb6e208ac146441f3)? – K. A. Buhr Dec 31 '20 at 17:32