Hacker News new | past | comments | ask | show | jobs | submit login

What do you think about the lens library?

I have been trying Haskell for a few weeks and this library feels strange. Also I don't really think I need the things it provides. But perhaps I just don't understand it.




Lens is difficult to get your head around. It is simultaneously a rather simple idea ("bundle getters and setters together... ") and a ridiculously sophisticated and generalized implementation ("... in every conceivable lawful way such that they all work together as they ought to!").

You probably do want lenses. Learning to use them is its own "mind blowing" experience similar to learning functional programming itself. Better, they integrate smoothly with Haskell so once you've learned them the cost of using them is quite small.

Unfortunately, learning lenses from `lens` itself is quite hard. There is some amount of training materials (shameless plug here)

    https://www.fpcomplete.com/user/tel/a-little-lens-starter-tutorial
    https://www.fpcomplete.com/user/tel/lens-aeson-traversals-prisms
but the library itself is typically targeting a proficient user far better than a new one. I'd recommend trying to implement your own lenses as a learning task. If you don't bother generalizing it as far as Ed has then the core concept is very simple. I wrote up a CodeWars task for this using the Van Laarhoven style used in `lens`, but there are even simpler forms as well.

    http://www.codewars.com/kata/54258ffb430ca2e4b5000239


There's also simpler packages, right? Targeted at having less power, but being more understandable as a result.

Was lens-family one of the simpler ones?

    https://hackage.haskell.org/package/lens-family


There are a lot of lens packages. I'd roughly split them into the "Van Laarhoven style" and the "Abstract style".

In the Abstract Style there is `data-lens` and `fclabels`. These use some kind of packaged form of (getter,setter) and require a Category instance to compose using (.). They are all mutually incompatible with one another.

In the Van Laarhoven Style are `lens`, `lens-family`, and `lens-family-core` of which the last is the simplest. These are all mutually compatible, and compose automatically using regular function composition and the Prelude version of (.), but they're somewhat hairy to see at first since they must expose their underlying type (for subtyping purposes) and that one is pretty unexpectedly weird.

I recommend using Van Laarhoven style lenses. If you really want an abstract style lens, fclabels is the best I've been told.


Thank you.


You can think of a lens as just a pointer (like in C). They're not exactly like a pointer...you can't get or modify the address that it points to. But you can get and modify the thing that it points to, which is primarily how you use pointers in C.

But lenses can do things that pointers can't do. Namely, they can be composed. If you have a lens that points to a person's birth date

    dob :: Lens Person Date
...and a lens that points to a date's year field:

    year :: Lens Date Int
Then you can compose them like this:

    (dob . year) :: Lens Person Int
Normally it would be easy to retrieve the person's birth year with plain function composition. Lenses let you do that too. But setting the person's birth year is much more painful. This is where lenses shine.

    set (dob . year) 2004 person


Huh? In C, if you have a function from Person* to Date* and another function from Date* to Int*, you can compose them as well, and then use the resulting function to both get and set a person's birth year. The syntax is admittedly ugly, but I don't see any asymmetry between get and set.


Yes, I was talking about plain pointers (i.e. Person*), not functions of pointers.

Sure, you can do both get and set, but the simple syntax is precisely what we're going for here. Without lenses that set line would have looked like this:

    person { dob = (dob person) { year = 2004 } }
And this is just one level of nesting. Adding more levels gets really ugly. Also, lenses make it easy to do this kind of thing in a dynamic and generalized way.


"Functions of pointers" do seem an in-place equivalent for simple lenses. Pointers themselves less so - you can't compose pointers, and they take zero (when getting) or one (when setting) argument whereas a lens takes one or two.

Much like traditional getters and setters, lenses can offer more functionality though - for instance, an angle can be got and set in radians or degrees.


Yeah, that's interesting. I chose pointers because I was looking for a simple metaphor that imperative programmers would understand easily. If you ask random C programmer how to pass a single thing to a function such that you can both get and set it, pointers are the first thing that most of them will think of.


Pointers being symmetric was the point of his analogy. The asymmetry shows up when the setter is pure, returning a new version of the record instead of mutating it (as is the case in Haskell).

A getter is simple:

    x = s.foo.bar;
As is an impure setter:

    s.foo.bar = y;
But a non-mutating setter is a bit more work:

    Foo foo2 = s.foo;
    foo2.bar = y;
    S s2 = s;
    s2.foo = foo2;


It's a pretty advanced library, and I don't think you should use it if you've only been trying Haskell for a few weeks. I've been using Haskell for about 7 years, 5 of which commerically, and I've never used it seriously. I find the smaller lens libraries, like 'fclabels', solve the most important problems 'lens' also solves, but with a lot less surface area and complexity. But even that can wait until you're further along.


For me it greatly simplifies my data access code. Sometimes I'll have multiple-layer nested data objects, and it's just not reasonable to access that any other way.

It's useful, but the amount of magic in it can be off-putting. For average developers it's a tradeoff of magic for functionality. Debugging it can be a nightmare if you use too much of the magic, but using it for simple data access and setting can be pretty straightforward.


Lens is fantastic when you're doing a lot of work with deeply nested data. Often times, you shouldn't be doing that in the first place.


I found this talk by edward kmett incredibly helpful[0]. I kept rewatching parts of it bit by bit until I managed to absorb it all (there is a lot of information for a beginner) but I found it hugely helpful and mind-blowingly powerful.

[0] https://www.youtube.com/watch?v=cefnmjtAolY


Lens has some very advanced abstractions, which can lead to some very thorny error messages. I second the suggestion to stay away from it for now.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: