palatable / lambda

Functional patterns for Java
https://palatable.github.io/lambda/
MIT License
865 stars 85 forks source link

Add disclaimers in the documentation for unlawful lenses #10

Closed Roxxik closed 6 years ago

Roxxik commented 7 years ago

just dropping some comment, might not even be relevant for this lib:

I just found this and i really like the idea to make stuff visible in the types and having some bits of experience in Java and Haskell, i might even use this in the future

but while looking through the readme in the chapter about lenses, something struck me:

lenses have to fullfill 3 laws, and if you do so you can vary the 4 type parameters of a lens freely. they are (https://hackage.haskell.org/package/lens-4.15.1/docs/Control-Lens-Lens.html)

1) You get back what you put in:

view l (set l v s)  ≡ v

2) Putting back what you got doesn't change anything:

set l (view l s) s  ≡ s

3) Setting twice is the same as setting once:

set l v' (set l v s) ≡ set l v' s

coming back to the first example:

Lens<List<String>, List<String>, Optional<String>, String>

would be something like: (where [] is List)

Lens [String] [String] (Maybe String) String

this is obviously not a lawfull lens. getting and then setting won't work, bc of the types involved.

some other examples are violating the laws, too.

If this was done for convenience reasons, I'd suggest making the lenses lawful and adding prisms (doing it right) or explicitly say that the lenses are broken. This is common in Haskell, some lenses can be unlawful under certain circumstances, but it is documented.

Just some random thoughts from me: i think only providing java with lenses won't go a long way, because accessors already compose (foo.bar.baz -> no need for view(bar.compose(baz),foo) ). Keeping it immutable is a bonus from lenses. And polymorphic updates are too. But having Prisms and Traversals pushes this even further, eleminating the need for Iterators and explicit Optional handling.

I haven't considered the verboseness of this in Java which might hinder the overall goal.

And especially more contrived use cases where you want indexed optics can be difficult to get right in java, but might be worth it

EDIT: Grammar

jnape commented 7 years ago

Hi Roxxik -

First, your comments are certainly relevant to this library, so thanks for taking the time to write them.

There are a few points in your comments, so I'll address each in turn.

[...] but while looking through the readme in the chapter about lenses, something struck me: [...] this is obviously not a lawful lens [...]

Correct, and there's no getting around that statement.

[...] If this was done for convenience reasons [...]

A bit reductionist, but your instincts are correct. Originally b was also Optional<String>, but it occurred to me that if the README was a sales pitch for the utility purpose of lenses, and my audience was a community that likely had little to no experiences with them, the current examples are potentially less scary. I suspect this is a rather unsatisfying answer for you. Me, too.

[...] I'd suggest making the lenses lawful and adding prisms (doing it right) [...]

I take this point to heart, and would like to offer an approximation of both Traversable and Applicative in Java, at which point Prism and Traversal should be nearly trivial. I've spiked out what I think is a reasonable Java approximation of both on a stash somewhere, so hopefully these will arrive in the near future.

[...] or explicitly say that the lenses are [unlawful] [...]

I will consider how best to annotate the documentation, pursuant to your concerns.

[I] think only providing java with lenses won't go a long way, [...] Keeping it immutable is a bonus from lenses. And polymorphic updates are too. But having Prisms and Traversals pushes this even further [...]

I get it, and agreed (mostly).

I haven't considered the verboseness of this in Java which might hinder the overall goal

And especially more contrived use cases where you want indexed optics can be difficult to get right in java, but might be worth it

I am Jack's eternal struggle.

In the meantime, let's call this particular Issue "Add disclaimers in the documentation for unlawful lenses", agreed?

Roxxik commented 7 years ago

I'm happy to hear that my concerns were already thought of and are going to be handled.

In the meantime, let's call this particular Issue "Add disclaimers in the documentation for unlawful lenses", agreed?

I'm totally fine with that

I'm not sure if you know purescript's approach to lenses, but it might be worthwhile to look into profunctor-lenses. It handles indexed stuff without Conjoined and is thus usable without type families and type equalities, but is not as capable (Combining IndexedOptics generically is a dread). I have no idea how to handle this in Java.

I am Jack's eternal struggle.

I'm not sure who Jack is...

random thought: what would a Java backend for Purescript look like? Would it be easier to wrap a Purescript lib in Java, instead of doing everything in Java?

jnape commented 7 years ago

I'm cursorily aware of PureScript. I'll check out profunctor-lenses, thanks for the suggestion.

Would it be easier to wrap a Purescript lib in Java, instead of doing everything in Java?

I suspect it would be proportionally difficult to implementing a Java backend for Haskell, which itself seems[1] non-trivial[2].

Even so, this would ultimately undermine one of the reasons I decided to write this library in the first place, which was to allow people to continue to use a language they already understand to learn concepts that they maybe don't understand. It's possible that this whole endeavor will ultimately be a fool's errand, but my confirmation bias tells me it's not.

(p.s. This is Jack)

jnape commented 7 years ago

I always hate when libraries orphan or abort improvement threads like this one, and as I've done a bit of work on this in a stash, I'll revive this thread for documenting my current thoughts.

First things first, none of this affects the future of generalized abstract optics in lambda like Traversal, Prism, and friends. If that was ever on, it is still on. If it doesn't make it, that has nothing to do with the direction proposed in this thread, and is more of an indictment of the pain of these types in the context of Java's type system.

My current thinking is to continue to ship things like OptionalLens with its lift/unlift(S/T/A/B) implementations, each of which, in isolation, necessarily only makes sense in the context of unlawful lenses (and are really just specializations of map(S/T/A/B) on Lens anyway), either sending the category of lawful lenses to the category of unlawful lenses, or vice-versa. This also seems immensely useful, provided Lens remains quadruply parametrically typed.

However, it also seems reasonable to expect that, although Lens provides an interface to intentionally violate lens laws if you're feeling brave, at least all builtin, fully constructed lenses that lambda ships are themselves lawful.

My plan then is to:

Feedback is welcomed and appreciated.

jnape commented 6 years ago

Addressed in commit 7d9de4f