Am I missing something, or can `Getter<S,A>`...
# arrow
m
Am I missing something, or can
Getter<S,A>
be contravariant on
S
? I'm trying to use it in a contravariant way and it's not working in compositions. Having it contravariant in the original type would fix this.
r
Hi @Mitchell Skaggs,
S
is declared invariant in
Getter<S, A>
because it defines members like zip where
S
appears covariant as argument.
Copy code
infix fun <C> zip(other: Getter<S, C>): Getter<S, Pair<A, C>>
I believe if we made Getter
S
in
then we will have to defined all those members as extension instead so they can accept additional type args at the function level instead of receiving
S
in the class scope. This causes people having to import manually all extensions. I believe this change would ripple through not just Getter but also fold and others in the hierarchy. Do you have an example reduced code that shows your compilation error?
m
Here's an example of what I want to do:
Copy code
interface MyInterface {
    val myValue: String
}

// 2 or more subclasses
class Implentation1(override val myValue: String) : MyInterface
class Implentation2(override val myValue: String) : MyInterface

val getter = Getter<MyInterface, String> { it.myValue }

val myOtherLens: Lens<Unit, Implentation1> = TODO()

val composition = myOtherLens + getter // Error!
r
Just tested your code. Looks like it can compile if
Fold
has
S
as
in
, results in `
Copy code
val composition: Fold<Unit, String> = myOtherLens + getter
Changing
Fold
S
to
in
is something we can consider as it seems to not require to declare
+
and others as extensions. Not sure if there are additional concerns so maybe we should get @simon.vergauwen and @Jannis opinion since they are more familiar with it.
s
I think we can definitely look into it, having the use-case above compile and work makes sense to me. There is an ongoing discussion/Spike in Monocle regarding this functionality. https://github.com/optics-dev/Monocle/issues/771
m
Wow, I seem to have accidentally stumbled in to a long-running issue. In that case, is the Kotlin type system not powerful enough to concisely describe these variant types?
s
Kotlin has no support for variance on a function level, so to walk around that you need to define such functions as extension functions. For example in `Either`: https://github.com/arrow-kt/arrow/blob/main/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Either.kt#L1075 This is needed for the same use-case as described above.
m
What about changing the variance at the use-site? From Arrow:
Copy code
infix fun <C> compose(other: Getter<in A, C>): Getter<S, C> =
    Getter(other::get compose this::get)

  operator fun <C> plus(other: Getter<in A, C>): Getter<S, C> =
    this compose other
This compiles and it resolves my issue.
j
Hmm we can always use
UnsafeVariance
to silence the variance checker on method implementations to avoid having to use extensions @raulraja, bit annoying but should not be too much of a problem, kotlin itself uses it all over the place iirc. Also use site variance such as what @Mitchell Skaggs suggested could work just fine. Iirc there are some annoying bits to it which is why I did not use it in my optics branch, but worth a try^^
Btw this is part of the reason I love profunctor optics so much, as they can handle this case easily with just one implementation for
compose
thats available for all optics kinds^^
1
r
If use site variance fixes it in the arg types declarations then I think it would be great to have these changes. @Mitchell Skaggs if you’d like to contribute a PR for it we’d be happy to review. Otherwise feel free to create an issue and we will tackle it when we have some time.
💯 1
m
I tested my use site variance solution on the other
compose
and
plus
methods and it appears to work as described in https://github.com/optics-dev/Monocle/issues/770#issuecomment-737077273.
Copy code
infix fun <C, D> compose(other: PIso<in A, out B, out C, in D>): PIso<S, T, C, D>
I'll wrap these
compose
variance changes into a PR in a day or two a few minutes (it wasn't that hard lol)! Thanks for the insight!
👏 2
😍 1
s
@Jannis I experimented with
UnsafeVariance
to move the extension functions from Either to concrete methods, but failed. It caused some other issues. Wow, didn't know we could use site variancee like this 😮 Is this a new feature? 🙃
m
I don't think it's new; I don't know of any changes to variance since like 2013 with mixed-site paper: https://www.cs.cornell.edu/~ross/publications/mixedsite/mixedsite-tate-fool13.pdf
My PR is now ready for CI/review here: https://github.com/arrow-kt/arrow/pull/2415