Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Allow summon typeclass instance of opaque type defined in companion object while deriving. #17639

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
jilen opened this issue Apr 13, 2023 · 3 comments

Comments

@jilen
Copy link

jilen commented Apr 13, 2023

import io.circe._
case class Foo(id: Foo.Id) derives Codec.AsObject // Cannot find encoder/decoder of Id
object Foo {
  opaque type Id = Long
}

The above very common opaque type use case won't compile.
Seems compiler don't know Id is Long while deriving.

Note, if opaque type defined in top level, it compiles just fine.

import io.circe._
opaque type FooId = Long
case class Foo(id: FooId) derives Codec.AsObject // Compiles ok
@jilen jilen changed the title Allow summon instance of opaque type defined in companion object while deriving. Allow summon typeclass instance of opaque type defined in companion object while deriving. Apr 14, 2023
@ivan-klass
Copy link

I think the one I created might be related: https://github.com/lampepfl/dotty-feature-requests/issues/344

I personally think that type classes for opaque types shouldn't automatically pass-through to underlying type, as it's breaking the concept of a "different type".
Consider the following:

opaque type PositiveInt = Int
def positive(i: Int): Option[PositiveInt] = Option.when(i > 0)(i)
def toInt(p: PositiveInt): Int = p

@jilen
Copy link
Author

jilen commented Apr 15, 2023

Not pretty sure, but I thought opaque type is basically used as newtype instead of refined type.

@ivan-klass
Copy link

Not pretty sure, but I thought opaque type is basically used as newtype instead of refined type.

However, Refined library is implemented exactly as opaque types for scala3. And many typeclasses (e.g. io.circe.Decoder, sttp.tapir.Shema, com.monovore.decline.Argument has extra modules to derive their typeclasses properly for Refined types, respecting their "business-logic".

In general I'm trying to say that by-passing typeclasses to underlying type shouldn't be automatic as it will make them unsafe - the intention of opaque type as I see it - is to encapsulate the underlying type at zero runtime cost. If no encapsulation is cared, then why not using a simple type alias (without opaque keyword)?

But as for companion-object case, I agree that by-passing opaque typeclass for derivation looks eligible, because the opaque type defined right in the companion object which (I believe) is the scope of derivation...
So /issues/17262 is the root cause and the thing to fix

@ckipp01 ckipp01 transferred this issue from lampepfl/dotty-feature-requests May 31, 2023
@scala scala locked and limited conversation to collaborators May 31, 2023
@ckipp01 ckipp01 converted this issue into discussion #17640 May 31, 2023

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants