My biggest piece of advice for people using lenses is to ditch all the operators. Things like ^. or ^.. or ^? or ^@.. or even <<|>~ are all real operators. Yet they look like line noise. Nobody fully remembers them anyways. Just ditch all operators. Use named functions. The function toListOf is immediately clear what it's doing (that it takes a structure and a fold to convert to a list) but ^.. is not.
In general I avoid all custom operators and only use operators that are in packages preinstalled by the compiler (basically just base and containers).
I strongly recommend using the lens operators. They are uniformly named such that you can trivially identify their behavior based on their lexical construction, and using them reduces mental parsing overhead significantly.
For the former assertion: ^. means "get a single result". ^.. means "get multiple results". ^? means "get zero or one result". ^@.. means "get multiple results, along with their indices". <<|>~ means "modify a value by combining the target with the |> operator from Snoc, then return a tuple of the old target value and the full structure including the combined value". There is a tiny language in the pattern of operator names, and it's worth the 3 minutes of work it takes to learn it.
And as a reward for learning it, you get to write expressions with far fewer parentheses. This is a massive win. Parenthesized expressions introduce a miserable minigame during reading, where you have to properly match each paren to its correct partner keeping a mental stack to handle nesting. By contrast, the lens operators give you the far simpler mental parsing task of separating the optic, the input, and the operation on the input. There's no nesting involved. The process is a simple visual scan that doesn't require keeping a mental stack. It's a lot easier to quickly read and comprehend.
About the only thing you lose is the ability to easily read code out loud. I don't limit myself to thinking in sounds, but I guess for some people it's important to communicate code out loud. For those kinds of pedagogical purposes, I guess it's ok to pass on the operators. But for code I'm going to work with over a long period of time I'd much rather have the readability advantages of the operators.
Having fewer parentheses is not a win, it makes more things implicit and forces everyone to remember operator precedence. In my opinion operator precedence is never worth remembering other than plus minus multiply and divide.
I find heavily parenthesized expressions easy to read, just because I tend to break them into multiple lines and the indentation serves as a guide. Don't put too many of them on a single line.
That might be a strong argument in many languages, but in Haskell you really don't need to memorize operator precedence. In nearly every case, the types tell you the precedence. They don't literally, but most expressions only type check in one particular parse tree.
As a result, you just don't think about precedence when reading code. If you assume the code type checked correctly, you know that it all just makes sense. You don't need to create a parse tree. You just trust.
(Actually, this is the huge advantage of Haskell in most every case. You don't need to understand everything. You just trust that it does what makes sense, and you're right. The compiler enforces it.)
You may want to check out J as a language. It is wonderfully terse and allows for point-free programming, and has all of the advantages you point to above.
I disagree. There are many operators that you’ll never use but if you memorize
(^.), (.~), and (%~), you’re pretty much set for a lot of real-world software development.
Per Kmett’s original talk/video on the subject, I can confirm my brain shifted pretty quickly to look at them like OOP field accessors. And for the three above, the mnemonics are effective:
“^.” is like an upside down “v” for view.
“.~” looks like a backwards “s” for setters.
“~%” has an tilde so it’s a type of setter and “%” has a circle over a circle, so it’s over.
I’ll also add that my experience in recent versions of PureScript things get even nicer: visible type application lets you define record accessors on the fly like:
foo ^. ln@“bar” <<< ln@“baz”
“.” Is unfortunately a restricted character and is not the composition operator like Haskell, but I alias “<<<“ with “..”
The pretty obvious question with the above is: why don’t you just write “foo.bar.baz”. In my case I use a framework that uses passed lenses for IoC, but I think “%~” is always nicer and less repetitive than the built-in alternative.
Maybe a text-editor should allow the user to look at source code through different "lenses" (pun intended) and show the meanings of symbols whenever the user wants to see them.
Emacs (of course) has `prettify-symbols-mode` which lets you describe symbols (eg lambda) and replacement characters (eg λ); the effect is purely in the display system—the underlying buffer does not get modified.
I agree strongly with this and take it one step further: I avoid the infix backticks that turn functions `into` operators.
But I'm not a hardliner. I do use backticks sometimes when building joins with Esqueleto and I do use a limited set of lens operators, like ^. and sometimes the %= variants if the situation calls for it.
Let’s just say that if you wanted to understand lenses, this is not where you should start; and if you wanted to move to more advanced scenarios, I wouldn’t start here either.
Uhhh... Haskell syntax is simpler than python's or javascript's. It's neither obscure nor impenetrable, but it sounds like it's different than what you're used to.
This is such low hanging bait that I'm not even interested in interacting further with it than: Haskell's syntax is obscure and impenetrable for the vast majority of software engineers because it was designed by FP nerds with zero interest in ergonomics.
It doesn't make it a bad syntax. It is, however, objectively terrible for anyone unfamiliar with it.
If you're in the Clojure world and feel an appetite for something like Optics, checkout the Specter library from RedPlanetLabs/Nathan Marz; it's Optics by another name, but functionally/philosophically quite similar.
I learned lenses from the mentioned Edward Kmett video but wish I'd learned from the "Optics by Example" book instead; it's more cohesive, comprehensive and can save you a bunch of time - https://leanpub.com/optics-by-example/
I don't understand why Haskell can't provide an imperative interface (at the grammar level, not semantic level) to get/set values in a type. If you can provide the do-notation to "simulate" imperative code, then why not?
Haskell's design prioritizes referential transparency and equational reasoning, which would be compromised by imperative get/set operations that mutate state directly - lenses provide a purely functional alternative that maintains these properties.
Everyone is like "Haskell is such a cool language, it's so much more clear concise and understandable than that stupid language you like so much" (their words, not mine). Then you ask them how they write `foo.bar.baz = 1` and you get 50k words of documentation, 113 new operators[1] like `<<<>~`, and a library with 20 new dependencies. I make fun of them only because I love them - I think Haskell has brought us a lot of cool things like Maybe and Either - but how has no one ever taken a step back and gone "wow, this seems a tad complex for what we're trying to accomplish"?
In general I avoid all custom operators and only use operators that are in packages preinstalled by the compiler (basically just base and containers).
For the former assertion: ^. means "get a single result". ^.. means "get multiple results". ^? means "get zero or one result". ^@.. means "get multiple results, along with their indices". <<|>~ means "modify a value by combining the target with the |> operator from Snoc, then return a tuple of the old target value and the full structure including the combined value". There is a tiny language in the pattern of operator names, and it's worth the 3 minutes of work it takes to learn it.
And as a reward for learning it, you get to write expressions with far fewer parentheses. This is a massive win. Parenthesized expressions introduce a miserable minigame during reading, where you have to properly match each paren to its correct partner keeping a mental stack to handle nesting. By contrast, the lens operators give you the far simpler mental parsing task of separating the optic, the input, and the operation on the input. There's no nesting involved. The process is a simple visual scan that doesn't require keeping a mental stack. It's a lot easier to quickly read and comprehend.
About the only thing you lose is the ability to easily read code out loud. I don't limit myself to thinking in sounds, but I guess for some people it's important to communicate code out loud. For those kinds of pedagogical purposes, I guess it's ok to pass on the operators. But for code I'm going to work with over a long period of time I'd much rather have the readability advantages of the operators.
I find heavily parenthesized expressions easy to read, just because I tend to break them into multiple lines and the indentation serves as a guide. Don't put too many of them on a single line.
As a result, you just don't think about precedence when reading code. If you assume the code type checked correctly, you know that it all just makes sense. You don't need to create a parse tree. You just trust.
(Actually, this is the huge advantage of Haskell in most every case. You don't need to understand everything. You just trust that it does what makes sense, and you're right. The compiler enforces it.)
Per Kmett’s original talk/video on the subject, I can confirm my brain shifted pretty quickly to look at them like OOP field accessors. And for the three above, the mnemonics are effective:
“^.” is like an upside down “v” for view.
“.~” looks like a backwards “s” for setters.
“~%” has an tilde so it’s a type of setter and “%” has a circle over a circle, so it’s over.
I’ll also add that my experience in recent versions of PureScript things get even nicer: visible type application lets you define record accessors on the fly like:
foo ^. ln@“bar” <<< ln@“baz”
“.” Is unfortunately a restricted character and is not the composition operator like Haskell, but I alias “<<<“ with “..”
The pretty obvious question with the above is: why don’t you just write “foo.bar.baz”. In my case I use a framework that uses passed lenses for IoC, but I think “%~” is always nicer and less repetitive than the built-in alternative.
But I'm not a hardliner. I do use backticks sometimes when building joins with Esqueleto and I do use a limited set of lens operators, like ^. and sometimes the %= variants if the situation calls for it.
Kotlin's Arrow library hits a good middle ground between FP wizardry and readability, and their documentation on lenses are understandable for the average person: https://arrow-kt.io/learn/immutable-data/lens/ / https://arrow-kt.io/learn/immutable-data/intro/
Uhhh... Haskell syntax is simpler than python's or javascript's. It's neither obscure nor impenetrable, but it sounds like it's different than what you're used to.
It doesn't make it a bad syntax. It is, however, objectively terrible for anyone unfamiliar with it.
https://academy.fpblock.com/haskell/tutorial/lens/
https://gcanti.github.io/monocle-ts/
https://github.com/redplanetlabs/specter
I find it to be a lot more comprehensible and transparent than the Haskell version.
[1]: I'm not even exaggerating - https://hackage-content.haskell.org/package/lens-5.3.5/docs/...