• someacnt@sh.itjust.works
    link
    fedilink
    English
    arrow-up
    5
    arrow-down
    1
    ·
    10 hours ago

    Monad is (a classes of type of) a collapsible container. Collapsible, like how you can flatten a nested list, or Option<Option<A>> can be flattened to Option<A>.

    A common pattern with monads is the flatMap function, where you apply function A -> List<B> to (each element of) List<A> to obtain List<B>. This happen to represent erroneous call chaining with Option or Result types.

    • balsoft@lemmy.ml
      link
      fedilink
      arrow-up
      4
      ·
      edit-2
      9 hours ago

      This is actually a good alternative explanation, as long as you clarify it a bit.

      What is meant by “container” type is that it’s possible to

      1. “Put” a single thing into our container (unit/pure)
      2. “Modify” the insides our container (map/fmap), without “taking any values out”

      Take for example a list.

      unit is simple, as I’ve described in my comment:

      def unit(a):
          return a
      

      Map is also fairly straight-forward, just applying a given function to every element of the list:

      def map(fun, lst):
          new_lst = []
          for element in lst:
              new_lst.append(fun(element))
          return new_lst
      

      Then your final operation (“flattening” the container) is traditionally called join. It concatenates a list of lists into a single flat list:

      def join(lst):
          new_lst = []
          for element in lst:
              new_lst.extend(element)
          return new_lst
      

      (you might notice that it is extremely similar to both map and bind)

      This allows us to define bind from my other comment (which you call flatMap) in terms of join and map:

      def bind(lst, fun):
          return join(map(fun, lst))
      

      Or, if you already have a bind (and unit) defined, you can define join and map as follows:

      def join(lst):
          return bind(lst, unit)
      def map(fun, lst):
          return bind(lst, lambda x: unit(fun(x)))
      

      Showing that a type defining unit and bind is equivalent to a type defining unit, join and map - they are both equivalent definitions of a monad, you can derive one from another and vice versa.

      Historically, the unit+bind definition is more popular and aligns with what most functional languages do, so I went with it for my explanation. But yours is probably a bit easier to understand for an FP outsider.