Hacker News new | past | comments | ask | show | jobs | submit login
Applicative Programming, Disjoint Unions, Semigroups and Non-breaking Errors (googlecode.com)
27 points by pufuwozu on Nov 26, 2010 | hide | past | favorite | 4 comments



Nice quote: "Non-breaking error handling is just an applicative functor on a partially applied disjoint union type constructor with semigroup error elements so what's the big deal?!"



The title is facetiously obfuscated (though accurate): this is about performing a sequence of computations which might throw errors (such as validating input), collecting those errors along the way.

The punchline:

    val f = (Person(_, _, _)).curried
    val age = validAge(args(0))
    val name = validName(args(1))
    val postcode = validPostcode(args(2))

    postcode <<*>> (name <<*>> (age map f)) match {
      case Success(p) => println("We have a person: " + p)
      case Failure(e) => e foreach println
    }
I'm puzzled by the author's choice of argument order, which means the call to the Person constructor at the end has to be reversed (arguments in reverse order with `f` at the end). In Haskell, using the built-in Applicative typeclass and flipping his definitions of <<star>> and vmap:

    instance Functor (Validation e) where
      fmap = flip vmap
    
    instance Semigroup e => Applicative (Validation e) where
      pure = Success
      (<*>) = flip (<<*>>)
you can write the constructor call in the natural order:

    Person <$> age <*> name <*> postcode
which is obviously analogous to what the call would look like without error handling:

    Person age name postcode

IMHO this shows the power of the technique described more clearly: that with a bit of plumbing up front, you can add arbitrarily sophisticated error handling behaviour without much disruption to existing code.

One of my Haskell 'aha' moments was that you could do this with Parsec (a parsing library) to build data structures in a way that mirrors the grammar being parsed:

    data Section = HostSection {hostNames :: [String], hostOptions :: [HostOption]}

    hostSectionP :: CharParser st Section   -- hostSectionP parses Chars and produces a Section
    hostSectionP = HostSection <$> hostHeaderP <*> many1 hostOptionP
      -- many1 is like '+' in regex


From the Haskell code it looks to me like the <<*>> operator is doing a monoid append, no?




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

Search: