Many thanks for your effort to explain it to me. You made work my brain!

At first I was really stuck, when trying to understand your code. And I actually started to describe my difficulties. And then by writing them down I started to understand bit by bit. So just because most of the text is already written and maybe if somebody is struggling like me in understanding this code, I will write down my journey...

First, for the convenience of potential readers, here your original code:

import Data.Map
import Control.Monad

number = return
add = liftM2 (+)
multiply = liftM2 (*)
variable = findWithDefault 0

environment = fromList [("a",3), ("b",4), ("c",7)]

expressionTree = add (variable "a") (multiply (number 2) (variable "b"))

main = print $ expressionTree environment

First difficulty for me, as a Haskell beginner, are the functions without parameters and without types. So trying to grok the code I wrote them down with everything. I write the function 'number' as

number n = return n

If I check in GHCI with ':type number' I get

number :: Integer -> Map [Char] Integer -> Integer

How the heck can this function become this type signature? But OK, apparently there is this hidden parameter of type 'Map [Char] Integer' that is passed when it is called. So I should be able to write the function number with explicit signature (that Haskell otherwise infers) and the 2nd parameter:

number :: Integer -> Map [Char] Integer -> Integer
number n env = return n

... and guess what? GHCI does not accept it and throws some cryptic error. How does this make sense? Haskell tells me through its inferred type signature that the function number takes 2 arguments (Integer) and (Map [Char] Integer) and returns only one. But when I write the function with a 2nd argument it does NOT accept it?

I was struggling here some time. Until I realized that in this example the 'return' function actually as well needs to get the 2nd argument. So if I rewrite it like this:

number :: Integer -> Map [Char] Integer -> Integer
number n env = return n env

it is accepted by the compiler. It might seem trivial now, but when I read your definition of 'return' being:

return x = \ _ -> x

it does not show any 2nd parameter and additionally confusing in the function body takes from wherever some parameter that it ignores (_). I understand that it should mean that the function 'return' returns another function that takes whatever argument you give it and returns the value that you provided to the 'return' function. But for me as a beginner this:

return x _ = x

is much clearer and has actually the same type signature. So far I would cynically conclude that Monads are about saving keyboard strokes by omitting writing parameters which are actually there. But I guess there is more than that.

But anyway, after I understood where the strange type signature with a 2nd parameter comes from, the rest was easier. Here my beginners' rewrite of the variable function:

variable :: [Char] -> Map [Char] Integer -> Integer
variable vn env = findWithDefault 0 vn env

This is pretty straight forward. Moving on:

*Main> :t expressionTree
expressionTree :: Map [Char] Integer -> Integer

I could understand where 'Map [Char] Integer' comes from when I looked how 'expressionTree' is used, namely by giving it the environment 'expressionTree environment'. So I rewrite the definition of expressionTree as:

expressionTree e = add (variable "a") (multiply (number 2) (variable "b")) e

Which now makes clear that the function 'add' takes 3 parameters and returns an Integer. And here came the next difficulty:

*Main> :type add
add
:: (Map [Char] Integer -> Integer)
-> (Map [Char] Integer -> Integer) -> Map [Char] Integer -> Integer

My first reaction was: What? Why does (variable "a") have a signature of a function, it should just return an Integer? This helped me to understand:

*Main> :type variable
variable :: [Char] -> Map [Char] Integer -> Integer
*Main> :type variable "a"
variable "a" :: Map [Char] Integer -> Integer

With 'variable "a"' a new function is constructed which has already got its 1st parameter and just needs the environment as 2nd environment, which again will be done by the 'liftM2' function inside 'add'. If I recall correctly the construction of such new function is called 'partial function application' in Haskell terminology. But anyway from there on the rest becomes clear. Maybe just for beginners (the '$' sign is confusing) and completeness of this explanation I would rewrite the last line as:

main = print (expressionTree environment)

As I told you, you made my brain do a lot of work (and my fingers too) - thanks! But at least I have understood a bit more about Monads: It is just about avoiding to explicitely type all the parameters. But at the same time this is possible without Monads. I guess I still miss something.

Thanks again for your code. I like Haskell, because the code appears to me beautiful, concise but not ugly and when trying to unwind all the details everything seems to fit, there are no contradictions.