Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

Re^4: Is it worth using Monads in Perl ? and what the Monads are ?

by gaal (Parson)
on Jun 14, 2007 at 06:24 UTC ( #621167=note: print w/ replies, xml ) Need Help??


in reply to Re^3: Is it worth using Monads in Perl ? and what the Monads are ?
in thread Is it worth using Monads in Perl ? and what the Monads are ?

    They are, emotionally, just a way of concealing the fact that Haskell programs contain code that has state

Rephrasing to mitigate the emotional stress: monads encapsulate effects. They aren't there to conceal in the sense of deception, but rather in the sense of information hiding.

I agree with the other poster who said the IO monad's the poorest one to look at, because I agree with you when you said a Perl program is in a(n IO) monad. But that's trivially true; the value comes when you look at the richness of different monads, and when you can take your code and separate functions out of it into those that really are pure and those that can be made to fit in monads a, b, c.

Here's a simple example.

ruleSubName :: RuleParser String ruleSubName = verbatimRule "subroutine name" $ do twigil <- option "" (string "*") name <- ruleOperatorName <|> ruleQualifiedIdentifier return $ ('&':twigil) ++ name

This is part of the Pugs parser, the code to parse out the name of a sub. Inside the RuleParser monad, sequencing means "demand the following parse to work". But it also means take care of bookkeeping (source code position, name of what we're trying to do for use in error messages in case we fail). If a parse fails, the associated further bookkeeping is automatic. Here we say "first of all, look for a '*' string, but it's okay if we don't find it". The function string is monadic; if it failed then the function 'option ""' works much like a try/catch and provides the fallback. Anyway, now twigil is either "" or "*", and the position has advanced by 0 or 1 columns.

Now we try to parse a "name". We try a first parser, ruleOperatorName, and if it fails, ruleQualifiedIdentifier. If that one fails too, ruleSubName will fail. The actual behavior of that failure depends on who called us; if they were ready for us to fail (for example, by modifying us with "option" or "<|>") then a different parse might result higher up. Rewinding the position happens automatically, for example if we had already consumed a "*". But if not -- if we're in part of the Perl 6 grammar where the only conceivable term is a SubName and one could not be obtained -- then the user will get an error message saying Pugs was looking for a "subroutine name".

What I'm hoping I've shown is that all these bits of pieces of combining small monadic functions are common to the domain of parsing. Haskell isn't being deceptive in not making me put try/catch blocks over every line. It's encapsulating very specific and repetitive functionality.

I think one great difficulty in understanding monads is they take "if you look at it that way" to the extreme; and Haskell programmers often look at problems in surprising ways. Since parsing is a very practical problem, and familiar to Perl programmers at that, it's a good monad to learn with.


Comment on Re^4: Is it worth using Monads in Perl ? and what the Monads are ?
Select or Download Code
Re^5: Is it worth using Monads in Perl ? and what the Monads are ?
by BrowserUk (Pope) on Jun 14, 2007 at 13:18 UTC

    Thanks Gaal++ Thanks for not saying: Oh, you just don't understand monads. And thankyou for giving a non-standard library example that made me really think.

    .... Inside the RuleParser monad, sequencing means "demand the following parse to work".

    .... But it also means take care of bookkeeping (source code position, name of what we're trying to do for use in error messages in case we fail).

    .... if they were ready for us to fail...But if not...the user will get an error message.

    .... It's encapsulating very specific and repetitive functionality.

    Okay. From your description (I glanced at the link but turned away because it would take me a long time to wrap my mind around the complexity in there), this is pretty much the sort of thing you are talking about--simplistically implemented in Perl:

    So, in this context, a monad is being used as a container to prevent the need to perpetually carry a state variable across sequenced functions calls (per the fileposition and world variables that you see passed in and out IO code in clean programs); and also--as the monad returned from a function is a different one to the one going in--if the function throws an exception, the return never happens, no new one is created and the pattern matching can (optionally), go ahead 're-using' the original monad, effectively rewinding any partial state changes that may have occurred prior to failure.

    In procedural terms, the retention of state can be object instance vars, or closures or global variables.

    The rewinding of object state after an exception can be achieved through try/catch (laborious), or through taking a (instance local) copy and installing a class-global exception handler (

    local $SIG{__DIE__} = sub{ @{ $self } = @{ $copy } }
    ) to restore the copy. The OO purist would freak, but what's new :)

    Relatively inelegant in Perl 5, but with PRE and POST handlers and similar mechanisms, maybe less so in Perl 6. I've not got that far in my exploration of Perl 6.

    In the context of the OP, this shows Perl doesn't need monads, Haskell does. And, in the lower half of my first post, I was trying to show why the second part of that statement is so.

    The point is that underlying the Haskell/monadic hype (emotive term, but I talking to a Perl audience, not the Haskell community), are familiar and regular mechanisms. There is nothing magic, or deeply mathematical or that requires a doctorate in chaos category theory to understand enough to use these 'monad' thingies. And neither do you need to understand the ins and outs and intricacies of Hindley-Miller type systems to do so either.

    Monads can be likened to objects, and classes of objects, and classes of classes (meta classes), that are familiar enough (even if not totally accurate), that they would be accessible to many more people who are already familiar with these concepts.

    The elegance of monads in Haskell, is in getting the compiler to take care of these mundanities on the programmers behalf. Just as objects in Perl 5 are cumbersome when compared to languages that set out to be object oriented, so attempting to tack on monads to Perl 5 would be equally cumbersome. But more importantly, it would be introducing both terminology and the (difficult mathematical concepts behind it) to a language that has no need for either.

    ...the value comes when you look at the richness of different monads, and when you can take your code and separate functions out of it into those that really are pure and those that can be made to fit in monads a, b, c.

    Haskell isn't being deceptive...

    I don't mean to imply that [the] Haskell [community] is either deceiving other people, or themselves. The deception (a term I wouldn't have used, but you have so lets run with it), is of the HM type system, and the 'pure functional' ethic.

    The problem that functional programming struggled with for a long time is that there are situations in which retained state and side-effects are unavoidable. Pretty much anything to do with IO is a good example. (Because it is necessary for pretty much any useful program, but also familiar to pretty much every programmer.)

    But, the ethics make it emotionally unsatisfactory, and the type system makes it syntactically difficult, to have and use retained state and side-effects without the need to 'invent' a way of getting the retained state past the type system, so as to avoid the need to corrupt the elegance and provability of the type inferencing system.

    Monads, as described by that obscure math, allow that to be done in an elegantly mathematical way. I don't understand the math, but like imaginary numbers (or even the number zero), I can see how they facilitate things that don't work well without them.

    But Perl doesn't have those ethics, nor that type system--so Perl doesn't need the concept, regardless of whether it could be implemented, elegantly or not.

    The source of much confusion for imperative programmers coming to Haskell, and of much of my disquiet about it, is that the Haskell community seems to be defensive about the need for monads. When asked questions like: why does Haskell need monads when other languages don't; they resort to baffling the guys with science about types systems and provability and category theory and so on.

    What is sadly lacking, at least as far as my investigations are concerned, are tutorials that set out to show you how to solve a problem using Haskell (oh, and by the way, that's a monad; and so's that; and that), rather than those that set out to explain what monads are and why they exist.

    If they would come straight out and say, it needs them because its necessary to make retained state and side-effects compatible with the type system and functional purity, and analogise them with familiar concepts, and then say, this how they work, and this is what you can do with them, rather than why they are needed to avoid breaking the rules, then they would become much more accessible and raise much less fuss.

    They would stand on the merits of their own power and elegance, rather than needing the support of math that few understand.

    Whether those things are beneficial in the wider world, and whether a language should or shouldn't have those things, are different arguments that will probably rage on for the rest of my lifetime and possibly well beyond that. I've pretty much nailed my colors to the mast on that one :)


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
        Monads can be likened to objects, and classes of objects, and classes of classes (meta classes), that are familiar enough (even if not totally accurate), that they would be accessible to many more people who are already familiar with these concepts.

      Yes, or like Aspects of AOP: an organizational unit of behavior or functionality. If you approach them from the software engineering perspective and not from the theoretical CS/mathy perspective, one rough way to think of them is as overloading the meaning of the semicolons that separates statements.

      Regarding coming out straight, I'm really not sure what to say. Some people who talk enthusiastically about Haskell come from a different background than advocates of many other languages you're familiar with, and that's part of why they take some things for granted and stress others. My own experience hasn't been easy, but it certianly hasn't been that somebody's evasive or dishonest about the design goals. So if you read Wearing the Hairshirt or Tackling the Awkward Squad, for example, I don't think you get the sense that somebody's bluffing (well, I didn't!). Sure, uncontrolled effects are presented as a bad thing, but part of what's good about monads is that they let you tag the bits that are and those that aren't pure, and the typechecker doesn't let you mix them incorrectly.

        ...it certianly hasn't been that somebody's evasive or dishonest about the design goals.

        When I used the words "They are, emotionally, just a way of concealing the fact that Haskell programs contain code that has state."

        I was not saying that there was anything "evasive or dishonest" going on.

        Have read both of those papers by SPJ and I have to say, that he is the exception that proves the rule when it comes to straight talking in the Haskell community. Indeed, I've downloaded the latter many times because when I go looking for it, I can never find it because the name (mark.pdf) doesn't ring any damn bells. Last time, I remembered to change the name to something meaningful :)

        Anyway, back to what I did mean and using the those papers as exhibits in my defense.

        Section 2.1 begins: "We begin with an apparently fundamental conflict. A purely functional program implements a function; it has no side effect. Yet the ultimate purpose of running a program is invariably to cause some side effect: a changed file, some new pixels on the screen, a message sent, or whatever. Indeed it’s a bit cheeky to call input/output “awkward” at all. I/O is the raison d’ˆetre of every program. — a program that had no observable effect whatsoever (no input, no output) would not be very useful."

        Section 2.2 starts:

        "2.2 Monadic I/O

        The big breakthrough in input/output for purely-functional languages came when we learned how to use so-called monads as a general structuring mechanism for functional programs. Here is the key idea:

        A value of type IO a is an “action” that, when performed, may do some input/ output, before delivering a value of type a.

        This is an admirably abstract statement, and I would not be surprised if it means almost nothing to you at the moment.

        And moves on through a lot of syntactic and semantic discussion before reaching:

        Monads were originally invented in a branch of mathematics called category theory, which is increasingly being applied to describe the semantics of programming languages. Eugenio Moggi first identified the usefulness of monads to describe composable “computations” 32. Moggi’s work, while brilliant, is not for the faint hearted. For practical programmers the breakthrough came in Phil Wadler’s paper “Comprehending monads” 47, in which he described the usefulness of monads in a programming context. Wadler wrote several more very readable papers about monads, which I highly recommend 48, 49, 50. He and I built directly on this work to write the first paper about monadic I/O 38.

        In general, a monad is a triple of a type constructor ...

        I wish I could c&p the big lump of algebraic notation that follows, but it suffices.

        He starts out saying "functions have no side-effects" and ends up describing how actually, they can. That's smoke and mirrors!

        Not in any "deceptive", "evasive" or "dishonest" sense. But in the same sense that "tax avoidance" exploits loopholes in the law. It's not breaking the law, but it still results in you paying less tax than you might otherwise have had to pay.

        So "“monad” derived from category theory, one of the most abstract branches of mathematics" is the loophole.

        I have no doubt that if I was able to understand the notation and theory behind the concept, that I to could convince myself that theory holds up. But I don't need to do that, because I am happy to take it as read from those that do understand it.

        But, IO still has side-effects. And functions that do IO still have side-effects. They haven't disappeared, they've just been moved 'outside of the program' into the world, where they do no harm. In particular, they do no harm to the purity of the lazy functional ethics. and especially, to the compiler magic made possible by the HM type system.

        The defensiveness I referred to does not exist because there is anything wrong with the theory underlying monads (I assume :). It arises because actually, there are far more Haskellers that don't fully uunderstand the theory than do, and their defensive posture is to refer questioners to the theory, rather than try and explain it in terms that non-mathematicians can grasp. There is an old saying I cannot remember, that says something to the effect that it takes an expert practitioner--one who really, really understands his subject--to explain complicated things in simple ways.

        It's not uncommon. We've all seen the examples of the resident 'expert' asked for an explaination of something complicated, resort to waffling in techno-babble. Most of us have been there at some point or another. It doesn't mean we do not understand. It just means that we have not reached that level of comfort with the subject to be able to de-cloak it from the technicalities as a real expert--and a very few gifted teachers (of anything)--can do.

        However, there is a common and unpleasent side-effect [sic] of techno-babble. It is often perceived as condescension. And that can be detrimental.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
      then they would become much more accessible and raise much less fuss.
      Would you mind creating a list of the worst Haskell tutorials that you found?

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://621167]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others lurking in the Monastery: (13)
As of 2014-10-01 21:33 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    What is your favourite meta-syntactic variable name?














    Results (38 votes), past polls