Hacker News new | past | comments | ask | show | jobs | submit login

Still, you haven't shown why anyone should be impressed by Haskell's lenses. It's like you do not understand that isomorphisms and first-classness is totally irrelevant to me and most programmers unless it leads to better code.

Here is a challenge for you or anyone else who loves lenses:

Take a small snippet of real source code you or anyone else has written and uses lenses and post it here. I'll then translate it into Python that has the equivalent effect. If the translation is impossible or is less pretty than the Haskell original, I'll award you $1000 internet points.




    import Control.Lens
and let us use two components from lens:

    rewriteOf :: Setter' a a -> (a -> Maybe a) -> a -> a
which can take a rewrite rule and apply it to any 'self-similar' setter recursively in a bottom up fashion until it ceases to apply and

    uniplate :: Data s => Traversal' a a
that says that if we have an instance for Haskell's built in generic programming framework 'Data', we can get a traversal of the immediate descendants.

Now

    rewriteOf uniplate $ \case 
      Neg (Lit a) -> Just $ Lit (-1)
      _ -> Nothing
will walk a syntax tree looking for negated literals starting recursively from the bottom of the tree, applying that rewrite rule on the right hand side until it no longer applies and fold it back up the tree. This works in a lazy setting where you can have potentially infinitely many targets and you didn't have to write any code to define the traversal.

The data type itself was just something like:

    data Term 
      = Var String 
      | Neg Term 
      | Lit Int 
      | App Term Term 
      | Abs String Term 
      deriving Data


Let's say you've serialized a tree structure of versioned data as JSON. Branches are arrays and leaves are objects.

    data OTree = Obj Object | Node (Data.Vector.Vector OTree)

    instance FromJSON OTree where
      parseJSON (Array as) = Node <$> traverse parseJSON as
      parseJSON (Object o) = return (Obj o)
      parseJSON _          = fail "OTrees are objects and arrays"

    instance ToJSON OTree where
      toJSON (Node as) = Array (fmap toJSON as)
      toJSON (Obj o)   = Object o
Now some of these objects have keyed "version"s which are arrays of semantic versioning numbers. Write a function which decodes and re-encodes a new tree with each of these semantic versioning numbers incremented at the patch level. If the version isn't in that format, just ignore it.

In Haskell you'd want to write a generic traversal over the objects of the tree useful whenever you want even access to the contained elements.

    eachObj :: Traversal' OTree Object
    eachObj inj (Obj  o ) = Obj <$> inj o
    eachObj inj (Node as) = Node <$> traverse (eachObj inj) as
And then here's the finale, the actual lens code specific to the task.

    upgrade = _JSON . eachObj . ix "version" . _Array . ix 2 . _Number +~ 1


That's a contrived example and not "real source code." Furthermore you are leaving so many symbols undefined that it is hard to see what is going on. Where does 'traverse' come from? Nevertheless, here is how you would do it in python: https://gist.github.com/bjourne/6219037


It's extracted, not contrived. Updating nested attributes on a tree of objects as a nice one-liner. The most contrived bits were that I didn't use the built in tree type so I had to define more stuff explicitly.

Traverse comes from Data.Traverable but is exported with lens as lens can be seen as a generalization of traverse.




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

Search: