I am reading the Scrap Your Boilerplate paper and trying to follow along by implementing the ideas in scala as best I can. However, I'm stuck on the very first function, the cast
, which is used to take a value and attempt to cast it to another type in an Option
, obviously a Some
if the cast is successful, and None
otherwise. I have a version of it working with the following code:
trait Cast[A, B]:
def apply(a: A): Option[B]
object Cast:
given cSome[A, B](using t: A =:= B): Cast[A, B] with
def apply(a: A) = Some(t(a))
given cNone[A, B](using t: NotGiven[A =:= B]): Cast[A, B] with
def apply(a: A) = None
This works when types are statically known. Trying the examples from the paper:
val maybeChar = summon[Cast[Char, Char]]('a') // Some(a)
val maybeBool = summon[Cast[Char, Boolean]]('a') // None
val maybeBool2 = summon[Cast[Boolean, Boolean]](true) // Some(true)
However, when I try to clean up the ergonomics a bit so that we can rely on type inference as in the examples in the paper with a generic helper defined as such:
def cast[A, B](a: A): Option[B] = summon[Cast[A, B]](a)
I'm getting only None
s, meaning the types are never being seen as the same:
val mc: Option[Char] = cast('a') // None
val mb: Option[Boolean] = cast('a') // None
val mb2: Option[Boolean] = cast(true) // None
The same happens even when I'm being explicit with the types:
val mc: Option[Char] = cast[Char, Char]('a') // None
val mb: Option[Boolean] = cast[Char, Boolean]('a') // None
val mb2: Option[Boolean] = cast[Boolean, Boolean](true) // None
I'm using scala 3.2. Is there any way to achieve this cast function with the less verbose ergonomics? I'm even more curious why what I have isn't working, especially with the explicit casting? I'm pretty sure shapeless
is able to provide an SYB implementation in scala 2, albeit probably relying on macros. Can we do this in scala 3 without macros?
CodePudding user response:
You missed implicit parameter
def cast[A, B](a: A)(using Cast[A, B]): Option[B] = summon[Cast[A, B]](a)
// ^^^^^^^^^^^^^^^^
When doing implicit resolution with type parameters, why does val placement matter? (implicitly[X]
vs. (implicit x: X)
in Scala 2, summon[X]
vs. (using X)
in Scala 3)