Just curious, what would people think of using `%=...
# arrow
n
Just curious, what would people think of using
%=
to avoid repeating the variable name, when using a lens to modify something? Current we have something like:
Copy code
var longVariableName = Foo(Bar(Baz(5)))
longVariableName = Foo.bar.baz.int.modify(longVariableName) { 20 }
Instead, it would be possible to do:
Copy code
longVariableName %= <http://Foo.bar.baz.int|Foo.bar.baz.int> { 20 }
Obviously any augmented operator works, I just chose modulus, because modulus -> mod -> modify 🙂
For those who are curious, it's pretty trivial to implement
Copy code
package foo

import arrow.optics.*

@optics data class Frob(val bar: Bar) { companion object }
@optics data class Bar(val baz: Baz) { companion object }
@optics data class Baz(val int: Int) { companion object }

class Modifier<T, U>(val lens: Lens<T, U>, val f: (U) -> U)

operator fun<T, U> Lens<T, U>.invoke(f: (U) -> U) = Modifier(this, f)

operator fun<T, U> T.rem(modifier: Modifier<T, U>) = modifier.lens.modify(this, modifier.f)
p
use a tuple instead of Modify
there’s no need for a nominal type
@simon.vergauwenwhat’cha think?
n
yeah, can just use pair come to think of it
With this approach, you can get setting values to be almost as concise as in the mutable case. There's only one real piece of "waste" left, which is having to mention the top level class, which in principle is redundant since we should know that from the variable we assign to
but I dont' see any other way around that
the other potential concern is if you want to set multiple values, the overhead of creating a new object every single time seems a bit nasty. Whereas with copy, you would at least only perform a single copy, to change multiple fields. I didn't see any way to compose that mentioned in the arrow docs but I can't say I was super thorough.
Something which might be fun would be to allow + on the right hand side, to set multiple things
Copy code
longVariableName %= <http://Foo.bar.baz.int|Foo.bar.baz.int> { 20 } + Foo.bar.baz.string { "hello" }
hmm there may not be any reasonable way to do that though, with lenses 😞
(well, you can do it, but avoiding the multiple copies I mean)
Improved version:
Copy code
class Modifier<T>(val f: (T) -> T)

operator fun<T, U> Lens<T, U>.invoke(f: (U) -> U) = Modifier<T> { this.modify(it, f) }

operator fun<T> T.rem(modifier: Modifier<T>) = modifier.f(this)

operator fun<T> Modifier<T>.plus(modifier: Modifier<T>) = Modifier<T>({ modifier.f(this.f(it)) })
Copy code
var x = Frob(Bar(Baz(5, "hello")))
    x %= <http://Frob.bar.baz.int|Frob.bar.baz.int> { 15 } + Frob.bar.baz.string { "bye" }
    println(x)
allows writing stuff like this
p
why do you need Modifier here, instead of just
T -> T
?
plus is just simple function composition 😄
Copy code
operator fun<T, U> Lens<T, U>.invoke(f: (U) -> U) = { this.modify(it, f) }

operator fun<T> T.rem(modifier: (T) -> T) = modifier.f(this)

operator fun<T> ((T) -> T).plus(modifier: (T) -> T) = this compose modifier
n
overloading operator+ on callables seems like a pretty bad idea
p
then use compose instead
x %= <http://Frob.bar.baz.int|Frob.bar.baz.int> { 15 } compose Frob.bar.baz.string { “bye” }
n
I like the nice syntax though 🙂
I'm not sure what the problem is
Modifier is an implementation detail
This approach gets the nicest possible syntax for the user (that I've seen), with minimal likelihood of interference with anything else
p
we reduced global operator overloading where we could, for reasons that you mention
that additional wrap may be unnecessary
n
You're saying the downside of this is performance?
p
it’s an opinion thing, it’s okay
one additional wrap, which has never stopped us 😄
you can even use inline type, although it’d still be boxed because it’s generics
n
If it's performance, I can understand. Beyond that, I don't 🙂 Yeah, I'm not used to thinking about performance this way.
In C++ land it's just a given this gets optimized out
p
in Java/Kotlin operator overload beyond the basics is frowned upon
n
That said, the cost of the extra wrapper is probably trivial compared to the fact that we're making copies, over and over
p
lense copies you mean?
n
Yes. When you "compose" here, to replace multiple field
you actually copy each time
p
yep, immutability hahaha
for me it’s what you said, operator overload for syntax
given that the whole thing is based off operator overload too, it’s not too disruptive
in general we avoid it save for very obvious cases, i.e. monoid
n
well, with immutable datastructures (list, map, etc) it's pretty much always a given that if you know all the things you want to update at once, you can do it reasonably efficiently
Here, we know all the things we want to update at once, yet we still copy N times.
Pretty bad.
I think this is decent but mostly it just convinces me that kotlin should probably just first class something into the language to deal with this.
The problem with Kotlin (and Java, and C#...) is that they dont' have anything like C++'s const (or Rust's mut)
p
Haskell has lenses because record syntax suuuucks among other things
n
That means that if you create a mutable data class, say, it's a complete free for all
any function you pass it into, can mutate it
p
accessing the field of a record is not trivial, it’s so funny it’s sad
@Nir ye, JVM has its own handwaving around many things
n
in C++, you usually use mutable structs, but functions at least take them by const, so they cannot mutate fields
p
you won’t find that kind of attention to certain details
n
So, I tend to have a much stronger preference towards immutability in Kotlin than I did in C++
p
at the same time it’s more rigid in some ways than say, typescript
n
Yeah. Hence why I think it's worth it for Kotlin to provide some nice syntax, to make immutable data classes pleasant to use.
it's unfortunate that immutability tends to get seen as this ultra functional feature. I don't really care about immutability that much per se, it's just that observable reference semantics are very bad
☝🏼 1
yet, it's what most mainstream languages have 🙂
r
@Nir this is interesting syntax and maybe worth a ticket in meta as we are gonna tackle the optics and generics plugin next and any ideas about user desired features for data and sealed classes would help us see what is missing from our current codegen.
n
@raulraja Sure. Where would you like me to open a ticket (assuming I understood correctly)
@pakoito
in Java/Kotlin operator overload beyond the basics is frowned upon
Well, Java does not have operator overloading, unless they added it recently? In Kotlin, I guess basics are relative 🙂 Kotlin's
-
for lists is really bizarre, IMHO. I've never seen that in any other language.
n
Thanks, will post my code and thoughts from above
r
Thanks!