7 ms·
The Day Python Embarassed Imperative Programming
- p4bl0 15y agoWould 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 15y agoWell, 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 15y agoI'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 http://jastadd.org/web/jastaddj/extensions.php
- yew 15y agoWould you mind expounding on that use of decorators? I'm not making the connection and it sounds like an interesting one.
- hythloday 15y agoSure, 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-decorator/ http://code.activestate.com/recipes/454322-type-checking-dec...
- yew 15y agoThanks!
- gosub 15y ago"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 15y agoA 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 15y agoFollowed by a misleading explanation that suggests turning ALL your gotos into ifs.
- superxor 15y agoAny other working link? App engine says the server is over quota.
- worldimperator 15y agoCached: http://bit.ly/yCRuLo http://bit.ly/yCRuLo
- vladsanchez 15y agoOver Quota This application is temporarily over its serving quota. Please try again later.
- tcard 15y agoI 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.
- lubutu 15y agoIt 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?
- willvarfar 15y ago(not downvoter) I come to completely the opposite conclusion: http://williamedwardscoder.tumblr.com/post/18319031919/programming-language-readability 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 ;)
- whateverer 15y agoMonads 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.
- ehutch79 15y agoI 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 15y agoYou 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 15y agowouldn't it be better to a) code f defensively in the first place, and b) not call f with invalid parameters?
- gaius 15y agoIt's a workaround for Python not being able to type-check, compared to Haskell.
- tmhedberg 15y ago> 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.
- tmhedberg 15y agoPerhaps 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 15y agom 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?
- tmhedberg 15y agoSorry 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.
- tripzilch 15y agoI 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.
- orblivion 15y agoI'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.
- NathanRice 15y ago"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.
- jerf 15y agoYes, it is by far the best way to learn what monadic really means. http://learnyouahaskell.com/chapters 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-and-monoids 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.
- kingkilr 15y agoIn 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."
- indiecore 15y agoEverything 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.
- klodolph 15y agoWhy 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.
- danieldk 15y agoExcept 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.
- whateverer 15y agoPoorly 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 15y agoNull 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.
- OkkeFF 15y agoCan someone create a mirror?
- michaelmior 15y agoGoogle cache 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 http://webcache.googleusercontent.com/search?q=cache:lM3dxT7...
- Kilimanjaro 15y agoIt 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 15y agoHow 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.
- obtu 15y agoPossibly 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.
- varelse 15y agoThere 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.
- funkah 15y agoWell, that was certainly saying something. Only problem is, I have absolutely no idea what.
- slurgfest 15y agoI 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.
- JadeNB 15y agoShouldn'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`.