Montag, 2. November 2015

Doing considered harmful

This is a bit of a philosophical excursion about beeing and doing and why doing is inherently dangerous

In our every day life we are accustomed to doing things. The result of doing something is an altered state of the world. This is quite different in mathematics. While the mathematician might be doing something, math itself does not do anything, math just "is".

Why is math so good?

When I was a student I had a chance to talk to a computer-science professor and I asked him the following question: "why is math capable of going from simple things like counting to elaborate things like tensor calculus and remain free of bugs, while in computer science we cannot accomplish such a thing and the more complex a program gets the more bug-ridden it becomes". He praised me for the intelligent question, but he did not have a satisfactory answer.

In hindsight I came to the conclusion, that the ingredients that enables math to climb such high levels ob abstraction are guarantees. In fact that is at the heart of all mathematical reasoning: there are things which are guaranteed to be true and from there you derive new things, which are then equally guaranteed to be true.

No guarantees

When you write an imperative program, the core ingedient is a statement. There may be other constructs like procedures, classes and so forth, but at the end of the day, these just organize statemens and to some extend limit the effect a statement can have.

A statement does something. Is there anything guaranteed about statements as such? There's certainly not much. The only guarantees I can come up with, is that a statement takes time and energy when executed.

So I believe, programming started on the wrong foot when making statements the heart of the matter. You are already on shaky ground to begin with.

Now of course, some statements are so simple that you can actually tell what they do and thus make a guarantee. But this only hold for thise particular statements and not for statements in general.

Compare this to math, where e.g. it is guaranteed, that every Integer has a successor. Imagine how difficult life would be, if this was true only for certain Integers. You'd have to inspect an Integer to determine whether it has a successor or not.

Math in programming

Incidently not all of programming is about statements. Every self-respecting programming language has a notion of expressions. An expression is something like

sqrt (a*a + b*b)

This is not a statement. It does not really do anything unless you are interested in the value it computes to:

c = sqrt (a*a + b*b)

Now when it computes c, you still don't care how it is computed. It may start by computung a*a or with b*b and you couldn't care less.

Doing and being

So in most programming languages there is a world of doing (statements) and a world of being (expressions) and programmers switch between these world effortlessly without even noticing.

But saying that "understanding a program means undestanding what it does" is clearly wrong. First this does not hold for expresions and second there are programming languages where the aspect of doing is completely absent.

Programming without doing

The following programming languages lack the concept of doing:

  1. In Excel or any other spreadsheet, a cell contains either an input value, a constant or a formula. The cells with the formulae show the results of the computation. The author of the spreadsheed does not care how the result is obtained. 
  2. The select statement is SQL produces a result in a way which is opaque to the programmer (at least it should be). The programmer does not know what the underlying engine does
  3. Pure functional programming languages like haskell do not have a concept of doing. In that respect they are conceptually closer to Expressions, Excel and SQL than to java.
Conclusion

In everyday life, doing is  quite respected. It is the only way we can change the state of the world. Usually we do things with some goal in mind. But let me ask you this question: "how well does this work for you?".

In my expericene it doesn't work well at all. The world is full of unintended consequences. Now uninteded consequences is the one thing we don't need in programming.

In the real world, we don't have any choice other than doing things. But, in order to avoid unintend consequences,  we should stay away from doing wherever possible.







Dienstag, 20. Oktober 2015

Intuitive Monads

Here are some difficulties I encountered when trying to learn Monads and how I overcame them.

A Monad is a triplet ...

Ocasionally you hear an explanation which start with "A Monad is a Triplet of ...". This sounded very strange to me. What it actually means is "for a Monad you need three things, namely ..."

How big is a Monad?

Initially I wasn't sure how big Monads are. Are they as big as inheritance? Or as big as static typig? No they are not, they are much smaller. Monads are more in the ballpark of method chaining. In a OO language it is a good idea to let each method return an object, so you can write

anObject.doThis().doThat().andThenSomeMore()

Monads allow you to do similar things. So a Monad is just slightly bigger than the advice to let methods return objects.

Is a monad like a Class or like an Object?

This is like asking: "is method chaining a Class or an Object". So, no, it is just a way of doing things.

What is the fuzz about chaining?

If you come from an imperative (or OO) language, then your primary way of composing things is writing one statement below the other. Of course there is all the OO magic, which may help you to organize things, but even without it, you may be able to envision a program which just consists of many lines of code.

Now this is one way of composing things you cannot use in haskell. In imperative languages the order of lines matters. In haskell you can write lines

y = f x
z = g y

in any order and the result will be the same. So writing lines below each other is simply not an option to achieve composability. Remember: composing things is what programming is all about.

The value z above can also be written as

z = g ( f x)

So the x is passed to f, which returns a new value, which is then passed to g. This kind of composition can be done in Haskell and it is called function chaining.

