News for hackers. Stuff that matters.
Re:State Monad for the Rest of Us by cryptonector on Friday August 09, 2024 @11:23AM (#41202747)
Monads can be used to attach all sorts of things besides state to data. For example, you can associate read-only data, such as configuration, authenticated identies, etc. You can associate I/O methods, which can be real or mocked. You can have a place in which to scribble logs/traces (this feels like state). And yes, you can associate mutable state, of course.

The point of monads, from the point of view of programmer convenience, is that they let you have a bag of contextual stuff along with your values. Thus if you think of a server application that processes requests, you might want to carry all of:

  - configuration applicable to this request
    (or global configuration)
  - request metadata (time received, from where,
    from whom, authenticated how, etc.)
  - request processing state
  - logs/traces
  - I/O methods, so that
     - you can make all your code deterministic
       and thus easier to write tests for
     - so you can mock the world (see previous
       item)
with each request. Thus each function that handles a request can get all of that context, and can be fully deterministic, yet it can function in a non-deterministic world.

Besides this there's everything about the Maybe ("Optional") and Error/Either monads that just makes it easy to make sure all errors and "nulls" are handled.

And what makes all of this possible (in Haskell) is the use of operators (functions) and syntax that lets one write "statements" that are actually combined into large expressions.

Re:State Monad for the Rest of Us by 12_throw_away on Friday August 09, 2024 @12:49PM (#41203470)

  you might want to carry all of:
   - configuration applicable to this request (or global configuration) [...]
Ok, this is super-interesting to me, as I'm currently dealing with the "configuration and interfaces as global state" pattern in a legacy application. Among other things, this makes it hell to test and refactor. Refactoring it into a functional abstraction like this seems like exactly what we need.

But ... I'm kind of struggling to figure out what "global configuration as a monad" would look like (very much not a Haskell programmer). How would something like this work in practice?

Re:State Monad for the Rest of Us by trealira on Friday August 09, 2024 @02:21PM (#41204170)
"Global configuration as a monad" is represented by the Reader monad, which is basically a wrapper over a function that takes a configuration/environment as a parameter and then returns something. It replaces a read-only global configuration.

This is Haskell, but here's a really simple example.

  -- As an ordinary function:
  foo :: Int -> Bool -> Int
  foo n shouldAdd = if shouldAdd then n + 1 else n - 1

  -- Exactly the same, but using the Reader monad
  foo :: Int -> Reader Bool Int
  foo n = do
    shouldAdd <- ask
    return (if shouldAdd then n + 1 else n - 1)
Here's the same thing, but with a record with one boolean in it:

  -- Config: a record with one boolean in it,
  -- and you access it with a function called `shouldAdd`
  data Config = MkConfig { shouldAdd :: Bool }

  foo :: Int -> Config -> Int
  foo n cfg = if shouldAdd cfg then n + 1 else n - 1

  foo :: Int -> Reader Config Int
  foo n = do
    mustAdd <- asks shouldAdd
    return (if mustAdd then n + 1 else n - 1)
It's basically a way to have implicit read-only parameters. You can call a bunch of functions that take a configuration parameter without having to actually pass the configuration as a parameter explicitly. In an object oriented language, you might use classes for this (if not global variables).
Re:State Monad for the Rest of Us by cryptonector on Friday August 09, 2024 @02:04PM (#41204043)
In Java or C++ or other OOPish languages, say, you might make all your classes be Configurable (unless they are Configuration), which means their constructors would take a Configuration in some way, possibly with an explicit Configuration argument or with a Configurable argument whose configuration to copy. This way all your objects will know how to find configuration information.
Re:State Monad for the Rest of Us by 12_throw_away on Friday August 09, 2024 @04:04PM (#41204955)
Sure, that makes sense, and is a go-to pattern for my own projects. But to map this onto FP, making classes Configurable feels like OO version of currying. I.e., I'm taking some parameters and baking them into methods that I will call later. But does this have anything to do with monads or some monadic version of state?
Re:State Monad for the Rest of Us by cryptonector on Friday August 09, 2024 @04:54PM (#41205336)
It's more like `Configured<T>` -- a Functor, something not too dissimilar to `List<T>`, but a) with just one `T` in it, b) with all your configuration things in the `Configured<T>` instance.

So everywhere you deal with a value that is of some type `Configured<T>` you can then refer to all the configuration methods you'd expect of you `Configuration` types. In Java you'd say something like `this.getConfigBlah()`, and it would just work.

> I'm taking some parameters and baking them into methods that I will call later.

Yes.

> But does this have anything to do with monads or some monadic version of state?

In languages that have monads: yes!

Re:State Monad for the Rest of Us by RandomThoughts3 on Friday August 09, 2024 @06:56PM (#41206042)
> In Java you'd say something like `this.getConfigBlah()`

No, the whole point is that it’s completely dissimilar to that.

> But does this have anything to do with monads or some monadic version of state?

There is no monadic version of state. The State monad is a way of chaining things together (not unlike shell pipes) which is nice in Haskell because it gives access to some of the language syntactic sugar (the do notation). There is nothing particularly special there otherwise.

The key takeaway here is that you can avoid littering your global scope by explicitly passing what’s shared as functions arguments and then remove side effects if you also pass by value.

Yes, it is this simple.