Hacker Timesnew | past | comments | ask | show | jobs | submitlogin
The Day Python Embarassed Imperative Programming (the-27th-comrade.appspot.com)
88 points by nicolast on Feb 27, 2012 | hide | past | favorite | 76 comments


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.


    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?


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++.


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!


It's actually more insightful to see the full type signature:

  (>>=) :: Monad m => m a -> (a -> m b) -> m b
So, >>= is a function that takes:

- A value a in a monadic type m

- A function (a -> m b): a function that takes a value a and returns a value b wrapped in the monadic type m.

And it returns:

- A value b wrapped in the monadic type m.

It helps to look at the definition of (>>=) for a particular monad. The article mostly discusses computations that can fail (None vs an object in Python). The corresponding Haskell monads are Maybe and Either. The signature for (>>=) in the Maybe monad is:

    (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
What the grandparent points out is that a monad defines a structure (how to combine expressions that result in wrapped values), but not semantics. The semantics are defined in the definition of a particular monad. E.g. the Maybe monad models failure in computation, while a monad such as MonadRandom does something different altogether (providing random numbers, while threading the state of the random number generator).


Sorry for not being clear. That's just the (abbreviated) type signature of >>=, which is formally written as

    (>>=) :: Monad m => m a -> (a -> m b) -> m b
It's not the definition of >>=, though. The definition is left up to the specific Monad instance that's defining it (which is basically the larger point that I was trying to make).

In English, that type signature basically says that >>= is a function which takes a monad and a function that operates on the monad's "contents" as its arguments, and returns the result of applying the function to the monad's contents. Any data type for which that abstract structure makes sense (and that also complies with the basic "monad laws") can be made an instance of Monad and provide its own definition for how >>= operates. That definition can be wildly different for different data types; the specific definition that the article focuses on is the one for the Maybe type.


> 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?

In this case, it's a type signature for a curried function. So

    a -> m b
is a function taking a value of type `a` and returning a value of type `m b`. In C# you would write:

    Function<A, M<B>>
so `m a -> (a -> m b) -> m b` is a function taking a value of type `m a` and a function with signature `a -> m b` and returning a function of type `m b`. Translating to C# again,

    Function< M<A>, Function<A, M<B>>, M<B> >
> Or am I completely mistaken here?

Pretty much yeah. In haskell, a function is either a top-level binding:

    someFunction arg = doSomething arg
or an anonymous function which is introduced by the character `\` (because `\` looks like `λ`, kind-of):

    \arg -> doSomething arg
In the expression you quoted, there is no function name, these are all types. The function name in this case is `(>>=)`


m a: means a type which take a parameter (another type).

For example,

data Maybe a = Nothing | Just a

    - The m is for "Maybe"
    - The a is for "a"
For example:

    (>>=) (Just 3) (\ x -> Just (show x))
should returns: Just "3".

    (>>=) Nothing (\x -> Just (show x))
better written

    Nothing >>= (\x -> Just (show x))
should returns: Nothing


Not to be nit-picky, but everything is Haskell is not always a function:

http://conal.net/blog/posts/everything-is-a-function-in-hask...


I completely agree. It's hard for beginners to learn that a monad isn't a thing, it's an property that can apply to many different things. A monad is so general it's hard to have a single intuition about it.

I think perhaps another reason why beginners find monads so hard to understand, especially those coming from a math background, is that >>= is a bizarre presentation of monads. I think, for example, defining a monad as an applicative with a join operation would have been a much better idea - most monads, like Maybe or [], make the most sense to me as "containers" that can be joined. The bind operator never really made intuitive sense to me when thinking about a monad as a container with a structure. Plus, to me, m (m a) -> m a is a lot easier to recognize than m a -> (a -> m b) -> m b.

This is especially true since applicatives are now becoming a very popular language idiom. It seems unfair to beginners to make them try to understand applicatives and then make them learn an unrelated typeclass for monads, when mathematically the two are very similar objects.


Actually, I'm pretty sure the reason most people have trouble understanding monads in Haskell is because they haven't learned what typeclasses are yet. Once you know what a typeclass is, the rest is pretty straightforward.

I tried to explain it here ( http://www.reddit.com/r/programming/comments/erzh1/monads_ar... ) a while ago, which might be helpful for some people. Although, the version of List's >>= function is much easier to understand here ( http://en.wikibooks.org/wiki/Haskell/Advanced_monads#The_Lis... ) than the one I pulled out of GHC's library code.


"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."


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.


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


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…


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.


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


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


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...


Thanks!


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.


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.


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.


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).


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.


It does come with an overhead for a beginner, to be sure. But because it is a consistent abstraction we can build idioms on top of the concept of a monad, and in doing so it actually becomes easier to reason about. The best things in life have a learning curve.

Edit: Wow, I was downvoted for this?


(not downvoter)

I come to completely the opposite conclusion:

http://williamedwardscoder.tumblr.com/post/18319031919/progr...

There's nothing stopping someone writing a haskell-alike with readable syntax as long as they stay away from trying to create terms like monads to explain things that don't need explaining ;)


I don't think that's a fair comparison. Here's a toy trie implementation I've just thrown together in Haskell. It may not be perfect, but I think it's more comparable to your Python.

http://lubutu.com/media/temp/trie.hs


> There's nothing stopping someone writing a haskell-alike with readable syntax as long as they stay away from trying to create terms like monads to explain things that don't need explaining ;)

There's nothing stopping someone from doing that while trying to create such terms, either; witness Haskell. (Or maybe your argument is that the syntax isn't readable? Well, is `>>=` really worse than `?:` in terms of immediate apprehensibility?)

However, I think it's important to recognise that the use of 'monad' is not a case of "creat[ing] terms … to explain things that don't need explaining". A pattern was identified, and it was realised that it fit into the existing mathematical formalism of monads (http://en.wikipedia.org/wiki/Monad_%28category_theory%29).

This can hardly be regarded as a willfully abstruse activity; identifying and naming existing patterns, especially if the name is already out there, is part of a (good) computer programmer's toolkit, right? http://en.wikipedia.org/wiki/Design_Patterns


And python should be embarrassed how exactly?


I never said anything about embarrassment; I was just defending the virtue of using existing names to recognise pre-explored patterns, even if they frighten people.


Monads only need a lengthy explanation if you don't understand that they are a typeclass in Haskell. The monad laws you can take to be principles on which to correctly implement them, but they aren't necessary for their use.

Besides, that whole post just gave me the impression that you haven't done the minimum necessary to understand Haskell programs. You claim that there is no immediate visual grouping of the expressions, yet you seem to gloss over all the pattern-based definitions of functions.

Now, if you find using such lengthy sequences of patterns to define your functions unsavory, you can use case expressions, which... require indentation for their cases.

On most respects, it just comes down to the fact that Haskell is different, and it takes a bit of reading to accustom oneself to it.

edit: It was a tad too defensive before. Sorry 'bout that.


It's a stumbling block the beginner must learn once, not really an overhead that comes up at every use.


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.


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.


Possibly the author is doing something inefficient that shouldn't happen in a mostly-static blog? In which case Google could provide a bit of load testing so that people know their site won't survive a social aggregator. Or offer some read-only version, idk.


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



  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."


Why pull a quote out of context to nitpick it? The article goes on to explain how common it is to check if an object is None and act differently, which is what gives us the same monadic structure as the Maybe monad in Haskell.


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.


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.


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.


That's not completely accurate. None is still special in Python because it's the value chosen by the interpreter to represent an expression that doesn't explicitly return a value.

It is true that None is implemented like any other object (whther the value of 5 or an instance of my own class), but it still maintains special semantics.


every reference in Python, like in Java, ALGOL 60 and countless other languages, may be of a certain type, or point to None.

Well, in Python every reference may be of any type. None is just another object, there's nothing particularly special about it.


Except that 'Hello, I am a sexy bear' is generally not used to indicate failure in Python, while None often is.

Edit: strange downvoting here :/. The parent has a point in the sense that None is just another object in Python. However, it's a singleton with a special meaning (by convention). Consequently, the parent used a false analogy.


Everything in life is 50/50, either it happens or it doesn't. The statistics industry is just trying to pull the wool over our eyes.

WAKE UP SHEEPLE.


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.


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.


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.


I'm just getting it now. Google for something called Typeclassopedia and read about Functors, and then Applicative functors. Those are much easier to understand right away, and they set the stage for monads. Applicative functors relate to functors in an obvious way (you could probably implement one in terms of the other). Monads relate to applicative functors in a less obvious way, to me anyway, but at least it's analogous enough that it helped a whole lot (particularly that return and pure were the same thing)

I read about the first two on the typeclassopedia page, and was somewhat unsatisfied with its explanation of monads. I then went to Learn You a Haskell's page on all three of these and skipped to monads, and I was happy with it. I bet you could just read about all three on the Learn You page, the guy does a great job.


"Learn you a Haskell for great good" is probably the most approachable book on the subject you can read it online for free.

People who are confused should definitely check it out.


Yes, it is by far the best way to learn what monadic really means. http://learnyouahaskell.com/chapters is the book, but if you already understand basic Haskell syntax, you can start at Chapter 11, Functors: http://learnyouahaskell.com/functors-applicative-functors-an...

Yes, it's a bit long, but it actually works, and, what's more, unlike the vast majority of monad tutorials written by someone who just sort of half figured out the concept half-an-hour ago (but they really didn't), it is also correct. This is one of the major root causes behind people's general inabilities to "understand monads after reading tons of tutorials", the tutorials are not only generally not very good but often wrong and mutually contradictory.


Oh sorry, yes I forgot that they were in two different sections. Definitely read the one about Functors before you get into Monads.


IMO the best intro to monads is You Could Have Invented Monads:

http://blog.sigfpe.com/2006/08/you-could-have-invented-monad...

It starts from concrete examples, lets you write a few exercises, then it shows you the common pattern behind those examples.


Same here. I must have read every article explaining Monads that I saw, but I still have no idea what they are.


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.


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?


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.


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


> 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.


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.


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.


Python doesn't have nullability, you must always pass some object; None is just an object like any other /pedant.


I'm not asking about haskell though, I was asking what the valid reasons for that python code being written.


I'm pretty sure that the author of the article didn't mean to imply that you should actually define monads explicitly in Python and start using them in your code. That would be inelegant, far from idiomatic, and mostly pointless, since Python doesn't type-check your code like Haskell does, and most of the advantages of an explicit Monad class arise from its usage in the presence of a strong type system.

The example snippet was likely just an illustrative example, using Python merely because it's a more widely understood language than Haskell. If he wrote it in Haskell, then not only would it be redundant (since monads are already defined in that language), but it would be less comprehensible to the intended audience of new and non-Haskell programmers.


Wouldn't coding defensively just end up looking like:

   def f(v):
       if v is None: return
       # REST OF f stuff
And say you have other functions g, h, i, ... each one of those would also start with the:

   if v is None: return
portion of the function. This is just a case of DRY.


No? The point is to factor out the repeated check-for-None code into one place to reduce redundancy and unclutter the algorithmic code in f. Separation of Concerns.


Writing wrapper functions that just calls a function provided as an argument seems insane to me.

If the code really cares what it's getting, it should not only be checking for None, but also that the arguments it is called with are valid.

I still fail to see why you would write a function like this at all.


Moving away from the trivial example used for simple demonstration purposes...

Have you ever written validator code for your inputs from a web form? It really is the same pattern, usually implemented via decorator rather than direct

   result = validate(f, inputs)
calls, but decorators are just syntactic sugar for that.

Or are you really suggesting that every function which may take data from an input source copy and paste the same validation code to the top of that function?


It's a workaround for Python not being able to type-check, compared to Haskell.


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`.


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.


Can someone create a mirror?



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




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: