4 ms·
This is exciting. Lenses are really powerful and ought to be used in more languages to accompany immutable data types. It'd be nice to see if this technique can
by tel 12y ago
This is exciting. Lenses are really powerful and ought to be used in more languages to accompany immutable data types. It'd be nice to see if this technique can generalize to traversals and prisms as well. In practice, I use those two in nearly any lensy computation.
Using macros to pre-compile the lenses is clever, but feels like a hack around typed-clojure instead of being aligned to it. All of the information needed to determine a lens' action is available at compile time even prior to expansion. Can a van Laarhoven representation be made in typed-clojure that recovers this information?
> type-safe, efficient, concise, idiomatic and flexible handling of deeply nested data structures, and that's what we got. Prima facie, these were unreasonable requests - it's not as though other languages were lining up to answer them
and since the line was crossed: yes, other languages are exactly lining up to answer all of those. You don't even have to burn-in the choice of lens. It's exciting to see this technology developing all around, but it'd also be nice to see it with a bit more humility.
- Blackthorn 12y ago> Using macros to pre-compile the lenses is clever, but feels like a hack around typed-clojure instead of being aligned to it. I couldn't help but feel this way as I was reading it as well...forget compile time though. I wonder if there's any way/plans to eliminate this kind of type erasure period from typed Clojure eventually?
- tel 12y agoIn Haskell you have appropriate typing for lenses because you build paths in a typed way. For instance, the API could like like this type Lens src tgt compose :: Lens src int -> Lens int tgt -> Lens src tgt and then chains of lenses composed with `compose` would carry the appropriate typing information to not have to fall back on Any. This doesn't work "idiomatically" in Clojure because the *-in commands want to consume a list of keys where (1) each key is untyped and (2) the list cannot be heterogenous like it must be for the typed API. That all said, it'd be easy to implement the typed API in Clojure as the "van Laarhoven" representation I mentioned has `Lens` just being a certain kind of function and `compose` just being regular (reversed) function composition: it's exactly like transducers in that regard (in fact, transducers can even be seen as a specific instance of a particular generalization of a lens, I believe). The challenge is figuring out where the vtable for things like `map` lives. I'm pretty sure this is solvable in Clojure by using protocols (although I'm also pretty sure you can't get `Traverses` that way since you need return type polymorphism to get `pure` to work). Anyway, it'd be neat to experiment. Maybe I'll teach myself some typed-clojure and see if I can't port the van Laarhoven lenses.
- kenko 12y ago> since you need return type polymorphism to get `pure` to work). If you don't mind an extra layer of interpretation (which you might very well mind!) you can get around this by building up basically an AST of your applicative computation---I did this, more or less, in my monads-in-clj library: https://github.com/bwo/monads https://github.com/bwo/monads which has a kind of not-that-useful applicative definition: https://github.com/bwo/monads https://github.com/bwo/monads I don't know too much about the innards of van Laarhoven lenses but I'd guess that a straight port might be inhibited by the fact that Clojure isn't pervasively lazy---so using Const as your functor, for instance, won't let you avoid actually computing the eventual second argument which is just discarded. But I dunno.
- tel 12y agoI was thinking about the applicative AST. I think that's a good solution, and perhaps if you're willing to explicitly compile it then you'll only need to pay once (and maybe even memoize it on later uses?) There shouldn't be a problem with strictness and van Laarhoven lenses. The particular trouble is just writing the lenses in the first place: (def one [inj] (fn [p] (fmap (fn [f] (pair f (snd p))) (inj (fst p))))) where did the `fmap` come from?
- podsnap 12y agoI apologize for my miscommunication and ignorance. First, the bragging was supposed to be on behalf of the Clojure ecosystem rather than myself. Second, while I am aware of the standard lens implementations in Haskell and Scalaz, I didn't see that either dealt nicely with intra-path transformations, and the Scalaz version didn't seem very concise. If there are more examples, or if I'm wrong about these, I would love to learn more. I agree that the interaction with typed Clojure is not ideal, but at least there's an interaction of some sort, albeit possibly a "hack." Googling "van Laarhoven" has led me to a few hours of reading material and to SPJ's talk, so thanks for that...
- tel 12y agoCould you demonstrate a simple example of the intra-path transformation? I am not sure that I see exactly how it should be implemented, but so long as it isn't a lens law violation I think you can do it in Haskell's lens system at least.
- podsnap 12y agoI was thinking of setting ```:c``` in ```{:a {:b "{:c 5}"}}```. More generally, a leaf in the container tree that is not itself a container but can be converted to and from one. I need to play more with http://hackage.haskell.org/package/lens http://hackage.haskell.org/package/lens, but I'd certainly appreciate a shortcut to enlightenment...
- tel 12y agoSo there are a few things going on here 1. Focusing on "the subtree" instead of "the value at the subtree" 2. Working with data types like `{:a {:b {:c 5}}}` is somewhat uncommon in Haskell (too little information gets projected into the types) 3. Doing "along the way parsing" Each of those are fine to do in Haskell and I could work up an example. Perhaps the best domain for this is using the lens-aeson[0] package since your data type is not so different from JSON. Here you can use the _JSON prism to do your "along the way parsing" (the failure mechanism of prisms catches failed parses naturally). You can also focus on whole subtrees naturally (since the JSON Value type is just a sum over subtrees and values all together, so the natural parametricity distinction is lost). So I'd look at [0] for a wealth of examples in that vein. I'd also look into zipper lenses. They were removed in more recent versions (I think anticipating splitting them out into another package) but they're available in the 3.* series [1]. [0] http://hackage.haskell.org/package/lens-aeson http://hackage.haskell.org/package/lens-aeson [1] http://hackage.haskell.org/package/lens-3.10.1/docs/Control-Lens-Zipper.html http://hackage.haskell.org/package/lens-3.10.1/docs/Control-...