5 ms·
Monads in Python, with nice syntax
- Goosey 16y agoMaybe I am just not understanding the true purpose of Monads. I thought they were a mathematic construct which (for lack of a better word, pardon my ignorance) allows Haskell to 'cheat' some imperativeness into it's otherwise purely functional garden of eden. Given that Python is a an imperative language to begin with, what is this trying to solve? Not trolling here. Just curious.
- zokier 16y agoPython is not purely imperative either, and I'd guess that monads would allow programming Python with more functional style.
- agentultra 16y agoIt's a valid question and I am looking forward to the answer. The author claims we should use monads in Python code. However, he leaves the explanation for another day. Hopefully it will be quite enlightening. I can't really see what Monads offer that Python is already well equipped to handle. We have weak-typing and exceptions. These are tools enough to call a function who may give us a random value of any type and we go off and start using it. If something blows up, we beg for forgiveness and can even try again... or whatever we need to do. Then again, perhaps I'm missing the point as well.
- gxti 16y agoThe post is from 2008, and this seems to be the follow-up: http://www.valuedlessons.com/2008/02/pysec-monadic-combinatoric-parsing-in.html http://www.valuedlessons.com/2008/02/pysec-monadic-combinato...
- jedbrown 16y agoI like parser combinators, this library is currently my favorite. Pysec is referenced at the bottom of the page. http://code.google.com/p/funcparserlib/ http://code.google.com/p/funcparserlib/
- agentultra 16y agoAh thanks. Hadn't finished my morning coffee. ;)
- limmeau 16y agoThink of a monad as a programmable semi-colon (who coined that term again?). Then you can build a monad for error-handling, backtracking, continuations, or whatever control-flow primitive you need. You probably wouldn't use the IO (to sequence IO operations) or State (to maintain state) monads in Python, though.
- jules 16y agoObjects can be used to implement a set data type, but that doesn't mean that objects are useless if your languages has sets built in. Monads can be used to encode state, but that is just one of the uses.
- jmillikin 16y agoMonads are used for abstracting computation composition. In any given Haskell code, I'd say at least 80% of my monadic code is non-imperative. There are imperative monads (ST, IO), but also many many functional ones. I've written an introductory article at < http://ianen.org/articles/monad-is-not-difficult/ http://ianen.org/articles/monad-is-not-difficult/ >. The first example in the article demonstrates what I mean by "computation composition". "mdiv(2.0, 3.0)" returns a Maybe, not a float, and before using it as a float the inner value must be unpacked. That can be done without the monad operators, but is verbose and error-prone: mdiv :: Float -> Float -> Float mdiv a b = if b == 0 then Nothing else Just (a / b) with_maybe :: Maybe Float with_maybe = case mdiv 2.0 2.0 of Nothing -> Nothing Just val1 -> case mdiv 3.0 0.0 of Nothing -> Nothing Just val2 -> case mdiv val1 val2 of Nothing -> Nothing Just val3 -> Just val3 Here's that same example, rewritten to use the monadic operators (with explicit lambdas, as in the article): with_maybe :: Maybe Float with_maybe = mdiv 2.0 2.0 >>= (\val1 -> mdiv 3.0 0.0 >>= (\val2 -> mdiv val1 val2 >>= (\val3 -> Just val3))) And, for completeness, using Haskell's lambda-hiding 'do' notation: with_maybe :: Maybe Float with_maybe = do val1 <- mdiv 2.0 2.0 val2 <- mdiv 3.0 0.0 val3 <- mdiv val1 val2 Just val3
- jerf 16y agoThis is yet another "monad" that can't do the list monad, and is not therefore actually a monad. The function passed to bind may be called zero times, once, or n times. (Or perhaps rather, any number of times, but I think phrasing it this way underscores the place where many putative monads fall down.) I mention this because this is actually critical to understanding the nature of Monads, it's not just an incidental point. One way of understanding Monads is that they turn everything that would be a "statement" in most languages into a "function", and these functions are nested in each other. (Well, arguably this is what it is, but it's not necessarily the best understanding. It is in my opinion the best start, though; at least it leaves you with a true understanding, even if all the clever tricks aren't obvious yet.) Statement one actually calls into statement two calls into statement three, etc. The Maybe monad works by taking these functions (the collection of which constitute the "monadic value"), but wrapping it in a check for Nothing, and if you have a Nothing it simply returns Nothing. This has two effects: It returns Nothing, and because it stopped calling down the function chain, it stops "executing" your monadic value. Otherwise it extracts the value from its wrapper and "keeps going" into (not down, into; each bind or <- is a nested function call) the rest of the monadic value. The List monad works by taking each element for a given line in sequence, then calling the remainder of the monadic value, hence the classic cartesian_closure = do a <- [1, 2, 3] b <- [4, 5, 6] return (a, b) which will return [(1,4),(1,5),(1,6),(2,4),(2,5),(2,6),(3,4),(3,5),(3,6)] See how we start with 1 for "a", then "call" the remainder of the Monadic value, which itself starts with 4 for "b" and proceeds on, and so forth. The bind function for the list monad is responsible for poking through the list like this. By turning "statements" into "functions" which are then something we can manipulate like any other functions, Monads provide the "programmable semicolon" functionality, though this isn't the best way to understand them. (This is why the State monad is pure, not "cheated imperative". It takes the old functional paradigm of passing down a state value as a parameter to a series of recursive functions, and abstracts out that parameter. It's exactly the same as the "old" functional answer, just spelled differently.) (Edit: This still glosses over some things because it's a post, not a place I can spend three hours polishing. I hesitate to add another "monad tutorial" to the intarwebs...)
- jmillikin 16y ago> This is yet another "monad" that can't do the list monad, > and is not therefore actually a monad. The function passed > to bind may be called zero times, once, or n times. (Or > perhaps rather, any number of times, but I think phrasing > it this way underscores the place where many putative > monads fall down.) The author's implementation support lists just fine -- Monad is just a typeclass, which can be emulated using Python's inheritance. Unfortunately dynamic typing makes the code much uglier than in Haskell, but that's to be expected. class List(Monad): def __init__(self, items): self.__items = list(items) def __str__(self): return str(self.__items) def bind(self, func): items = [] for ii in map(func, self.__items): items.extend(ii.__items) return List(items) print List([1, 2, 3, 4]) >> (lambda x: List([x * x]))