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.
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.
Sure, a Haskell programmer does write toList when relevant. The lens library also has operators named each or over or _Left.
But let's say I write <$> and you tell me to use fmap. Is that any better? Not particularly. If you didn't know what Functor is, you wouldn't automatically be able to infer it by reading the name `fmap`.
A large number of Haskell concepts simply have no analogue in everyday English.
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.
I find it to be a lot more comprehensible and transparent than the Haskell version.
https://www.stackage.org/haddock/lts-23.27/base-4.19.2.0/Dat...
https://hackage-content.haskell.org/package/bluefin-0.0.16.0...
[1]: I'm not even exaggerating - https://hackage-content.haskell.org/package/lens-5.3.5/docs/...
A monad is a flatmappable.
A monad is a flatmappable.
A monad is a flatmappable.
The easiest example would be something like wrapping a bunch of arithmetic operations with a "cumulative" monad. Effectively this changes your add, sub, mul, div functions such that instead of taking 2 floats and returning a float, they take a hashmap and return a hashmap. The hashmap consists of the original args as well as the cumulative total, for whatever reason. The details of the hashmap are hidden from you, you use the functions as per normal.
You could also make the wrapper monad have some state, and then batch the operations while making them appear to execute sequentially, or make it appear you are doing pure logic when I/O is happening under the hood.
While you can do monads in dynamic languages, it can be hard to reason about changes to the code without strong compiler support, so typically you see it more often implemented in statically typed languages.
In dynamic languages such as lisp you might be better off writing a small interpreter, and in OO languages there are other patterns that might serve the purpose better.
I still don't know what a monoid is though. Or an applicative.