IO is Your Command Pattern

August 2, 2015 by Alexey Shmalko
Tags: haskell

In this post, I’ll try to give some intuition behind IO in Haskell for people who know what command pattern is, without explaining what M-word or do-notation is.

First, let’s revisit command pattern. According to wiki:

In object-oriented programming, the command pattern is a behavioral design pattern in which object is used to encapsulate all information needed to perform an action or trigger an event at a later time. This information includes the method name, the object that owns the method and values for the method parameters.

Let’s implement the pattern in Java:

Command is an interface for implementing different commands. It has method execute that triggers command execution. Pretty simple.

But let’s extended the interface and allow return values.

This allows to define commands that return values, such as GetLine (remember getLine :: IO String in Haskell).

Now you should see how IO String in Haskell is similar to Command<String>.

Let’s implement PutStrLn now. The Haskell analog is putStrLn :: String -> IO (), that is a function that takes a String and returns a command that prints that string to the console. The proper Java interface for such a thing is Function<String, Command<Void>> (from java.util.function).

We can use it like this:

(you see how execution stage is separated from command creation?)

Now the fun begins. Let’s add a couple of methods for combining our Commands:

pure is a simple method that creates new Command which, when executed, returns given value. This method is the same as Haskell’s return :: a -> IO a function.

then takes additional command and returns a new command which, when executed, executes this command and then executes the second command and return its result. This method is Haskell’s (>>) :: IO a -> IO b -> IO b.

bind takes a function from T to a new command and returns a command which, when executed, executes the this command and passes its result to the function and executes the final command. This function is Haskell’s (>>=) :: IO a -> (a -> IO b) -> IO b.

Let’s write a “real-world” application with that.

ask is a helper function (command) that asks the user for some info and returns user’s answer.

The same program in Haskell:

But Haskell can do better!

Haskell has a special do-notation. In fact, it’s just syntactic sugar for >>, >>= and lambdas. So the last program is the same as previous one.

Now you should notice that the whole Haskell program is one big command and there is no execute in Haskell—all programs should be composed of primitive commands provided by the base library.

That’s how Haskell keeps things pure. Think! Your whole program is just a command—a recipe that describes what program should do. putStrLn is a pure(!) function that returns command (which in turn knows what to print on screen). getLine is a constant(!) value—a command; you’re not performing I/O when you type name <- getLine.

Conclusion

P.S. Note that >>, >>=, return and do-notation are not unique to IO and I lied about their types. They all work for any Monad, not just IO.