Monads? Easy!
A Monad over X simply is a Monoid in the Category of Endofunctors of X.
Monads? Really!
- Monads are a concept in mathematics
- Algebra is an area of mathematics
- Category theory is an abstraction of algebra.
- Monads are defined in category theory
- Remember: in category theory we talk about infinity beyond cardinality of infinite sets…
- The „idea“ has been transplanted into something used in programming languages…
- Just think of stars in terms of astronomy, in terms of decoration and in terms of music or movies
Monad as Design Pattern
- Actually Monads should be viewed as a design pattern
- That describes the level of abstraction
Motivation
- Pure functional programmers are poor guys when it comes to state
- State is evil
- State is not possible or hard
- I/O is state, actually a segment of the outer world is manipulated
- Monads encapsulate that
- Even if you are happy with state (poor IT theology…):
Multilevel nil problem 🙁
Monads in Functional Programming
- Container type
- wrap(foo):
- class method
- can also be called unit
- Btw. arrogant Haskell guys call it return
- pass(&block)
- instance method
- Can also be called bind
- Optionally more operations (mjoin, empty, +,…)
Identity Monad
class Identity def initialize(value) @value = value end end def Identity.wrap(value) new(value) end class Identity def pass yield @value end end
Axioms
- Left-identity for pass:
Calling pass on a newly wrapped value is the same as applying the block to that value - Right-identity for pass:
Calling pass with a block that only calls wrap on its parameter results in the same as its target object - Nesting:
Nesting pass blocks should be equivalent to calling them in sequence.
Does our Monad survive 1st law?
Identity.wrap(foo).pass do |value| f(value) end
Is equivalent to
f(foo)
That is how we defined pass.
Does our Monad survive 2nd law?
bar.pass do |value| Identity.wrap(value) end
Is equivalent to
bar
That is how we defined pass.
Does our Monad survive 3rd law?
bar.pass do |value_a| f(value_a) end.pass do |value_b| g(value_b) end
Is equivalent to
bar.pass do |value_a| f(value_a).pass do |value_b| g(valueb) end end
That is how we defined pass.
Array as Monad
def Array.wrap(value) [ value ] end class Array def mjoin inject([]) { |combined, arr| combined + arr } end def pass(&block) map(&block).mjoin end end
Does it work?
Have a look yourself…
More Monad Operations
Empty:
def Array::empty [] end
+:
Already there
Other Examples
- Option (Some, None)
Links & Sources
- https://de.wikipedia.org/wiki/Monade_%28Informatik%29
- https://en.wikipedia.org/wiki/Monad_%28functional_programming%29
- http://moonbase.rydia.net/mental/writings/programming/monads-in-ruby/00introduction.html
- http://codon.com/refactoring-ruby-with-monads
- http://www.valuedlessons.com/2008/01/monads-in-ruby-with-nice-syntax.html
- http://www.codecommit.com/blog/ruby/monads-are-not-metaphors
This article is based on a speach given in the Ruby on Rails user group Switzerland.