[HN Gopher] Exercises for understanding Haskell Lenses (2019)
       ___________________________________________________________________
        
       Exercises for understanding Haskell Lenses (2019)
        
       Author : revskill
       Score  : 94 points
       Date   : 2022-12-06 09:33 UTC (13 hours ago)
        
 (HTM) web link (williamyaoh.com)
 (TXT) w3m dump (williamyaoh.com)
        
       | wk_end wrote:
       | For anyone - anyone who's faffed around with JavaScript's spread
       | syntax when working with nested immutable data, anyway -
       | struggling to understand the general concept or utility of
       | optics, I'd encourage looking into monocle-ts. The motivation [0]
       | that kicks off the README instantly made me a believer, and
       | TypeScript's idiosyncratic type system makes using the library
       | remarkably natural; it fits right in without needing to deal with
       | anything analogous to Template Haskell.
       | 
       | [0] https://github.com/gcanti/monocle-ts#motivation
        
       | shinzui wrote:
       | Lens is one of my favorite libraries, but I can understand why
       | optics, in general, is a divisive topic. Like many things in
       | Haskell, you won't appreciate optics without suffering through a
       | steep learning curve.
       | 
       | Optics don't just save you a few keystrokes. Instead, they make
       | tedious code that you would never write trivial. And more
       | importantly, they're composable. So it's impossible to appreciate
       | their effectiveness without using them on a real-world app. For
       | example, I've worked in an app with three layers, each with its
       | record types, and without the lens library, the pain of
       | converting between records would have been insufferable. Instead,
       | most developers would couple the three layers together to avoid
       | the problem, which makes the codebase brittle.
        
       | bradrn wrote:
       | (2019) -- although it hasn't affected the correctness of the
       | article, as far as I can tell
        
       | macpete42 wrote:
       | Lovely all these nonsense operators like ^. .~ just to save a few
       | characters and make everything more magic than it is.
        
         | garethrowlands wrote:
         | The closest equivalent your fave language has are also
         | operators. ^. does roughly what . does in most languages. And
         | .~ does roughly what = does.
        
         | yamtaddle wrote:
         | I don't understand why Perl is consistently dismissed as "line
         | noise" and "write-only" but that characterization's rarely
         | leveled against Haskell. It's much worse about it, IMO. At
         | least the "line noise" variety of Perl doesn't usually dominate
         | basic tutorials and introductions to the language.
         | 
         | [EDIT] Nor do Perl libraries tend to _contribute to_ or
         | _encourage_ line-noise style.
        
           | Tainnor wrote:
           | I agree that FP languages sometimes exaggerate with the
           | opeators (I think introducing new operators has its uses but
           | the learning curve has to be traded off against the supposed
           | benefits), but...
           | 
           | Perl is IMHO worse because it relies much more on global
           | mutable context. For example, the diamond operator <> behaves
           | differently depending on whether the magic global variable
           | @ARGV is set.
           | 
           | By contrast, Haskell is typed and purely expression based, so
           | that at least in principle you can pull apart expressions,
           | examine them (and test them!) individually and then
           | understand how they combine.
        
         | klntsky wrote:
         | > just to save a few characters and make everything more magic
         | than it is
         | 
         | No, this is not the true motivation. Human attention spans on
         | symbols, the more succinct the code is, the more meaning it can
         | encompass in the same number of characters. Writing abstracted
         | enough code often requires inventing new languages to hide the
         | complexity behind abstractions.
         | 
         | Software development in Haskell is particularly focused on
         | DSLs, lens language being one of them.
         | 
         | ^.. is a relatively simple operator though, it just abstracts
         | away traversals.
        
           | garethrowlands wrote:
           | Yes and you tend to use ^. in Haskell where you'd use . in
           | python, say. And . is also an operator. Similarly, you'd
           | likely be using = in python where you'd use .~ in python. As
           | for ^.., I'm not sure there's an idiomatic python equivalent
           | - certainly nothing as simple as adding an extra . to the
           | operator.
        
             | klntsky wrote:
             | They are in fact well structured and not random at all.
             | Take a look at this:
             | 
             | https://github.com/ekmett/lens/wiki/Operators
        
           | AnimalMuppet wrote:
           | > Human attention spans on symbols, the more succinct the
           | code is, the more meaning it can encompass in the same number
           | of characters.
           | 
           | I could make it even more succinct by LZW compressing it.
           | That would not _improve_ comprehension, though...
        
           | dmitriid wrote:
           | > Writing abstracted enough code often requires inventing new
           | languages to hide the complexity behind abstractions.
           | 
           | Human _attention_ may pick out symbols more easily, but human
           | _comprehension_ benefits from things that actually have
           | meaning. Otherwise you end up with a soup of ASCII symbols
           | that very few can parse since every library in Haskell seems
           | hellbent on inventing new and useless ascii combinations for
           | function names and operators.
           | 
           | Even the article goes (emphasis mine):
           | 
           | --- start quote ---
           | 
           | The basic operators here are ^., .~, and %~. If you're not a
           | fan of funny-looking operators, Control.Lens provides _more
           | helpfully-named functions called view, set, and over_ (a.k.a.
           | mapping)
           | 
           | --- end quote ---
           | 
           | And then proceeds to use the ASCII soup instead of the _more
           | helpful_ function names.
           | 
           | It doesn't help in the least that there are multiple get
           | operations ^. ^? ^.. Or this idiocy: "Here, a single < before
           | the operator signifies that it returns the updated value,
           | while a double << signifies that it returns the old value.
           | This gets a little weird with the monoidal operator; <<>~
           | returns the updated value, not the old one". Bonus points.
           | What does this do: <<<>~
           | 
           | Because "why have useful names when you can pretend your fake
           | intellectual superiority by using confusing ascii symbol
           | combinations"
           | 
           | > Software development in Haskell is particularly focused on
           | DSLs, lens language being one of them.
           | 
           | The domain specific _language_ for lenses is view, set,
           | traverse, update etc. Not whatever this is.
        
         | vintermann wrote:
         | One of the bad things about having mathematicians design your
         | language.
        
           | niek_pas wrote:
           | I take your point, but this is not a language design problem
           | -- it's a library design problem. These are just function
           | names.
        
             | vintermann wrote:
             | I kind of see idioms like lenses as part of the language.
             | 
             | But the language allows defining your own (infix)
             | operators, so the core language designers weren't entirely
             | innocent either. The abundance of custom operators in
             | Haskell kind of made me appreciate Lisp and Forth a little
             | more!
             | 
             | There are of course many good things about having your
             | language designed by mathematicians, too.
        
             | anentropic wrote:
             | it's a culture problem, the library is like this because
             | that's the culture of the language
        
               | whateveracct wrote:
               | It's not a culture problem it's just culture. Just
               | because you don't like it doesn't mean Haskell is wrong
               | lol.
        
       | azalemeth wrote:
       | I must confess that it took me until the first code sample to
       | realise that the "lenses" mentioned are not those described by
       | 1/u+1/v=1/f....
        
       | seanparsons wrote:
       | I think most people would find these clearer if they used the
       | functions view/set/etc, rather than the various operators which
       | are just alternatives to those.
        
         | JadeNB wrote:
         | > I think most people would find these clearer if they used the
         | functions view/set/etc, rather than the various operators which
         | are just alternatives to those.
         | 
         | Everyone has their own tastes. Someone unfamiliar with the
         | notation of ordinary algebra might argue for "you will have to
         | find two numbers that the difference between the two is 10
         | (that is, so much as is our number) & that we make the product
         | of these two quantities, the one multiplied by the other,
         | exactly 1, that is, the cube of the third part of the variable"
         | (https://www.maa.org/press/periodicals/convergence/how-
         | tartag...) as easier to understand than "find u and v such that
         | u - v = 10 and u v = 1", but I think most modern readers would
         | agree that people uncomfortable with the algebra are better
         | served by learning how to read the latter than by sticking with
         | the former. (And, I think, also that it doesn't help either to
         | keep the variables but replace the symbolic operations by
         | words: `(and (eq (subtract u v) 10) (eq (mult u v) 1))`, in
         | pseudo-Lisp.)
        
           | simiones wrote:
           | The thing is, basic algebra notation has two major advantages
           | over ad-hoc operators in some Haskell library: (1) it is
           | widely taught and understood and extremely widely applicable,
           | and (2) each operator has a standard name that is widely
           | explained.
           | 
           | In contrast, most Haskell notation I've seen is either an ad-
           | hoc invention for some library, or it is an ASCII version of
           | notation in a niche domain like category theory. Even
           | Haskell's >>= operator for flatMap/bind seems to be an
           | invention, as far as I can tell the equivalent concept in CT
           | is Kleisli composition, denoted by a sharp sign and the
           | regular composition operator (as far as Wikipedia shows - I'm
           | not formally trained in CT).
           | 
           | Additionally, people rarely if ever give a proper name to
           | this notation in Haskell, making it completely impenetrable
           | to even represent the formulas in your mind. How am I
           | supposed to read `user1 ^. name` ? When I see `[?]f` I know
           | how to read it (del cross f, or nabla cross f, or curl f)
           | because that was an explicit part of how I was taught the
           | operation (and note that it is not an arbitrary digraph, it
           | can really be computed as the cross product of the pseudo-
           | vector nabla and f), but Haskell tutorials and documentation
           | completely skip this step, in my experience.
        
             | rssoconnor wrote:
             | Not that you would necessarily use the same words in
             | Haskell, but I'm curious how you would read `user1^.name`
             | in Pascal, or `user1->name` in C or `(*user1).name` in C?
             | 
             | (Edit: I'd also be curious about `x += y` and `x << y` in
             | C.)
        
               | AnimalMuppet wrote:
               | user1^ is what the user1 pointer points to, if I remember
               | my Pascal correctly. It's a structure and the .name says
               | to take the name field of that structure. So ^. isn't a
               | digraph, it's two separate operators.
               | 
               | Same with your C example. It's doing the exact same thing
               | in C, except the operators (* and .) are separated from
               | each other.
               | 
               | -> _is_ a digraph. It means the same as `*.` I don 't
               | usually pronounce it, I just think of it as itself. If I
               | have to say it to myself mentally, I say "sub". I'm not
               | sure I've ever tried to say it aloud to a coworker; if I
               | did, I might have said "arrow" or something.
               | 
               | You do have to learn these things, just like you have to
               | learn all the other operators. But simiones still has a
               | point - at least the math-based operators are much more
               | widely known and understood than the category-based ones.
        
               | anon291 wrote:
               | But what are their names? That was the standard applied
               | to Haskell.. that the operators needed well understood
               | names. C++ does not have that. Worse still, C++ operators
               | are completely unintellible without context.
               | 
               | For example, '>>=' (commonly called bind in haskell) is
               | well-specified. Anything I see it used with is going to
               | be a Monad, and thus follow certain laws. Examining the
               | imports, I can immediately tell what any operator is.
               | 
               | In C++? Forget about it. The 'left-shift' operator which
               | is supposed to shift bits to the left, can somehow also
               | print things to standard output. In what world can the
               | terminal be bit-shifted left? In fact, we understand this
               | because no one reads 'std::cout << "Hello world"' as
               | 'shift std::cout left by "Hello world", because such a
               | thing is non-sense, whereas '1 << 2' is '1 shifted two
               | bits to the left'.
               | 
               | EDIT: And then, when you add in external libraries, it
               | gets worse. `<<` can be used for creating lexers and
               | parsers in boost if I recall correctly. Completely
               | lawless, and, when you survey the ecosystem, also
               | dangerous. So many bugs in C++ and such due to this.
        
               | rssoconnor wrote:
               | I agree. I do feel there is a double standard here. C
               | gets away with using `->` and `^=` and `*foo.bar` mixes
               | prefix and infix and postfix with hard to remember
               | precedence rules; C uses `{` and `}` instead of `BEGIN`
               | and `END` and no one bats an eye. But when lens libraries
               | use operators, suddenly some people lose their mind.
               | 
               | I perfer `rec^.field` with operators in lens for the same
               | reason I prefer `ptr^.field` in Pascal over
               | hypothetically writing `ptr DEREFERENCE ACCESS field` or
               | `ACCESS(DEREFERENCE(ptr),field). It lets me hide what is
               | essentially just plumbing-like-grammer behind operators
               | in order to let me focus of the parts of the program
               | related to the business logic at hand, namely `rec`,
               | `ptr` and `field`. Otherwise the plumbing tends to drown
               | out the more important parts of what is going on.
        
               | AnimalMuppet wrote:
               | What are their names? "Member access" is the name of
               | "->". That's a pretty well-understood and easily-
               | understood name.
               | 
               | > Worse still, C++ operators are completely unintellible
               | without context.
               | 
               | Um, compared to what? The C++ operators require less
               | context to understand than the Haskell lens operators by
               | a _large_ amount, so this seems like a really odd
               | criticism.
               | 
               | Your last two paragraphs... yeah. Operator overloading
               | has been used for some, uh, _unusual_ uses, even by the
               | standards committee.
        
               | anon291 wrote:
               | > Um, compared to what? The C++ operators require less
               | context to understand than the Haskell lens operators by
               | a large amount, so this seems like a really odd
               | criticism.
               | 
               | This is my exact point. The C++ operators require much
               | more context.
               | 
               | Suppose you see:
               | 
               | `v << i`
               | 
               | in c++. What is the type of v? What is i? Is it an int?
               | Or an output stream? Or something else entirely! I have
               | _no idea_. By the C++ standard it could be anything. In
               | fact, it is heavily dependent on what 's in scope, and
               | indeed, several different things could overload it
               | depending on what's in scope. Very confusing.
               | 
               | In haskell,
               | 
               | 'x ^. i'
               | 
               | tells you everything. 'x' is any type, but 'i' is
               | mandated to relate to 'x' in that 'i' is an optic
               | targeting 'x' and returning some 'subfield' of x. In
               | particular, 'i' is 'Getter a b', where 'a' is the type of
               | 'x'. That is absolute. There is no other interpretation.
               | Haskell type class overlap rules prevent any module
               | imports from confusing the matter.
        
               | AnimalMuppet wrote:
               | _Riiiiiiight..._
               | 
               | I'll agree that overloading << to do output is a WTF
               | moment. But at least I can hire programmers off the
               | street who know what output _is_. In contrast, that last
               | paragraph of yours looks like you were carrying around a
               | bowl full of random jargon, tripped, and spilled it all
               | over your keyboard.
               | 
               | For your C++ example, I have to know the type of v and i
               | (and that in C++, it's possible to overload operators).
               | For your example, I have to know what an "optic" is, what
               | a "Getter" is, what Haskell thinks a "subfield" is (and
               | that means that 'x' can't be _any_ type, but has to be
               | the kind of type that contains a subfield), and I suspect
               | several other background things that I don 't even know
               | what they are.
               | 
               | C++ makes you know a bit more about the types (though
               | even in Haskell, you had to know that x had subfields).
               | Haskell makes you know much more concept-level context.
        
             | JadeNB wrote:
             | > The thing is, basic algebra notation has two major
             | advantages over ad-hoc operators in some Haskell library:
             | (1) it is widely taught and understood and extremely widely
             | applicable, and (2) each operator has a standard name that
             | is widely explained.
             | 
             | > In contrast, most Haskell notation I've seen is either an
             | ad-hoc invention for some library, or it is an ASCII
             | version of notation in a niche domain like category theory.
             | 
             | But that's my point! When it was introduced, none of the
             | symbology of school algebra satisfied condition (1) or (2);
             | it was ad hoc (perhaps supported by some reasoning--as
             | Reade's for the equal sign being two parallel lines, "than
             | which no two things are more equal"--or perhaps not), and
             | was found fully as abstruse and obfuscatory as you find
             | symbolic operators in Haskell. Even Arabic numerals were
             | thought a device for lies and deception, compared to good
             | honest Roman numerals, when first introduced.) If we hadn't
             | adopted those operators _anyway_ , then we'd still be
             | writing out all our equations in words as Tartaglia did,
             | and our mathematics would be the poorer for it; had we
             | stopped our notation earlier, we would still, as in pre-
             | Arabic numeral times, send our children to special advanced
             | schools to learn multiplication. New notation when first
             | introduced is confusing and strange, but, once made
             | commonplace, enables new thought, so that what was a niche
             | domain becomes commonplace.
        
           | wakamoleguy wrote:
           | In Haskell, replacing the symbolic operations doesn't imply
           | we have to abandon infix operations:
           | 
           | (and (eq (subtract u v) 10) (eq (mult u v) 1)) could be:
           | 
           | (u `subtract` v `eq` 10) `and` (u `mult` v `eq` 1)
        
         | mejutoco wrote:
         | From the beginning of the article:
         | 
         | > The basic operators here are ^., .~, and %~. If you're not a
         | fan of funny-looking operators, Control.Lens provides more
         | helpfully-named functions called view, set, and over (a.k.a.
         | mapping); these three operators are just aliases for the named
         | functions, respectively. For these exercises, we'll be using
         | the operators.
        
           | n0us wrote:
           | ...yes and it would be clearer if they used the named
           | functions
        
       | zeendo wrote:
       | My all-Haskell company has avoided the use of Lens whenever
       | possible and I haven't missed it for a second.
       | 
       | Its payoff doesn't seem to justify the cognitive overhead
       | (concepts, operators, etc) that it brings.
       | 
       | I'd highly recommend that newcomers to Haskell avoid Lens in
       | their own code for as long as possible. The juice really isn't
       | worth the squeeze.
        
         | goostavos wrote:
         | Haskell foot-guns lenses into something awful by doing what so
         | many FP libraries do: over generalizing themselves into
         | impenetrable category theoretic self-indulgence. Why do I need
         | to understand Profunctors to get a value inside of a record?!?
         | 
         | Lenses themselves are awesome. The "bad", naive, not-general
         | type is really simple.
         | 
         | ``` newtype Lens a b = Lens {get :: (a -> b), set :: (a -> b ->
         | a)} ```
         | 
         | So simple that modifiers can be mechanically generated for your
         | data types. And suddenly, you can express super-duper complex
         | modifications without all the obnoxious boilerplate it'd
         | otherwise take.
        
           | anon291 wrote:
           | That lens is completely useless. If you are equivocating
           | lenses to struct accessors, then you're adding abstractions
           | for nothing. Lenses are good for things like, extracting all
           | unique values out of deeply nested data structures for
           | example.
           | 
           | Say you have two data structures
           | 
           | data Struct1 = Struct1 { someStuff: [ Struct2 ] } data
           | Struct2 = Struct2 { otherStuff: HM.HashMap Text Struct3 }
           | data Struct3 = Struct3 { name: Text }
           | 
           | And you have a list `[Struct1]` and want a `[Text]` of the
           | `names` of Struct3. How do you do this in an imperative
           | language? Manually accessing the fields, for loops, etc.
           | 
           | The van Laarhoven formulation of lens and ekmett's lens
           | library optiocs in Haskell makes this trivial.
           | 
           | `myList ^.. someStuff . each . otherStuff . each . name`
           | 
           | Now, what if you wanted to modify all the names and prefix
           | them with 'id-'? Again.. trivial.
           | 
           | `myList # someStuff . each . otherStuff . each . name %~
           | ("id-" <>)`
           | 
           | Your formulation is just field accessors. Not composable, and
           | ultimately no better than just using the generated field
           | accessors haskell provides.
        
             | Hercuros wrote:
             | One thing that does always strike me about such libraries
             | is that they are often relatively easy to use for common
             | scenarios like that, and the mental model is really not so
             | complicated or abstract as to require category theory or
             | something like that. When you use such libraries, I don't
             | think you are really doing deeply category-theoretic
             | thinking. More so just seeing how the types fit together
             | and maybe thinking of some higher-order functions that you
             | want to combine and apply.
             | 
             | If you take Haskell and remove all the types (e.g. Scheme
             | or JavaScript), you can have all the same abstractions,
             | with basically the same syntax and operational behavior.
             | But then of course the operational behavior is just
             | applying/composing functions in the end... And because
             | there are no higher-kinded types to deal with, category-
             | theoretic language is less likely to sneak in.
             | 
             | I say this as someone who has used Haskell for years and
             | studied category theory / PL theory as well. And I like all
             | of those things, just think it's sometimes not really
             | necessary to get the mental model at all and be able to use
             | those tools.
             | 
             | I think that some code duplication is also not always the
             | worst thing. Just because strings can be "combined" through
             | concatenation and numbers can also be "combined" through
             | addition does not necessarily mean you need one general
             | notion/function to capture both, especially because if you
             | take that to an extreme then you end up with some function
             | that "sqoogles the byamyams" (i.e. the abstraction has
             | become so general that the only way people really
             | understand/use it is by looking at its concrete
             | instantiations).
        
               | ghostwriter wrote:
               | > If you take Haskell and remove all the types (e.g.
               | Scheme or JavaScript), you can have all the same
               | abstractions, with basically the same syntax and
               | operational behavior.
               | 
               | This isn't generally true, as such a language would have
               | to be lazy by default and the property is more important
               | than having all the types, because certain abstractions
               | that can be expressed "naturally" in Haskell is a
               | byproduct of non-strict semantics. Also it's the property
               | that sometimes helps avoiding worst-case complexity of
               | evaluations - something that eager languages have to live
               | with at all times, no matter the abstractions.
               | 
               | There's a few examples here:
               | https://augustss.blogspot.com/2011/05/more-points-for-
               | lazy-e...
        
         | djha-skin wrote:
         | The whole lens thing was what really turned me off to Haskell
         | in the first place. It seems like I would never get it and it's
         | hard to want to be in a language where you don't get something.
        
           | _huayra_ wrote:
           | Is there a language where you _get_ everything? (i.e. there
           | 's nothing you don't get)
           | 
           | Based on the people who have amount of "C++ experts" I've
           | interviewed over the years who don't know much about template
           | metaprogramming or weird rules about initialization and class
           | layout, yet have shipped meaningful code while avoiding most
           | of the weird parts of the language.
           | 
           | Even languages like Python can get weird, such as default arg
           | initialization:                 def add_item_to_list(item,
           | list_to_add=[]):         list_to_add.append(item)
           | return item            a = add_item_to_list(1)
           | a.append(3)       b = add_item_to_list(2)
           | 
           | What do `a` and `b` print?
           | 
           | This is my go-to Python 1st-round question, and many prolific
           | Python coders don't get through this.
           | 
           | From what little I've learned of Haskell so far, it seems
           | like one can eschew the weird PL-heavy parts of the language
           | and just be productive (with the tradeoffs the language
           | contains, e.g. GC).
        
             | quibono wrote:
             | Do you mean ``` def add_item_to_list(item, list_to_add=[]):
             | list_to_add.append(item) return list_to_add
             | a = add_item_to_list(1)       a.append(3)       b =
             | add_item_to_list(2)
             | 
             | ```
        
             | goostavos wrote:
             | You ask this in interviews...? What makes this a good 1st
             | round question in your mind? What is there to learn about
             | the candidate?
             | 
             | It seems to me that it's one of those things you've either
             | encountered (and go "well, that's weird") or... haven't
             | encountered (and go "well, that's weird" upon having it
             | explained). Have pop quizzes about the sharp edges of
             | various languages proved useful as a filtering mechanism?
        
               | anon291 wrote:
               | As someone who deals in low-level code and has been at
               | several different companies... Absolutely. This stuff can
               | be the difference between a good product and 'leaks
               | customer secrets at runtime'. It is irresponsible to
               | program in a language and ship code when you do not
               | understand.
               | 
               | It is scary that software engineers want to be considered
               | professionals, and yet don't want to be held responsible
               | for understanding the tools they use.
               | 
               | Would you trust a bridge designer who didn't really
               | understand the intricacies of concrete? Not me, no thank
               | you.
        
               | insanitybit wrote:
               | I think it'll depend on the role.
               | 
               | If you're coming into a role saying "I'm very familiar
               | with Python" and you don't know one of the most famous
               | 'gotchas', that says something.
               | 
               | If you're coming into a role and saying "Hey, I'm not so
               | familiar with Python but I have experience in other
               | languages and I'd love to learn it as part of this role"
               | I think that question is not appropriate.
        
               | caconym_ wrote:
               | The _responsible_ thing to do is to have a robust code
               | review culture and cover your code with enough tests that
               | boneheaded mistakes in this vein have a very low chance
               | of making it into production. There is no amount of
               | experience that can prevent boneheaded mistakes. If your
               | users ' privacy depends on your programmers understanding
               | all the footguns and weird little corner cases in the
               | tools they use, and on _implementing that understanding
               | perfectly whenever they write code,_ you already don 't
               | deserve said users' trust.
               | 
               | Is it fair to expect a candidate claiming fluency in
               | language X to back that claim up? Sure. But I don't think
               | the general thrust of your comment is very productive.
        
           | anon291 wrote:
           | What is there to get? If you elide the actual implemetation,
           | and go off of syntax, it's just
           | 
           | `struct ^. field1 . field2` to get stuff
           | 
           | and
           | 
           | `struct # field1 .~ blah` to set stuff.
           | 
           | It's syntactical memorization on par with any other language.
           | A bit strange, but just syntax.
        
       | SaddledBounding wrote:
       | The title needs to be changed, because:
       | 
       | > This isn't meant as a lens tutorial. Prerequisites are some
       | basic knowledge of how to use Haskell, GHCI, and familiarity of
       | the idea of lenses, if not the specifics.
       | 
       | With that out of the way, it's kind of disappointing that the
       | article isn't a tutorial. As it is, it just lists the lens
       | operators, gives you alternate, but still non-obvious names for
       | them, and foists some exercises upon you. It could have easily
       | given one short example for each operator without adding any
       | significant tedium for those who already have an idea of what
       | they do, and then it would have been a perfectly fine tutorial.
       | 
       | So here's some examples, (Hoogle has some nice examples too;
       | thanks Hoogle):                   person = Person {_name =
       | "Alice", _title = "Dr"}              -- get field with magic lens
       | person ^. title == "Dr"              -- create new object with
       | new field value         person & name .~ "Bob"             ==
       | person & (name .~ "Bob")             == (name .~ "Bob") person
       | == Person {_name = "Bob", _title = "Dr"}              -- create
       | new object with function applied to field value         person &
       | name %~ lowercase             == (name %~ lowercase) person
       | == Person {_name = "alice", _title = "Dr"}
        
       | ToJans wrote:
       | I remember being stuck on Haskell lenses for a while, until I
       | asked the brilliant Scott Wlaschin during a speakers' dinner in
       | Lithuania to explain lenses in layman's terms to me, to which he
       | simply replied "it's just like property getters and setters".
       | Short, to the point, and I immediately understood the core value
       | prop. There's of course a lot more to it, but in essence that's
       | what it's about...
        
       | worldsayshi wrote:
       | When finding out about lenses some years ago I really felt that
       | they could be immensely useful in the way they could potentially
       | separate all data mapping concerns from the rest of the code
       | base.
       | 
       | But I quickly ended up in dependency or type hell when I tried
       | using them. I hope that people will take inspiration from the
       | concept in the future.
        
         | [deleted]
        
         | bradrn wrote:
         | > But I quickly ended up in dependency or type hell when I
         | tried using them.
         | 
         | Wouldn't know about the dependency issues, but the newer
         | 'optics' library [https://hackage.haskell.org/package/optics]
         | has much simpler type signatures and greatly improved error
         | messages. Personally I don't like it, since it hides the
         | implementation details too much for my taste, but it seems to
         | be becoming pretty popular in the Haskell community.
        
         | gnull wrote:
         | It may be a matter of experience. I probably would not be able
         | to use them during my first or second year of slowly learning
         | Haskell, but later when I learned to naturally think in terms
         | of functors, applicative and traversables, I just sat down and
         | used them.
         | 
         | A lot of the time when I couldn't completely untangle some type
         | definition in lens library, I'd just go with gut feeling or
         | guess what it does by analogy with stuff I already understood,
         | and surprisingly often it compiled and worked like I expected.
         | 
         | I found Hoogle very useful for this guesswork.
        
           | jerf wrote:
           | I have as a general principle that it is difficult to
           | understand a solution until you have the problem.
           | 
           | You will need to attain a certain degree of fluency in
           | Haskell before you have the problem that deep access into
           | structures or any of the other problems lens can solve [1] is
           | a clearly-separated problem that rates among your biggest.
           | 
           | Until you reach that point, I advise not to touch lens. By
           | the time that is your biggest problem, it is likely that your
           | other Haskell experiences will make it not too frightening,
           | but it's not a very good gateway into Haskell.
           | 
           | [1]: One of my favorite uses, though not a common one, is
           | that you can use it to abstract a pair (thing to operate on,
           | how to operate on it) in a way where both the elements are
           | still fully composable, where the lens is sitting in the
           | second spot of that tuple, and whatever is operating on that
           | only needs the type of the "how to operate on it" part, it is
           | oblivious to the type of the "thing to operate on". I used it
           | to build a game engine where the engine did not have a hard-
           | coded concept of "player 1 goes, then player 2 goes, then
           | player 1 goes", etc.; instead, the game logic itself decided
           | that and all the engine got was a combination of what structs
           | to manipulate and what to perform on that struct. (The engine
           | itself did networking and converted things to and from JSON,
           | while letting the pluggable game logic do everything else.)
           | An interesting use for lenses beyond just "better record
           | syntax" as I started passing the lenses themselves around in
           | my code as first-class values. But probably ultimately an
           | even worse way to get into Haskell than using lenses as
           | record syntax improvements.... my point here is just that
           | lenses do have uses beyond record accessing, that's just
           | their headline usage.
        
           | gnull wrote:
           | I must admit though that I've spent quite a lot of effort on
           | playing with types that time, and often neglected carefully
           | designing the architecture of my program and the way I'm
           | applying lenses there, which made me regret it later.
        
       | valyagolev wrote:
       | we use lenses in react/typescript together with jotai atoms to
       | express data paths in complex structures. it's an amazing basic
       | idea that is done a disservice by the proliferation of weird
       | concepts and operators
        
       ___________________________________________________________________
       (page generated 2022-12-06 23:02 UTC)