5 ms·
Great article. But I'm wondering, how many of you have used Lenses in larger, or legacy projects? It is this sentence that gives me some doubts: > The formulat
by misja111 4y ago
Great article. But I'm wondering, how many of you have used Lenses in larger, or legacy projects?
It is this sentence that gives me some doubts:
> The formulation is wonky, and very difficult to grasp. Don't worry if the intuition hasn't kicked in. It turns out that you can use lenses quite a bit without fully grokking them.
Whenever I read something like this about a framework, I imagine that sooner or later some colleague will fall in love with the power of the framework too much and write code that is very hard to understand/maintain.
But I have to admit that I haven't worked much with Lenses myself yet so maybe things are not so bad?
- kccqzy 4y agoI have. Like many other concepts in Haskell, it takes a bit of time for intuition to kick in. For example you likely wouldn't understand Traversable or Applicative when you first heard of it but it will become second nature in a few days. The only rule I would have is to ban all the operators and force everyone to use actual words for names. For example (^.) is not allowed but `view` is.
- pmoriarty 4y ago> The only rule I would have is to ban all the operators and force everyone to use actual words for names. For example (^.) is not allowed but `view` is. Yeah, this is what I view as a enormous weakness of Haskell compared to Lisp/Scheme, where in the latter descriptive naming is a community norm. It makes for much more readable code... especially if I've been away from the language for a while.
- rssoconnor 4y agoDo you also consider v^.x hard to read when it is Pascal?
- HelloNurse 4y agoPascal has a much smaller number of much simpler operators than Haskell + fancy libraries, which puts a low ceiling on the incomprehensibility of Pascal expressions.
- aranchelk 4y agoPoint of clarification for readers who aren’t familiar with Haskell: Haskell doesn’t have operators in the classic sense, since they’re just an alternate syntax (infix) for regular functions; implemented in libraries. The Haskell ecosystem OTOH has a shitton of infix functions, as does Edward Kmett’s lens library. I don’t think there’s any reason to bother memorizing the ones that aren’t useful to you. You can always search them on Hoogle if you encounter one you don’t know. E.g. https://hoogle.haskell.org/?hoogle=%28%5E.%29&scope=set%3Astackage https://hoogle.haskell.org/?hoogle=%28%5E.%29&scope=set%3Ast... I use lenses frequently in a large application and have only ever used view, set, and over.
- bmacho 4y ago> Haskell doesn’t have operators in the classic sense, since they’re just an alternate syntax (infix) for regular functions Actually in Haskell there is a concept of 'functions' and 'operators'. The former are made up symbols, while the latter are made up mostly alphanumeric characters. Also they parse differently.
- kccqzy 4y agoNo it wasn't hard in Pascal. Pascal had way fewer operators (^ and . being different ones) than the lens library.
- ParetoOptimal 4y ago> The only rule I would have is to ban all the operators and force everyone to use actual words for names. For example (^.) is not allowed but `view` is. I sometimes find myself thinking the same, but then I remember "Notation as a tool of thought"[0] and become less sure. Also I remember some lens compositions being more awkwatd with words, though I can't recall an example. 0: https://www.jsoftware.com/papers/tot.htm https://www.jsoftware.com/papers/tot.htm
- trenchgun 4y agoBut Haskell does not have the squiggly sumbols…
- spopejoy 4y agoWe actually have this rule in our company style guide. We do allow '.=' and '.%' (MonadState ops) because they fit elegantly into 'do' contexts.
- nerdponx 4y agoI think in a lot of cases like this, what's wonky is the actual mathematical details that are necessary for proof of soundness and/or correctness. Understanding it generally is not needed in order to use the thing. It's not unlike abusing obscure details of a web framework to write overly-clever code. Overuse of mathematical abstractions is bad code anyway. Deep understanding is helpful, but not important for general productivity.
- contravariant 4y agoHaskell has a tendency to require mathematics that is by itself wonky. Usually because they use category theory but neglect to mention that they're actually working in a closed monoidal category (or perhaps cartesian closed, it's not 100% clear), and use currying implicitly everywhere. This is why you even have an "Applicative" class of functors for instance, they're the ones that work nicely with the closed monoidal category structure. Somehow this detail gets skipped a lot in explanation of it (and you won't find it mentioned in mathematical texts).
- hither_shores 4y ago> This is why you even have an "Applicative" class of functors for instance, they're the ones that work nicely with the closed monoidal category structure. Applicatives still make sense with only monoidal structure: they're monoidal functors `C -> Set`, or more generally `C -> V` for `C` enriched over `V`.
- contravariant 4y agoI'd be interested to see a justification for that, given that the usual definition is essentially "A functor which preserves the internal hom functor". Regardless I hope you can see what I mean when I say the mathematics you need for Haskell is wonky.
- hither_shores 4y ago> I'd be interested to see a justification for that, given that the usual definition is essentially "A functor which preserves the internal hom functor". Applicatives are monoid objects for Day convolution of functors (in the same way that monads are monoid objects for composition of functors) given by the coend `F \otimes G (x) = \int^{i, j} Hom_C(i \otimes j, x) \otimes_V F(i) \otimes_V F(j)`. These coincide with closed functors by currying if we have closed monoidal structure, but remain well-defined without it. Using `<*>` instead of `liftA2` in the typeclass definition is basically a historical accident, and imo a mistake. > Regardless I hope you can see what I mean when I say the mathematics you need for Haskell is wonky. Wonky is too strong, but I agree that it's usually not treated with anything near the appropriate level of detail.
- girvo 4y agoI have, but not in Haskell. Over in Flow land, well before we had the nicer `?.` nested property access, I worked on a React Native project that used them extensively. It was nice! We didn't do anything too fancy with them, they just allowed for safe (runtime) and safe (type-safe) access of some deeply nested state objects. Worked pretty well, and that project is still being developed today with no complaints (about that side of it anyway) One of the nice things was being able to re-use and compose those lenses. Led to some really nice abstractions that were honestly pretty simple in practice, but to me that's the sign of a well-chosen tool.
- christophilus 4y agoI’ve used lenses, and yes, at least in my case, I couldn’t resist writing clever code. It was a lot of fun to write, but a giant nuisance to read later. I don’t think this is the fault of lenses, though. I think they can add clarity. The problem was that I got curried away. Heyo! I’ve seen the same thing happen with Scala, Clojure, Ramda, Haskell, Rx. Newcomers want to flex their new-found muscles and push the abstractions as far as they can. The result is a world of pain come maintenance time.
- robinsonrc 4y agoCurried away. Nice
- mightybyte 4y agoAs soon as I heard about this formulation back in 2012 or so I concluded that this particular structure was going to be foundational in Haskell and potentially the broader (purely) functional programming world. I agree with your intuition here with respect to that statement, but this case is an exception to that heuristic. I would say that these lenses are the purely functional equivalent to pointers in C. These lenses are type-safe functional pointers that are composable! This lens formulation is much more powerful than the naive lens formulations that came before it and it admits a number of just as powerful related abstractions such as prisms, traversals, etc. Historically I have been pretty skeptical about adopting flavor-of-the-week flashy abstractions in the Haskell ecosystem for use in production code, usually preferring to lean on ADTs and pure functions for their simplicity and power. But lenses are essential. They are the fundamental building blocks for accessing and modifying ADTs. You don't have to understand the complicated type wizardry to use them. They're particularly useful for drilling deep into arbitrary JSON structures. See a reddit comment of mine for some examples: https://old.reddit.com/r/haskell/comments/792nl4/clojure_vs_the_static_typing_world_haskell_in/doyyakq/ https://old.reddit.com/r/haskell/comments/792nl4/clojure_vs_... For more on this, see this excellent video "The Unreasonable Effectiveness of Lenses for Business Applications": https://www.youtube.com/watch?v=T88TDS7L5DY&t=1204s https://www.youtube.com/watch?v=T88TDS7L5DY&t=1204s
- dmix 4y agoAre lenses used much in OCaml the way they are in Haskell?
- tasuki 4y agoSome things are invented and others are discovered. Frameworks are invented, while the concept of lenses is something you could discover on your own. I can't help but believe that the things that are discovered are somehow more important than the ones which are invented. Discovered things are timeless while invented things are fleeting.
- wyager 4y agoA nice thing about optics is that they are strongly typed enough that it's pretty hard to make them do the "wrong" thing. The type errors can be confusing (because the underlying types are rich), but I've rarely/never actually been able to make incorrect optics-based code compile. As far as legacy projects - optics are awesome for refactoring complex nested state operations like you would see in many business-centric applications. A lot of apps are basically just complex nested operations on a big, heterogenous, in-memory data structure, and optics are the best known DSL for reading/writing/processing such structures.
- ParetoOptimal 4y ago> As far as legacy projects - optics are awesome for refactoring complex nested state operations like you would see in many business-centric applications. Motivating lenses will consist of concisely demonstrating this in a RecordDotSyntax world I think. Optics by example does a really good job, but I think we need like a hotel reservation app architected with lenses or something.
- headbee 4y agoI've used Lenses as a beginner/intermediate haskeller and they're very easy to use. It's easy to get bogged down in the theory and specifics but Lenses are perfectly usable on the practical level without ever diving in too deep. That said, now that RecordDotSyntax is a thing, there's less of a use case for surface level lenses.
- dysoco 4y agoNot me personally; but I remember that Mojang used Lenses in some of the Minecraft (or related tools) code: https://github.com/Mojang/DataFixerUpper/blob/8b5f82ab78b30ff5813b3a7f3906cd3f4f732acf/src/main/java/com/mojang/datafixers/optics/Lens.java https://github.com/Mojang/DataFixerUpper/blob/8b5f82ab78b30f...
- Rumudiez 4y agoI used lenses with Ramda JS and Redux to great success. Codebase was very easy to reason about, simple to extend, and never surprised me. Highly recommend this pattern to anyone who likes function composition / piping!
- whateveracct 4y agoHaskell abstractions are true abstractions - not ad-hoc encapsulation like mainstream languages' frameworks. That means you don't ever need to know their internals to understand their semantics.
- anon291 4y agoLenses in Haskell are truly easy. The hardest part around them is the tooling. They'd be much better if GHC could generate them for you. When I go back to imperative languages, like Rust, C++, or Javascript, I find myself missing the easy traversals and folds one can do with lenses. It's like being able to have a Graph QL database for all your data structures instantaneously. When it works, it is truly glorious. The intuition is easy. The fundamental thing is a getter and a setter. Getters can get multiple things, but always produce one answer. So, in order to take multiple things and combine them, you have to fold them. Thus, a getter of multiple things is a fold. A setter can also set / modify multiple things. To take multiple things and produce multiple things is a traversal. Due to the way Haskell works, all traversals are also folds (the math checks out). Everything else is just a specialization of folds and traversals. And honestly, you can view folds as a particular type of traversal (the identity traversal with an effect). Thus, really traversals are fundamental. For example, prisms are just traversals that can target zero or one (but not >1 element). A 'getter' (lens's terminology) is a fold with only one element. A lens is a getter and a traversal. Thus it can only set one element.
- mcluck 4y agoI have. They actually make a lot of sense in the Redux world. We eliminated the need for reducers by just composing lenses