Skip to content
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

use Equal[_] with case object #735

Open
bwiercinski opened this issue Feb 13, 2020 · 9 comments
Open

use Equal[_] with case object #735

bwiercinski opened this issue Feb 13, 2020 · 9 comments

Comments

@bwiercinski
Copy link

bwiercinski commented Feb 13, 2020

I want to achieve:

sealed trait Status extends Product with Serializable
object Status {
  case object A extends Status
  case object B extends Status
  case object C extends Status
}

import shapeless.::
import shapeless.HNil
import eu.timepit.refined.api.Refined
import eu.timepit.refined.boolean.OneOf
import eu.timepit.refined.generic.Equal
import eu.timepit.refined.auto._

type ValidStatus = OneOf[Equal[Status.A.type] :: Equal[Status.B.type] :: HNil]

val validStatus: Status Refined ValidStatus = Status.A

and i get

<console>:22: error: compile-time refinement only works with literals
       val validStatus: Status Refined ValidStatus = Status.A

when i changed to

import eu.timepit.refined.auto.refineMV
val validStatus: Status Refined ValidStatus = refineMV[ValidStatus](Status.A)

i got

<console>:20: error: implicit error;
!I v: Validate[A.type, OneOf[Equal[A.type] :: Equal[B.type] :: HNil]]
OneOf.oneOfHConsValidate invalid because
!I vt: Validate.Aux[A.type, OneOf[Equal[B.type] :: HNil], OneOf[RT]]
――OneOf.oneOfHConsValidate invalid because
  !I vh: Validate.Aux[A.type, Equal[B.type], RH]
――――Equal.equalValidate invalid because
    !I wu: WitnessAs[B.type, A.type]
――――――WitnessAs.natWitnessAs invalid because
      !I ta: ToInt[B.type]
       val validStatus: Status Refined ValidStatus = refineMV[ValidStatus](Status.A)                                                                     

refineV also not working

@kubukoz
Copy link
Contributor

kubukoz commented Feb 13, 2020

I think we can generalize this issue to that refineMV and refined.auto don't handle singleton objects, just literal singleton types (at least that's what I think it is).

@kubukoz
Copy link
Contributor

kubukoz commented Feb 25, 2020

In fact, it doesn't work on literal singleton types either, just inline literals :/

@fthomas
Copy link
Owner

fthomas commented Feb 25, 2020

In fact, it doesn't work on literal singleton types either, just inline literals :/

@kubukoz Could you provide an example? The example below works as expected and is not using an "inline literal".

scala> val s = "Hello"
s: String = Hello

scala> refineMV[Equal[s.type]]("World")
<console>:37: error: Predicate failed: (World == Hello).
       refineMV[Equal[s.type]]("World")
                              ^

@fthomas
Copy link
Owner

fthomas commented Feb 25, 2020

@bwiercinski refineV works if the value you pass in is of type Status:

scala> refineV[ValidStatus](Status.A: Status)
res5: Either[String,eu.timepit.refined.api.Refined[Status,ValidStatus]] = Right(A)

scala> refineV[ValidStatus](Status.C: Status)
res6: Either[String,eu.timepit.refined.api.Refined[Status,ValidStatus]] = Left(Predicate failed: oneOf((C == A), (C == B), false).)

It will never work with refineMV because that macro needs to evaluate its value parameter at compile-time and that is only safe for literals. If the constructor of Status.A would contain side-effects, evaluating it at compile-time would be a bad idea.

@kubukoz
Copy link
Contributor

kubukoz commented Feb 25, 2020

@fthomas but Status.A is a singleton object, its type is fully known at compile-time.

As for the literal singletons:

type X = String Refined Equal["foo"]

@ val a: X = "foo"
a: X = foo

@ val b: "foo" = "foo"
b: String = "foo"

@ val c: X = b
cmd11.sc:1: compile-time refinement only works with literals
val c: X = b
           ^
Compilation Failed

which is basically the opposite way (the literal is the value I want to refine, and in your example it's the type to refine to)

@fthomas
Copy link
Owner

fthomas commented Feb 25, 2020

Yes, but refineMV requires the value at compile-time to decide if it satisfies the predicate. And it is not safe to evaluate Status.A at compile-time since this would execute its constructor.

@kubukoz
Copy link
Contributor

kubukoz commented Feb 25, 2020

Could we inspect the type of Status.A and use the fact that it's a singleton?

I tried doing this myself but I'm not that familiar with the scala-reflect API and couldn't figure that out soon enough...

@kubukoz
Copy link
Contributor

kubukoz commented Feb 25, 2020

Oh, I see. That would probably be impossible in the general case (some validations will really require an instance). Equal is fine, but it's a special case.

However, for literal singleton types we could use the name of the type as the value.

@fthomas
Copy link
Owner

fthomas commented Apr 14, 2020

However, for literal singleton types we could use the name of the type as the value.

You're right. But I wouldn't recommend changing RefineMacro for this since it is 2.13-only and I'm not sure if RefineMacro will survive the transition to Scala 3.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants