

The Day Python Embarassed Imperative Programming - nicolast
http://the-27th-comrade.appspot.com/blog/ahJzfnRoZS0yN3RoLWNvbXJhZGVyDAsSBUVudHJ5GOFdDA

======
tmhedberg
Perhaps the author is just glossing over the bigger picture for the sake of
explanation, but it's a bit of a narrow view of monads to say that they are
just "conditional function calls". That's true if the monad in question is
Maybe, Either e, or (if you're squinting at it just right) []. But >>= in the
State s and IO monads, for instance, has little to do with conditionally
calling functions; there, it's more about sequencing.

Beyond obeying some simple laws, there are strikingly few restrictions on the
semantics of >>=. It can mean completely different things to different Monad
instances. To me, that's at the heart of why newbies struggle to understand
monads: >>= operates at a higher level of abstraction than many programmers
are accustomed to. Outside of specific Monad instances, >>='s meaning is less
significant than the structure that it imposes via the type system.

But that flexibility is also what makes Monad such a useful class in Haskell.
Once your brain begins to recognize the `m a -> (a -> m b) -> m b` pattern in
code, you see it everywhere. Monads are just a way of acknowledging that that
particular structure exists in your code and abstracting it away to be
replaced by a single operator. A chain of function calls with possible
failure, like the author points out, is one case in which such a pattern
emerges, but it's far from the only one. I think the article sort of misses
that larger point.

~~~
tripzilch

        m a -> (a -> m b) -> m b
    

Okay, so what does this mean? I thought it was `f x -> x * x` is a function
definition, right? Or am I completely mistaken here? Because in the above
code, what is the function name, the parameter and the return value? I know
"everything is a function" in Haskell but I really can't see through the
multiple arrows here. Any help?

~~~
haberman
This is one of those things that functional-programming people seem to "get"
without ever making the explanation very clear.

First of all, this is a function _declaration_ (ie. prototype), not a
definition. So this is describing a function type, not a function
implementation. But that doesn't help very much because it's still not very
obvious what the function type means.

The shortcut way to understanding Haskell function declarations is this; if
you see:

    
    
      a -> b -> c -> d
    

in your head, think of it as:

    
    
      f(a, b, c) -> d
    

In other words, it is a function that takes three parameters of types a, b,
and c and returns type d. Everything before the final -> is a parameter and
the final type is a return type.

My "shortcut" isn't literally true, obviously. Here is the gory detail.

In Haskell, every function takes at most one parameter. Functions of multiple
parameters do not exist; they are simulated through a technique called
"currying." When you think you're calling a function of more than one
parameter, you're _actually_ calling a series of functions, each of which
takes exactly one parameter. So in Haskell, if you call:

    
    
      f a b c
    

This is actually parsed as

    
    
      ((f a) b) c
    

Or in more C-like notation:

    
    
      f(a)(b)(c)
    

In other words, you call a function with a single parameter "a", which returns
a function that you call with a single parameter "b", which returns a function
that you call with a single parameter "c."

Likewise, the Haskell type declaration:

    
    
      a -> b -> c -> d
    

Is actually right-associative, so it's parsed as:

    
    
      a -> (b -> (c -> d)))
    

Which is why the whole thing works.

So to parse:

    
    
      m a -> (a -> m b) -> m b
    

Think of it as a function that would be called like so:

    
    
      f(m a, g) -> m b
    

Where g is a function that would be called like so:

    
    
      g(a) -> m b
    

The "m a" and "m b" business you can think of as being a lot like M<a> and
M<b> in C++.

~~~
tripzilch
Thank you! It's coming back to me now :) This was taught to me in college, I
know about currying, the right-associativity was the big part I missed!

------
gosub
"The Python programmer said he had downloaded BASIC, and was experimenting
with it. “But what is this GOTO stuff?” Never. Never ever ask that. One
negative effect of asking that question is the horror of finding out that you
use GOTO more than BASIC programmers; but because they are everywhere in your
life, you have developed a blind spot to them. (Hint: do you use if?) In fact,
as I showed him, every control structure in Python is an instance of GOTO."

~~~
lubutu
A nice analogy, but I would argue that as monads are at a higher level of
abstraction than nullables it's more akin to "but what is this _if_ stuff?" It
is the _f(v) if v else None_ which is analogous to GOTO, not the simpler and
more direct _v >>= f_.

~~~
Dylan16807
Followed by a misleading explanation that suggests turning ALL your gotos into
ifs.

------
slurgfest
I don't see anything here showing Python to be embarrassing. Python IS
dynamically typed, which puts the burden on you to do type checking when it is
necessary. But everybody already knew that.

None is just a singleton value. A particular object may be None, or may be 4,
or may be whatever. However, you cannot assign a new value to 4, or None, so
it isn't accurate to say that any object could have a value of None. It isn't
that an object might have something in it, or not. It's that the object might
be None, or something other than None, JUST as it might be 4, or something
other than 4. That is just how it works in Python's type system. There really
is no notion of "has no value". None IS a value. If (for some reason,
hopefully a good one) you mean to exclude it and raise an error, you must deal
with that in exactly the same way as you would exclude and raise an error on
any other particular value you found important.

In Python, it is not true that anything might not have a value. Python itself
doesn't even have predicates for "has something in it" or "doesn't have
something in it." That might be a common idiom using None by convention, but
it's by no means inherent to Python, or necessary. It's really not the same as
NULL.

What's true is that any argument (or namespace binding) could have a value of
None. OR 4, or a certain dict, or whatever. That's just because Python isn't
doing automatic type checking. None does not take any special role which you
do not give to it. Its semantics are up for grabs. NOTHING in Python actually
forces you to use None to denote "no value." And it is BY DESIGN that Python
does not automatically type check everything. YOU must decide whether and how
to type check arguments - not at all, by using your own decorators or asserts
or some library, or by not using Python. Python isn't a bondage and discipline
language. If you don't like that, don't use it, it's just that simple. There's
no need to call it embarrassing, or gloat about how you schooled somebody who
showed interest in your favorite language.

So. If you use the return value of a function without a return statement, you
are asking for a value which may be None, the same as if you had written
'return None'. You bear responsibility for that decision - the same as you
bear responsibility for feeding int(4) or a module object to your functions.
If you don't like that, then don't do it.

Under normal circumstances, without doing anything special, you will get
something like a TypeError if someone (e.g. you) feeds a None to a function
not anticipating it.

You only have to handle that in the case where it is vitally important to have
a different exception or other behavior. You don't need "six hundred and forty
nine thousand two hundred and eighty-eight if statements" unless you are being
needlessly compulsive to begin with, in a vain effort to emulate a bondage-
and-discipline language. Python isn't even supposed to be a bondage-and-
discipline language, so it's no revelation that it isn't good at that. Trying
to use it that way is just doing it wrong.

(n.b. Instead of using imperative if-statements, you might prefer: f(v) if v
else None, for those instances where it's even necessary).

Dear Author:

It's cool that you are proud of yourself, but in this case I think your
advocacy is being hurt by your ego. I have never gotten the impression that
Haskell programmers in general are smug and boastful. If YOU don't want to be
thought of as smug and boastful, then don't publish articles like this where
you are smugly bragging about who you schooled.

------
p4bl0
Would it be possible that there are no blind spot on the Python side and a
"semantic golden hammer" (for the lack of a better term) on the Haskell side?

I'm not being sarcastic or anything, I'm really wondering: are Python
programmers using monads without knowing it? Or is it that monads are a good
tool at giving a semantic (or "mathematical meaning") to what they are coding?

I think the distinction between the two points of view isn't relevant, but my
point is that when you know something and like it, you easily see it
everywhere: if you want it, all your code is just lambda-calculus, all your
control structure are some kinds of monads, all for loops are just tail-
recursive functions, all objects are just closures…

~~~
klodolph
Well, that's the idea of monads -- you can see them everywhere because their
original purpose is to express imperative programming mathematically. It's
like thinking that all numbers can be represented with digits; of course --
digits were invented to represent all numbers.

But the thing that makes Haskell unique is not that you have explicit monads.
It's that you can (1) use monads other than the IO monad (like Maybe, [], STM,
ST), and (2) that you can choose to use no monads at all in a given function.

To put it another way, Haskell is unique because it allows you write functions
without using monads, but in other languages you can't turn them off. Like
nullable types, which can't be turned off in Java (ever debug
NullPointerExceptions?) or Python, but which have to be enabled on a case-by-
case basis in Haskell.

~~~
hythloday
I'm not disputing that Haskell makes it easier, but from a practical
perspective I'd challenge the idea that nullability can't be turned off in
Java with @NotNull annotations (and nullability inference tools like
JastAdd[0]), or with decorators in Python. Do you see an essential difference
in kind, or just a spectrum of ease of reasoning?

[0] <http://jastadd.org/web/jastaddj/extensions.php>

~~~
yew
Would you mind expounding on that use of decorators? I'm not making the
connection and it sounds like an interesting one.

~~~
hythloday
Sure, it's just a specific kind of type-checking decorator in the same vein as
the ones you can see at e.g. [0]. Obviously it can be made a lot more
sophisticated to only pick up certain arguments. You can do a similar thing
with metaclasses to prevent object attributes being set as None, too.

    
    
      def isnotnone(fn):
        def decorator(*args, **kwargs):
          for arg in args:
            if arg is None:
              raise TypeError("{0} does not accept None".format(fn.func_name))
          for arg in kwargs.values():
            if arg is None:
              raise TypeError("{0} does not accept None".format(fn.func_name))
          return fn(*args,**kwargs)
        return decorator
    
      @isnotnone
      def f(a, b=dict):
        return a(), b()
    
      >>> f(None)
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
        File "<stdin>", line 5, in decorator
      TypeError: f does not accept None
      >>> f(b=None)
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
        File "<stdin>", line 8, in decorator
      TypeError: f does not accept None
      >>> f()
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
        File "<stdin>", line 9, in decorator
      TypeError: f() takes at least 1 argument (0 given)
      >>> f(str)
      ('', {})
      >>>
    

[0][http://code.activestate.com/recipes/454322-type-checking-
dec...](http://code.activestate.com/recipes/454322-type-checking-decorator/)

~~~
yew
Thanks!

------
tcard
I find the whole point of the article to be weak, to say the least. OK, I'm
doing monads, or something equivalent to monads, all the time along my
imperative code without even noticing because it's so intuitive concept. So
when this common abstraction of "conditional function calls" is made explicit
through monad syntax instead of intuitive-implicit, it is supposed to be
easier to deal with?

Perhaps it's cleaner, and you can get accustomed to it, but it surely comes
with an overhead over imperative thinking, at least for a beginner.

~~~
danieldk
It is not only cleaner, it is also safer. In Python you have to add checks
explicitly, or your program may try to execute a method on _None_. When using
the Maybe or Either monad in Haskell, failure is always handled. Say that you
have a sequence of three functions that return a Maybe value:

    
    
      do
        b <- f a
        c <- g b
        h c
    

Now, suppose that in a particular case _f a_ returns _Nothing_ , then the
whole do-expression will evaluate to _Nothing_ and _g Nothing_ is never
evaluated.

Failure monads do not only add cleanliness, but also safety.

~~~
icebraining
One could say the problem is using None for indicating errors. Python does
have exceptions, which also prevent further statements from being executed.

    
    
        try:
          b = f(a)
          c = g(b)
          return h(c)
    

If f(a) raises an exception, g and h don't get executed.

None should be returned when it's a valid value (say, in search() if it
doesn't find anything), and in those cases it makes sense to have explicit
handling.

~~~
danieldk
That's a fair point. On some level a try block resembles a monad that
encapsulates success/failure. However, it is not general, e.g. it does not
provide a solution if you want to chain computations where returning
None/Nothing is valid (e.g. a Map lookup in Haskell).

The nice thing about monads is that it provides an abstraction on sequences of
computations, involving failure, error, state, effects, etc. Though, it can
get ugly at times when you want to use multiple monads simultaneously (via
monad transformers).

~~~
icebraining
_However, it is not general, e.g. it does not provide a solution if you want
to chain computations where returning None/Nothing is valid (e.g. a Map lookup
in Haskell)._

But I think that's a feature, not a bug. Conflating errors and valid values
leads to ambiguity. As PEP 20 says, "Explicit it better than implicit".

If you actually want certain function return values to act as a failure, I
think you should wrap it in a new function that adds those semantics.

------
Kilimanjaro
It is embarrassing for google app engine to allow unusual spikes like that to
bring a site down. What better PR strategy to let the sites up and running to
show that no google property can ever be slashdotted/dugg/hackernewsed?

Google please fix this mess, elegantly.

~~~
Kilimanjaro
How would I fix it you may ask? Well for app engine free riders like me, just
allow one or two excessive spikes per month. That way if you have a blog
nobody reads but is front-paged once in a blue moon, you won't go down in
flames making app engine look bad.

Now those being on the spotlight more than twice please consider upgrading
your account or adding a banner at the bottom of your site "indestructibly
hosted on app engine" to continue free riding.

------
superxor
Any other working link? App engine says the server is over quota.

~~~
worldimperator
Cached: <http://bit.ly/yCRuLo>

------
kingkilr

      In Python, every object is an example of a monad. It has two possible values: None and anything_else.
    

Is no more true then, "It has two possible values: 'Hello, I am a sexy bear'
and anything_else."

~~~
whateverer
Poorly worded, but the point is that every reference in Python, like in Java,
ALGOL 60 and countless other languages, may be of a certain type, or point to
None. In Haskell, save for a caveat[1], this doesn't happen, and you represent
nullability (failure, usually) with types like Maybe or Either, _when you want
to_. Those happen to be monads, too, but you can use them without the monadic
operators.

[1] The caveat is the bottom value, which represents something akin to an
exception which wasn't handled, that your program is now in an undefined state
and now will promptly crash if you use the 'bad' part.

~~~
mikeash
Null is an interesting exception in languages with static typing such as Java.
But there is no static typing in Python. Literally any variable can hold any
value of any type. None is not nearly so special there, since you can also
assign 3 to any variable, or the identity function, or a string containing the
answer to the question of life, the universe, and everything.

In languages like Java, null has special semantics that no other value has. In
Python, there's nothing particularly special about None, it's simply used as a
convenient sentinel by convention. I could easily write Python code that
returns the literal string "This value is empty" as the sentinel instead,
something that doesn't apply to languages like Java.

~~~
whateverer
Yes, actually, you are right. It's all the same in Python because the
references can always point to any value, and None is just a value of the
NoneType.

And due to Python's runtime types, it wouldn't matter either way, because even
if None only existed within the context of a Maybe type, you could still just
apply any operation on it and have crash at the first method call on it.

------
tripzilch
I read the article and I _still_ don't understand what monads are about. This
happens every time. There must be something about the Haskell syntax (which
has been _ages_ since I used it in college--and I utterly failed to grasp
monads then, too) it's _very_ frustrating, I get the rest of functional
programming. Apparently I forgot how to read Haskell.

~~~
ludflu
Its not the syntax, its the concept that's hard. I read about 10 different
monad tutorials, and it only made things worse. The only thing that made it
click was writing code that used them. (Some monadic parser code.)

Seriously, reading monad explanations is like struggling in quicksand of
abstraction. Only by making it concrete in working code did I make any
progress.

If I had to pick the most tractable monad, I would try the Maybe monad, use it
for map lookups in Haskell's Data.Map, since its something you would commonly
do with a Python dictionary anyway.

~~~
recursive
My issue with that approach is that it's hard to know whether you're actually
using monads if you don't really understand what they are.

------
ehutch79
I don't get it. I see this;

def pymon(f, v = None): if v: return f(v)

and I just can't keep reading. Why would you do that? What reason would you
have for not just calling f directly?

~~~
lubutu
You wouldn't want to call _f_ directly if it doesn't accept _None_. pymon is a
function meaning _f(v) if v else None_.

~~~
ehutch79
wouldn't it be better to a) code f defensively in the first place, and b) not
call f with invalid parameters?

~~~
tmhedberg
> wouldn't it be better to a) code f defensively in the first place,

In Haskell, you don't have to. If your function takes a String as an argument,
for instance, the compiler will ensure that it never receives a null instead.
It will always receive an actual String, because normal types are non-nullable
in Haskell. You have to explicitly wrap a type in a Maybe if you want to add
nullability, and `Maybe String` is a completely different type from `String`,
so the type checker can and will enforce that they don't improperly
intermingle.

> and b) not call f with invalid parameters?

