Comment by itishappy

Comment by itishappy 10 hours ago

1 reply

A reader is just an interface that allows you to build up a computation that will eventually take an environment as a parameter and return a value.

Here's the magic:

    newtype Reader env a = Reader { runReader :: env -> a }
    
    ask = Reader $ \x -> x
    
    instance Functor (Reader env) where
      fmap f (Reader g) = Reader $ \x -> f (g x)
    
    instance Applicative (Reader env) where
      pure x = Reader (\_ -> x)
      ff <*> fx = Reader $ \x -> (runReader ff x) (runReader fx x)
    
    instance Monad (Reader env) where
      (Reader f) >>= g = Reader $ \x -> runReader (g (f x)) x
That Monad instance might be the scariest bit if you're unfamiliar with Haskell. The (>>=) function takes a Monad (here a Reader) and a continuation to call on it's contents. It then threads the environment through both.

Might be used like this:

    calc :: Reader String Int
    calc = do
      input <- ask
      pure $ length input
    
    test :: Int
    test = runReader calc "Test"
    -- returns: 4
Not sure how this compares to Zig!

https://stackoverflow.com/questions/14178889/what-is-the-pur...

Edit: Added Applicative instance so code runs on modern Haskell. Please critique! Also added example.

itishappy 7 hours ago

Here's a minimal python translation of the important bits:

    class Reader:
        def __init__(self, func):
            self.run = func
        def pure(x):
            return Reader(lambda _: x)
        def bind(self, f):
            return Reader(lambda env: f(self.run(env)).run(env))

    ask = Reader(lambda env: env)

    def calc():
        return ask.bind(lambda input_str:
            Reader.pure(len(input_str)))

    test = calc().run("test")
    print(test)
Admittedly this is a bit unwieldy in Python. Haskell's `do` notation desugars to repeated binds (and therefore requires something to be a Monad), and does a lot of handiwork.

    -- this:
    calc :: Reader String Int
    calc = do
      input <- ask
      pure $ length input

    -- translates to:
    calc' :: Reader String Int
    calc' = ask >>= (\input -> pure $ length input)