Okay, now we know what chainig means, but what does this leave to be desired, why do people say that monads are about chaining, where chaining is already there and working beautifully?

The "nullable" example

Lets modify the example above slightly to plain functions:

andThenSomeMore(doThat(doThis(x)))

This is plain old function chaining. Now ask yourself the question, what any of the functions should do when it receives a null value. Exotic cases aside, it will have no other option than returning null itself. You won't be surprized to see code like this

SomeType doThis (SomeOtherType x) {
    if (x==null) {
        return null;
    }
   . . .
}

Similar code is expected to be at the beginning of doThat() and andThenSomeMore(). This is a pity, because all the functions expect an Object of a particular type and not a null value. Why do they all have to check for null, when they don't want a null in the first place? You wouldn't want to check if an int parameter is really an int, would you? Somebody else should take care of that.

So we'd like to give our functions the certainty that they really get what they want as parameters, so they don't have to check for null anymore. But someone else will still have to do this and we have to find a good place where to express a function receiving a null value shall return null.

Certainly we cannot say this once and for all. It turns out that null leads to null is a property of the chain. We'd like to say that in this chain

andThenSomeMore(doThat(doThis(x)))

null shall lead to null. In the java world you may be inclined to use some magic, like an annotation and write

@nullLeadsToNull
andThenSomeMore(doThat(doThis(x)))

and then someone else will catch nulls along the way and the functions can be sure, they never receive a null.

Magic chaining

What we need is a way to chain functions, such that nulls are never passed on to the next function. This would be difficult to achieve in Java, because chaining operates on functions and functions are not first class citizens in java. Still it is doable, but it looks very ugly.

What we want to achive is the following
  • If you have a value and a function you want to pass the value to, and the value is null
  • then don't call the function, but return null right away
  • otherwise faithfully call the function and return its result
So this operates on a value and a function. Let's denote this operation with  

value >>= function

>>= would have to do something like this

v >>= f = if v == null
          then null
          else f v

With this definition

 x >>= doThis

would safeguard doThis from ever receiving a null. The result of this operation will be either
  • null because x was null
  • null even though x was not null, but doThis happened to return null
  • any other value returned by doThis
So it will be a value which may be null or not, just like the original x. We can pass the result of this operation to the next function, using the same safeguard:

(x >>= doThis) >>= doThat

Which is again a "nullable" value, and we can pass it to the next funtion

((x >>= doThis) >>= doThat) >>= andThenSomeMore

It turns out, the parantheses are not needed, and we can as well write

x >>= doThis >>= doThat >>= andThenSomeMore

So now we have a chain of functions, where
  • each function can be certain to never receive a null and hence
  • does not have to check for null
Intercepting

More generally speaking, we are intercepting functions calls. In the example above we intercepted the passing of the parameter into the function, but we may as well do some magic to the result before we pass it on.

In the picture, the pink parts represent the magic we can add by means of a monad. It can be just about anything.

Interestingly what exactly we wrap around the function depends on the type of the Value. In our example above, our Value was a "nullable something" and the wrapping we applied was a "wrapping for nullables".

This does not only work for nullable, but for various other types. Another prominent example concerns Lists and functions which take a List and return a List.

We assume that passing a list into such a function will make the function iterate over all its elements, produce a List for each of them and then combine all the list results into a single list.

The iteration over the elements and the combining of the individual result lists would have to be repeated in every such such function. It is like a little technical detail, which does not have much to do with the function's main purpose.

So again, we can factor these things out into an input interceptor, which takes care of the iterations and an output interceptor which combines the individual lists. This is a "wrapping for Lists" and it is specific to the type "List of something". The functions then would only have to be prepared to take a scalar value and return a List.

When we say "Some Type is a Monad", then we mean, that someone has implemented a >>= function and another function called return (which I didn't mention here) and these three, i.e. the "Some type", the >>= and return operations together make a monad. This is the triplet I mentioned earlier. You may also read "Some Type is a Monad" as "Some Type is interceptable.

Effort

Now after all this writing it may appear like writing a monad gives rise to a huge framwork of interceptors. But this is not the case. The specification of the monad typeclass, i.e. what this interceptor framework is all about, is only a few lines of code.

Likewise, implementing a particular monad only takes a few lines of code (but maybe some heavy thinking).

Instead the author of a monad will have to answer the question: "given a value of some type and a function, what shall happen?". This question is answered by implementing the >>= operator.

The term "interceptor" is not really used by the Monad guys. I made this up, to illustrate things.

Getting technical

So far for the "intuitive" aspects of monads. I've made some simplifications (e.g. I didn't mention return atll) and introduced some sloppiness here and there. Next you'll have to dive into one of the many monad tutorials in order to get a pecise understanding. I hope after this introduction this will be easier for you.