Same story here. If f takes a String argument, you can't (deliberately or by
accident) pass it a Maybe String instead, or your code simply won't compile.
The strong static type system in Haskell eliminates the mental burden of
having to always watch out for nulls and handle them as a special case.

That may not seem like much of a burden if you're accustomed to languages like
Python, Java, etc. where nullability is the default. But think about how much
time you've spent dealing with NullPointerExceptions (or the equivalent in
your language of choice) and imagine how much nicer it would be if the
language itself could simply eliminate the possibility of them ever occurring
in the first place. Well, thanks to its type system, Haskell can do that.

~~~
_delirium
I agree, but that particular issue can be fixed with a type system tweak
instead, to treat nullability as a type modifier, vaguely like constness,
which can then be statically enforced. Cyclone adds nullability annotations to
C, for example, and Ada has a "null exclusion" type modifier.

~~~
tmhedberg
The advantage to Haskell's type constructor approach, though, is
extensibility. Making nullability a language keyword like `const` requires a
change to the fundamental grammar of the language. In Haskell, `Maybe a` is
just another data type, defined entirely in Haskell itself. This opens the
door to allowing users of the language to create arbitrary type system
constraints without having to modify the compiler to support them.

------
varelse
There may be a point here somewhere, and monads probably have a bigger use
than this, but what's presented here looked more like my syntactic sugaring
beats yours to me.

------
JadeNB
Shouldn't `hamon2` be

    
    
        hamon2 f (Just x) = f x
        hamon2 f Nothing  = Nothing
    

? The author's definition doesn't seem to typecheck if `f :: a -> Maybe b` (as
in `(>>=)`).

EDIT: Ah, I see; looking at the blackboard picture suggests that the author
has confused `(>>=) :: Monad m => m a -> (a -> m b) -> m b` with `fmap ::
Functor m => (a -> b) -> m a -> m b`.

------
OkkeFF
Can someone create a mirror?

~~~
michaelmior
Google cache
[http://webcache.googleusercontent.com/search?q=cache:lM3dxT7...](http://webcache.googleusercontent.com/search?q=cache:lM3dxT7hvT8J:the-27th-
comrade.appspot.com/blog/ahJzfnRoZS0yN3RoLWNvbXJhZGVyDAsSBUVudHJ5GOFdDA+http://the-27th-
comrade.appspot.com/blog/ahJzfnRoZS0yN3RoLWNvbXJhZGVyDAsSBUVudHJ5GOFdDA&cd=1&hl=en&ct=clnk&gl=ca)

------
funkah
Well, that was certainly saying something. Only problem is, I have absolutely
no idea what.

