Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Question / Help] Can anybody explain how to use Free monad? #65

Open
hariroshan opened this issue Aug 14, 2020 · 3 comments
Open

[Question / Help] Can anybody explain how to use Free monad? #65

hariroshan opened this issue Aug 14, 2020 · 3 comments

Comments

@hariroshan
Copy link

I've been search for a good tutorial on understanding this Monad but I'm unable to grasp the concept. I understand how IO monads work. Can anybody explain how to use Free monad to perform IO operation or DB writes with Ecto ?
Simply explaining with IO.puts() is enough.
Thanks,
Hari Roshan

@hariroshan
Copy link
Author

@expede can you help?

@expede
Copy link
Member

expede commented Aug 16, 2020

Hey @hariroshan 👋

I've been search for a good tutorial on understanding this Monad but I'm unable to grasp the concept.

http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html

Can anybody explain how to use Free monad to perform IO operation or DB writes with Ecto ?

I don’t know if I’d start with the free monad when trying to understand the concept of monads broadly. IO is also a bit of a weird case that will not build your intuition.

Much ink has been spilled on the topic of “what is a monad / monad tutorial”, but they generally don’t work because there’s an abstraction that’s not obvious until you work with them a bit. The good news is that you can build this intuition pretty quickly, but you need to write some code.

I’d work though writing instances for Identity, List, Either, and Writer from scratch. This means implementing the following type classes (fancy super protocols provided by Witchcraft) for each of them:

  • Functor
  • Apply
  • Applicative
  • Chain
  • Monad

The TL;DR is that in the same way that map lets you apply a function to every element in some container without changing its structure (that’s Functor.map, not Enum.map), “monad” is an abstraction over things that have some pure “effect” automatically book-kept by the instance. If you’re familiar with JS, Promises are the kind of “effect” that I’m talking about. It turns out that when you look at the structure of lots of computations, there’s similarities, even though the surface details are different (which is just “abstraction”). This means that you can then write code once, and then run it in any monad and have it take care of different things for you. For example:

  • Writer — log messages to a store that is invisibly kept next to the data it references. You can send the data and log across the wire, persist them to disk and resume later, just debug the path that got you to some state, and so on.
  • Either — totally pure version of with (automated error handling)
  • State — a totally pure equivalent to an Agent (and because it’s restricted as pure you can have an impure implementation that’s say, based on Agent or even RAFT without needing to change your code just, the specialized monad that it’s running in)
  • List — the effect of running the same functions (plural!) over multiple inputs (also plural). Sometimes call the indeterminacy monad
  • Many, many more

Not sure if this video is helpful for your case, but can’t hurt: https://www.youtube.com/watch?v=psdG5iV57q0

Free Monad

If you’ve ever written an interpreter or a toy languages, the free monad lets you do that in a very clean way. Because DSLs can express literally any domain, it’s a super powerful tool! You also get the added benefit of being able to debug the AST before (or during) running it, which makes debugging incredibly simple. It’s also restricted to your DSL, so it can’t do anything unexpected like sending an unexpected email when your program is for doing DB actions.

The free monad takes the “essence” of a monad, and encodes that. You can then embed a functor that describes a DSL that you’re trying to express. This builds up an AST for your DSL (the same general idea as the Elixir AST, but specific to your domain). You then pass this structure off to an interpreter, and as you tear it down, re-run branches (for loops), and adjust the tree of the runtime, you perform effects in the context of the interpreter (e.g. just regular Elixir). The interpreter is a very simple function that has a case for each command in your DSL, and does something with it. This gives you a very very clean, decomposed structure (though at the cost of performance unless you’re in Haskell GHC >= 8.10 with some features enabled).

There’s a lot of excitement around free monads right now, because they’re so flexible, are equally expressive as any other monad, are super clean / maintainable. But in Elixir they do with runtime cost (that interpreting+data structure). You can make something similar in a macro (free applicative) and push the performance cost to compile time, but you loose the ability to react/change based on the result of a step — a list instead of a tree.


I hope that’s helpful! In my experience teaching a bunch of people these concepts, there’s really no substitute for just rolling up your sleeves and trying out. There’s really nothing comparable that you can build a solid intuition around other than perhaps saying “it’s like JS promises applied to different domains.” It’s a new, different, super powerful way of thinking, in the same way that anonymous functions, pure functions, and actors are super powerful tools. And just like actors, once you understand a monad, they seem obvious — but getting over that first hill is the hardest bit.

@hariroshan
Copy link
Author

hariroshan commented Aug 16, 2020

Thank you for your descriptive reply. @expede 👏 🎉 I will practice writing some of the type classes and look through the documentation and code implementation of Algae. I have understanding of map/2, apply/2 and bind/2 or chain/2. I know how to Either and Maybe well enough to do Railway Oriented Programming.
I learned most of the concepts from Professor frisby's mostly adequate guide to functional programming book. In this he implemented IO monad approximately as
IO.of( ( unit -> any ) ) :: {:io, ( unit -> any )}
map/2 and bind/2 are implemented as function compositions. Finally, he uses a run/1 function to execute the IOs. Thus making the caller to decide whether to execute side effects or not. Is this how we make impure functions to pure functions by making them return an option IO ?
For example
add(integer, integer) :: {integer, Maybe.t(IO.t())}
Also I tried executing similar things with Free monad in IEx. It does take a function and composes them while mapping.
For example I have,


  @spec initialize ::
          Free.Roll.t(
            ({ipAddress, port_number()} ->
               Either.t(
                 :gen_tcp.reason() | String.t(),
                 port
               )
            )
          )
  def initialize do
    Free.free(fn {ip, port_number} ->
      @infrastructure.testConnection(ip, port_number)
      |> bind(fn connected_port ->
        Port.close(connected_port)
        @infrastructure.start_connection(getPortJSPath())
      end)
      |> map(fn port ->
        port
        |> @infrastructure.sendValue(0)
        |> @infrastructure.sendValue(
          {:init, %{"ip" => ip |> to_charlist, "port" => port_number}}
        )
        port
      end)
    end)
  end

Is this how we use Free monad? Or should we implement our own using Typeclasses?
I hope I'm not bothering
Thanks,
Hari Roshan 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants