Monads allow you to chain together operations in your program without any unintended consequences. And they are a functional design pattern that are widely used and some languages even force you into using them like Haskell. I hope to build upon the definition of a monad as the post goes on.

An analogy from the wikipedia, states they are something like an assembly line where the conveyor belts transports data between functional units and they are transformed one step at a time. During this process, the conveyor belt does not stop.

I like to think of them as a type with a wrapped value inside it. For example, a list can have data inside of it and that can be thought of as the value or it can be an empty list with no data, but the type is still a list. Some languages, like Java 8, have the built in Optional type. There is data inside the optional like an integer or string or it could be empty with no data inside it. And the type is still an optional. Both Lists and Optionals are Monads.

Monads have a bind function that allows them to continue being transported down the assembly line. The bind function must abide by this definition

ma X (a -> mb) -> mb

Where ma is a wrapped value and a defines the type of the value inside the wrapped object. For example, the list [1, 2, 3] m is a list (the type of the wrapper) and a is an integer (the type of the data inside the wrapped object)

So the bind function takes a wrapped object of type a and a function and converts it to a wrapped object of type b.

I want to slowly start introducing an example of Monads that Emmanuel and I started writing in Ruby. We wrote an IO example, the purpose of this small program is to be able to go through the IO process without having the program crash. We first defined an object that is like a wrapper to our data.

class MIO
  attr_reader :value

  def initialize(value)
    @value = value
  end

  def empty?
    value.nil?
  end

  class << self
    def new_empty
      self.new(nil)
    end
  end
end

This way we wrap our data in the MIO object. For example, monad_io = MIO.new(5) and to declare an empty one monad_io = MIO.new_empty. So if we call monad_io.empty? we get a boolean back as to if there is data in the MIO object or not.

Next, we define functions that perform IO operations. They are defined as lambdas and can be invoked with .call(). For example, get_line.call()

class MIO
  attr_reader :value

  def initialize(value)
    @value = value
  end

  def empty?
    value.nil?
  end

  class << self

    def new_empty
      self.new(nil)
    end

    def get_line
      @@get_line
    end

    def get_contents
      @@get_contents
    end

    def puts_str
      @@puts_str
    end

    @@get_line = lambda {
      user_input = gets.chomp
      MIO.new(user_input) }

    @@get_contents = lambda { |filename|
      begin
        file = File.open(filename) do |f1|
          contents = IO.read(filename)
          contents.nil? ? MIO.new_empty : MIO.new(contents)
        end
      rescue Exception
        MIO.new_empty
      end
    }

    @@puts_str = lambda { |contents|
      puts contents
      MIO.new_empty
    }
  end
end

Since they are lambdas the function signature is not so clear. But they take a value (not the wrapped value) and perform some sort of IO operation on them. They return an empty MIO object if the operation is a failure and a MIO object with a value if the operation was sucessuful. Now all we need is a bind that glues everything together - it takes a wrapped object and a function and returns a new wrapped object.

class MIO
  attr_reader :value

  def initialize(value)
    @value = value
  end

  def empty?
    value.nil?
  end

  def bind(function)
    if self.empty?
      MIO.new_empty
    else
      function.call(self.value)
    end
  end

  class << self

    def get_line
      @@get_line
    end

    def get_contents
      @@get_contents
    end

    def puts_str
      @@puts_str
    end

    def new_empty
      self.new(nil)
    end

    @@get_line = lambda {
      user_input = FakeIO.gets
      MIO.new(user_input) }

    @@get_contents = lambda { |filename|
      begin
        file = File.open(filename) do |f1|
          contents = IO.read(filename)
          contents.nil? ? MIO.new_empty : MIO.new(contents)
        end
      rescue Exception
        MIO.new_empty
      end
    }

    @@puts_str = lambda { |contents|
      puts contents
      MIO.new_empty
    }
  end
end

In this implementation of bind, it operates on an instance of MIO so it only needs the function passed in. Now we can do chain together functions like this

monad_io = MIO.get_line.call() .bind(MIO.get_contents) .bind(MIO.puts_str)

The big advantage is that our whole program will run no matter what the inputs are for each function if they exist or not. For example, if the user does not enter a valid file name in get_line the rest of the functions are still able to operate, the program never crashes. Additionally, this pattern allows for consistency in our program. As you can see each IO function is crafted in a similar manner.