refined
refined copied to clipboard
Size predicate validator error
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
Try Size[Equal[n]].
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))
)
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
Equalbut 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
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]?
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
SixPalias and validate instance to a different project so i could userefineVdirectly on it instead ofInt 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
Got it, thanks for clarifying!
Is there any chance of having Validate for Size predicate in refined out of the box?
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.