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

Size predicate validator error #1155

Open
DenisNovac opened this issue Feb 26, 2023 · 7 comments
Open

Size predicate validator error #1155

DenisNovac opened this issue Feb 26, 2023 · 7 comments

Comments

@DenisNovac
Copy link

DenisNovac commented Feb 26, 2023

Lib version: "0.10.1"

There is no validator for types refined with Size[n] predicate. Which leads to an error:

could not find implicit value for parameter v: eu.timepit.refined.api.Validate[Long, T]

In "Practical FP in Scala" book there is this workaround (SizeValidator on the code), but it doesn't work for me anyway with error:

exception during macro expansion:
java.lang.ClassNotFoundException: Playground$Test2$
at scala.reflect.internal.util.AbstractFileClassLoader.findClass(AbstractFileClassLoader.scala:75)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:587)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
at __wrapper$3$9ef546e2ad714e8c98288e38cb01ef12.__wrapper$3$9ef546e2ad714e8c98288e38cb01ef12$.wrapper(:31)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.$anonfun$compile$11(ToolBoxFactory.scala:291)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.eval(ToolBoxFactory.scala:460)
at scala.reflect.macros.contexts.Evals.eval(Evals.scala:32)
at scala.reflect.macros.contexts.Evals.eval$(Evals.scala:26)
at scala.reflect.macros.contexts.Context.eval(Context.scala:18)
at eu.timepit.refined.macros.MacroUtils.$anonfun$eval$1(MacroUtils.scala:22)
at scala.Option.getOrElse(Option.scala:201)
at eu.timepit.refined.macros.MacroUtils.tryN(MacroUtils.scala:26)
at eu.timepit.refined.macros.MacroUtils.tryN$(MacroUtils.scala:25)
at eu.timepit.refined.macros.RefineMacro.tryN(RefineMacro.scala:10)
at eu.timepit.refined.macros.MacroUtils.eval(MacroUtils.scala:22)
at eu.timepit.refined.macros.MacroUtils.eval$(MacroUtils.scala:15)
at eu.timepit.refined.macros.RefineMacro.eval(RefineMacro.scala:10)
at eu.timepit.refined.macros.RefineMacro.$anonfun$validateInstance$2(RefineMacro.scala:53)
at scala.Option.getOrElse(Option.scala:201)
at eu.timepit.refined.macros.RefineMacro.validateInstance(RefineMacro.scala:53)
at eu.timepit.refined.macros.RefineMacro.impl(RefineMacro.scala:25)

import Typ._
import eu.timepit.refined.api.{Refined, Validate}
import eu.timepit.refined.auto._
import eu.timepit.refined.collection.Size

trait SizeValidator {
  implicit def validateSizeN[N <: Int, R](implicit
                                          w: ValueOf[N]
                                         ): Validate.Plain[R, Size[N]] =
    Validate.fromPredicate[R, Size[N]](
      _.toString.length == w.value,
      _ => s"Must have ${w.value} digits",
      Size[N](w.value)
    )
}

object Example extends App {
  println("Hello")
  println(Test2.x)
}

object Typ {
  type SixP  = Int Refined Size[6]
}

// won't work at all because validator not found
/*object Test1 {
  val y: SixP = 123
  val x: SixP = refineV[SixP](123).toOption.get
}*/

// will throw macros error
object Test2 extends SizeValidator {
  val x: SixP = 123
}

Here is scastie runnable example: https://scastie.scala-lang.org/frNAMizRRpaTBaoeJjjrEQ

@DenisNovac DenisNovac changed the title Size predicate validator Size predicate validator error Feb 26, 2023
@fthomas
Copy link
Owner

fthomas commented Feb 26, 2023

Try Size[Equal[n]].

@DenisNovac
Copy link
Author

Validator is still missing.

I also tried to rewrite self-written validator with Equal but the macros error is still here.

  implicit def validateSizeN[N <: Int, R](implicit
                                          w: ValueOf[N]
                                         ): Validate.Plain[R, Size[Equal[N]]] =
    Validate.fromPredicate[R, Size[Equal[N]]](
      _.toString.length == w.value,
      _ => s"Must have ${w.value} digits",
      Size[Equal[N]](Equal[N](w.value))
    )

@fthomas
Copy link
Owner

fthomas commented Feb 26, 2023

Validator is still missing.

Right. You're trying to check if the string representation of an Int has n digits. We don't have a predicate for that.

I also tried to rewrite self-written validator with Equal but the macros error is still here.

Looks like https://github.com/fthomas/refined/blob/master/modules/docs/macro_pitfalls.md. Custom predicates + macros probably won't work in Scastie. refineV works: https://scastie.scala-lang.org/3nGUIQ9tSdKGmQ2lNZE8fw

@DenisNovac
Copy link
Author

They are not working in the IDEA either. Tried to write exact types instead type aliases and it helped. Thanks for helping out!

I didn't really got what it is about in macro_pitfalls. I should replace my SixP alias and validate instance to a different project so i could use refineV directly on it instead of Int Refined Size[6]?

@fthomas
Copy link
Owner

fthomas commented Feb 27, 2023

I didn't really got what it is about in macro_pitfalls.

My recommendation is to not use refined's macros at all. They cause problems like above and are not available on Scala 3.

I should replace my SixP alias and validate instance to a different project so i could use refineV directly on it instead of Int Refined Size[6]?

No, the alias is fine. If you don't use macros, there is also no need to move the Validate instance to another project.

What I've been doing lately is to define a type alias + an object with the same name that extends RefinedTypeOps like this:

import eu.timepit.refined.api.RefinedTypeOps

type SixP = Int Refined Size[6]
object SixP extends RefinedTypeOps[Int Refined Size[6], Int]

which gives you nice syntax for creating SixP instances, like SixP.from(123456). Here is a Scastie that demonstrates this: https://scastie.scala-lang.org/njwd5esnRpe6daeNizS0HQ

@DenisNovac
Copy link
Author

Got it, thanks for clarifying!
Is there any chance of having Validate for Size predicate in refined out of the box?

@fthomas
Copy link
Owner

fthomas commented Feb 27, 2023

There is already a Validate instance for Size in the library but it requires that your base type is Iterable. For example, this would work out of the box: String Refined Size[Equal[6]] // strings with length 6. But your base type is Int (which is not Iterable), so the instance in the library does not match. I guess one could write a higher-order predicate that checks if the string representation matches some predicate. Something like final case class ToString[P](p: P) which could be used to define your SixP like this: Int Refined ToString[Size[Equal[6]]. I'd be open to add ToString to the library.

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

2 participants