3 ms·
Free monads are hard to get your head around IMO. But there is any easier way. In Haskell just use a typeclass! The typeclass defines the “syntax” and your imp
by quickthrower2 5y ago
Free monads are hard to get your head around IMO.
But there is any easier way. In Haskell just use a typeclass! The typeclass defines the “syntax” and your implementation of the class defines the “semantics”
You can also do this in C# or Typescript simply using an interface, or in JS I guess using duck typing.
Basically you can make DSLs using OO concepts!
- mjburgess 5y agoI think Haskell's return-type polymorphism makes this much easier. In other languages, a monadic structure is at least general. A "monad" is just a type with a sequence operation. The heart of it is just that we aim to do "1 then 2 then 3", etc. This only becomes something worth talking about when not using the semi-colon to sequence, as we then need to use function application (ie., passing arguments). The benefit of using a function to sequence operations is that we can just "make it the semicolon", ie., and do nothing extra. *Or* we can add extra functionality, "sequence and ...". In the case of free monads (I just skim read the article), it appears this "extra" is just a "package" operation. So rather than sequence to execute, we (recursively) wrap in a container. This recursive container is then an abstract syntax tree, and we can define an interpreter separately. This I think is very straightforward, and really just confused by terminology. We are just making a AST constructor from a linear sequence of operations.
- quickthrower2 5y agoYeah for some reason this explanation which should be simple just blows my mind. I’m probably too experienced in imperative languages. It took me a long time to grok plain old monads. And when you get those GHC compiler errors from heavily stacked up types I find it very hard to work out what went wrong. For practical purposes I prefer OO even though it might be more limited in some regards.
- mjburgess 5y agop = newPerson(); w = walk(p); t = talk(p, w); s = shout(p, t); becomes, seq(shout, seq(talk, seq(walk, seq(newPerson()))) "infix", newPerson() seq walk seq talk seq shout The operations `newPerson` and `seq` form a monad. If you can omit `w =`, `t=`, and `s=` you have an applicative, rather than a monad. Ie., the operations being sequenced are independent.
- eru 5y ago> A "monad" is just a type with a sequence operation. The heart of it is just that we aim to do "1 then 2 then 3", etc. Isn't that an Applicative Functor? Monads also have join / bind.
- valenterry 5y ago> But there is any easier way. In Haskell just use a typeclass I understand your point. In fact, I mostly do what you say, because it is simple and usually sufficient in most cases. But it is important to understand that there is a difference. A minilanguage or an embedded DSL with free monads allows for custom transformations and it completely separates the interpretation from the description of the domain logic/actions. With the approach(es) that you mention, that is not the case. You both describe and directly execute/interprete the logic in an intermingled way. That can be an advantage because it makes things easier (less abstraction) and more direct, but it can be a disadvantage if you have the need to interpret in different ways and have full separation between these two phases.