Banner

Monads and the meaning of imperative languages

Posted by alpheccar - Jan 16 2008 at 20:45 CEST

This post is not about functional programming but about plain old imperative languages (C, Java, Python ... replace the dots with your favorite imperative language). I will try to show that you are forced to invent monads if you want to define the meaning of imperative languages ! I have not yet seen this approach to monads in the blogs and tutorials available on the web so I thought it could be a good idea to say a few words about it.

What is the meaning of a language

When you define a new language, you can specify the meaning of each language construct using English, French or any other natural languages. It is useful but natural languages are too fuzzy. It is difficult to avoid slight variations of the interpretation according to the reader. It could translate into small differences between the compilers for the language. And, as a consequence, the same source code would behave in slightly different ways when compiled with different compilers.

Worse : with a specification using natural languages, it is difficult to see the impact of the introduction of a new feature in the language. It may break some properties considered as obvious without people even noticing it ... until it is too late.

It is a good thing to try to complete (and not replace) natural language specifications using more formal approaches : a mathematical specification of your language constructs. The mathematical specification should describe the meaning of each language construct as a mathematical object.

For instance, the meaning of the symbol 10 in your source code should obviously be the number 10.

The meaning of a function:

Haskell Code by HsColour
int f(int)

should be a mathematical function from integers to integers :

Haskell Code by HsColour
f :: Z -> Z

Of course, I assume int can code integers of any size (not true with a real language but it is a blog so I simplify a lot). I also assume there is no global state. It is just to give an example.

To specify the meaning of language constructs, you first need to define domains where the values (int, string, functions) live and then you need to define a semantic function to move from the source code world to the mathematical world. I'll note this semantic function:

Haskell Code by HsColour
[[f]] 

which is the meaning of f and should be a mathematical object. f is just a piece of source code.

If there was a state then my semantic function would also depend on a global state S and could be noted [[ f ]] S

Now we are ready ! Let's try to specify the meaning of exceptions for a very simple language with integers, booleans but no variables (just to simplify since it would not change things in a fundamental way)

Meaning of a simple imperative language

Let's begin without exceptions. We have the domain of integers, the domain of booleans (just a set with two elements), and functions from and to these domains.

The function f defined in the source code with

Haskell Code by HsColour
int f(int)
 ...

has, as a meaning, the object

Haskell Code by HsColour
[[f]] :: Z -> Z

which is a mathematical function from integers to integers.

Of course, domain is a mathematical concept. I should define it accurately. Not any function from integer to integer can belong to our domain. Obviously, uncomputable functions are not in this domain.

A domain can be complex. For instance, in which domain is the "while/do" construct living ? Said differently : what is the mathematical object representing the meaning of "while/do" ?

But, let's avoid technical details.

A program is useless if we cannot do several operations. So, at least, we need to define the meaning of g o f where o is the function composition.

It should be obvious that it is:

Haskell Code by HsColour
[[g o f]] =  [[g]] o [[f]]

On the right I am in the math world. o is my function composition operator. [[ f ]] is a mathematical object : the meaning of f which, in this case, is a mathematical function.

Now, let's try to add exceptions to the language. I'll just add an abort statement that is stopping the execution of the program. How do I specify the meaning of a function in this new context ?

The meaning of f cannot be [[ f ]]] anymore because f could raise an exception. So, I'll have to track it. I need to encode the fact that f may return a value or return an exception. Let's assume f is a function from integers to integers. Its old meaning was:

Haskell Code by HsColour
[[f]] :: Z -> Z

Now, it has to be:

Haskell Code by HsColour
[[f]]' :: Z -> Abort or Z

Abort or Z is the disjoint union of a singleton set (contening the label abort) and of the integers. It can be written as 1 + Z where 1 is a singleton set:

Haskell Code by HsColour
[[f]]' :: Z -> 1 + Z

To encode the fact that a function may raise an exception, I have to modify the semantic function and lift the target of the mathematical function [[ f ]] into a new universe which has been extended with the label "abort" : the singleton set named 1.

But, as a consequence, I cannot anymore write:

Haskell Code by HsColour
[[g o f]]' =  [[g]]' o [[f]]'

The composition has to take into account the fact that the result of [[ f ]]' is not in the domain assumed for the argument of [[ g ]]'. So, I need to create a new composition operator on the right.

Assume we have two projection functions for 1 + Z.

Haskell Code by HsColour
left :: 1 + Z -> 1
right :: 1 + Z -> Z

If [[ f ]]' is returning an abort, the meaning of [[ g o f ]]' is

Haskell Code by HsColour
[[g o f]]' = left o [[f]]' = abort

otherwise it is:

Haskell Code by HsColour
[[g o f]]' = [[g]]' o right o [[f]]' 

What we have created is a monad ! We have replaced each function :

Haskell Code by HsColour
[[f]] :: a -> b

with a function

Haskell Code by HsColour
[[f]]' :: a -> m b

(In our example, m b is 1 + b)

And we have replaced the function composition :

Haskell Code by HsColour
[[g o f]] = [[g]] o [[f]]

by something more complex that can combine a function :

Haskell Code by HsColour
f :: a -> m b

with a function

Haskell Code by HsColour
g :: b -> m c

If you now try to extend your language with the possibility to have variables (a state), outputs and inputs, then you'll see that in each case you'll need to add information to your domains (to encode the new features of your language) and change the composition operator as a consequence. You are in fact layering monads.

Conclusion

To describe the meaning of constructs in an imperative language, you need to invent monads and stack them. So monads are always here even if you don't see them. They are here because they define how the constructs of your imperative language are interacting through composition and sequencing. They are not the most complex part of the specification. Defining the right domains in which the objects are living is probably the most difficult.

Tags | | |

Comments

Add a comment...

Oops

Posted by alpheccar - Jan 17 2008 at19:39 CEST

Oops : wrong paper by Moggi. The right one is A modular approach to denotational semantics

I think ... not sure ... :-)

Posted by alpheccar - Jan 17 2008 at19:34 CEST

augustss : I know the paper by Moggi. I read it when I was trying to understand monads. But, I have not seen any blog or tutorial about monads talk about it. And, if it is in a research paper then most programmers won't know it.

sigfpe : You should blog about the use of "theorem for free" for dynamically typed languages and containers.

sam: Thanks. Blogs helped me a lot too when I was trying to understand Haskell.

Posted by Sam - Jan 17 2008 at03:44 CEST

First.

This is also my first comment on this blog though I have been reading it for a few months. Your post is down to earth and totally awesome. Unfortunately I stumbled on monads for quite a while. It's nice to read stuff like this to confirm that I am, in fact, on the right track in my understanding. Another problem with monads is that they are so simple. When I finally got them it was really anti-climactic and I was certain there had to be something more. Keep writing. Your blog is a close second to haskell.org in my book. Please don't be offended by that statement.

Good point

Posted by sigfpe - Jan 17 2008 at00:13 CEST

This is a good point. One of the reasons I decided to learn Haskell was that concepts from Haskell are widely applicable elsewhere. The same goes for type theory. People imagine it only applies to statically typed languages, but interesting structures and algorithms developed using the theory of types can often be transferred directly to dynamically typed languages. For example, the "theorems for free", even if they aren't theorems for dynamically typed languages, still give a good set of regression tests for containers in dynamically typed languages.

Posted by augustss - Jan 16 2008 at23:15 CEST

Well, it was exactly for doing denotational semantics that Moggi introduced monads to the rest of us.