typelevel / cats

Lightweight, modular, and extensible library for functional programming.
https://typelevel.org/cats/
Other
5.22k stars 1.19k forks source link

Scala 3 type inference related issue #4043

Open agilesteel opened 2 years ago

agilesteel commented 2 years ago

Hi I don't know how to explain it in short and I'm also not sure whether it's a Scala3 issue. I reproduced it with the scala-cli. If you have it installed have a look at this gist. There are 2 flies and each has a Main method in different packages. You can run them with either

scala-cli https://gist.github.com/agilesteel/05482df2e1f420f762dec7696088552d -M good.Main

or

scala-cli https://gist.github.com/agilesteel/05482df2e1f420f762dec7696088552d -M bad.Main

The Good.scala file in there has no dependencies on cats-core. I defined my own trait Semigroup and my own instance for Option. I also defined a Maybe ADT with the new enum keyword and an instance for it as well.

enum Maybe[+A]:
  case Just(a: A)
  case Nothing

I noticed 2 things:

  1. Some(1) combine Some(2) compiles and works even though my instance is for Option. I don't have an instance for Some, which is surprising. So we don't need .some anymore?!
  2. Maybe.Just(1) combine Maybe.Just(2) compiles and works as expected since the enum keyword generates factories which return the Maybe type instead of the Maybe.Just type (as opposed to manual ADTs in Scala 2 for instance).

The Bad.scala file in there has a dependency on cats-core (2.6.1 but I doubt that the version matters much) and therefore I used the Semigroup from cats. I found the following:

Maybe.Just(1) combine Maybe.Just(2) // does not compile BUT IT SHOULD!
[error] ./Bad.scala:24:11: value combine is not a member of bad.Maybe.Just[Int]
[error]   println(Maybe.Just(1) combine Maybe.Just(2)) // does NOT compile
[error]

however

val maybe = Maybe.Just(1) // notice that there is no type ascription or any other type information
maybe combine Maybe.Just(2) // compiles!!!

I used Scala 3.1.0 but other Scala 3.x versions behaved the same.

My expectation is that at least the Maybe examples should compile, right? Why the Option examples work in Good.scala is a mystery to me, but I guess this one is not a cats issue.

I also created this issue https://github.com/lampepfl/dotty/issues/13948

SethTisue commented 2 years ago

I suggest you include the complete, exact error message that the compiler is emitting.

agilesteel commented 2 years ago

Thx, done.

bishabosha commented 2 years ago

I removed my comments from before as it was a misunderstanding of the whole context - scala 3 style extensions seem to improve selection of extension methods

agilesteel commented 2 years ago

@bishabosha cool so does this mean that this issue becomes a feature request/improvement to encode typeclasses in cats differently (i.e by using the Scala 3 style extensions)?

On the other hand the different widening based on whether sth is in a val or inlined remains weird. It doesn't seem like intentional behavior of Scala3.

bishabosha commented 2 years ago

this comment might be off-topic but - @agilesteel enum widening is intentionally only when it is the result of some definition, maybe this scope could be increased to more places, it depends on the use case - but it should stay possible to keep the type narrow if required

agilesteel commented 2 years ago

but it should stay possible to keep the type narrow if required

Isn't it enough that it's possible to keep the type narrow via new Maybe.Just(1) as opposed to Maybe.Just(1)?