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.
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.
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.
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.
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).
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...
However, it was hardly ever used in practice because it isn't polymorphic and, perhaps more importantly, it can only be used for accessing a single item from a record.
The reason for the explosion of interest in "optical" libraries in the 2010s is because of the development of other optics that can simultaneous access multiple fields (in order), access a functor full of fields (unordered), access field that maybe doesn't exist, pattern match on a variant, zip an indexed set of fields, etc. And all of this is done is both a composable, and transparent way.
Haskell's Traversable functors existed in 2008, and while your lens type can also be traversed, you have to explicitly convert it to a traversable to use it and, or compose it that way.
I'm sorry you feel the need to understand profunctors in order to use optics. Ideally the system would abstract that all way. Unfortunately in Haskell, some of these components leak out (how much varies from implementation to implementation) because this abstraction uses constraints and polymorphism in a very novel way. It's kinda amazing that Haskell's type system accepts it at all.
The alternative seems to involve a proliferation of casting of optics upon every use, requiring either chaining of casts or writing n^2 casting functions from between every related pair of optics.
[0]https://web.archive.org/web/20071023064034/http://luqui.org/...
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).
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?
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.
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.
It's pretty easy to just never bump into certain corners of languages -- even if you use said language extensively. I've used Python for, like, 8(ish) years professionally. I maintain a few moderately popular open source libraries. I honestly don't recall exactly what happens when you do the `list_to_add=[]` thing, because these days I pretty much never pass in a list with the purpose of having it be mutated by something else. I know is that I did try it once, a long time ago, and that it does something unexpected. But I couldn't tell ya more than that just by looking at in.
Color me dismayed to discover that I'm actually a hack fraud.
What I said elsewhere still stands. Yes... this kind of thing is the difference between 'works well' and 'exposes private user data to other users'. I'm glad you don't use such constructs, but realistically, given python's closure rules and when data with interior mutability are captured, this situation arises more than just in this particular situation. And like I said... it can easily lead to exposing private information to other users.
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.
it would be extremely hard for a Python developer not to encounter this core property of the language (call by assignment), unless the candidate has just graduated from a random 1-month bootcamp, which may be the purpose of discovering of with the first-round question.
a = add_item_to_list(1)
a.append(3)
b = add_item_to_list(2)
```I should not try to HN comment while running out the door to catch my bus...
Trick question it's a type error?
`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.