从我的previous question开始,我尝试在Scala 3中实现Scrap Your Boilerplate,现在遇到了本文中描述的mkT
函数的问题。假设cast
的定义如下:
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
def cast[A, B](a: A)(using c: Cast[A, B]): Option[B] = c(a)
我试着让mkT
如下所示:
class MakeTransform[A] (val f: A => A) {
def apply[B](b: B)(using c: Cast[A => A, B => B]): B = c(f) match {
case Some(fb) => fb(b)
case _ => b
}
}
def mkT[A](f: A => A): MakeTransform[A] = MakeTransform(f)
这似乎适用于布尔型示例:
def not(a: Boolean): Boolean = !a
mkT(not)(true) // false, function is clearly called on input value
mkT(not)('a') // 'a'
但是,当我尝试使用公司模型对象时,只有当我提供显式类型调用并且参数与该类型匹配时,才能使其按预期运行。因此,给定以下Salary
定义:
sealed trait Salary
case class S(amt: Float) extends Salary
def incS(amt: Float): Salary => Salary = {
case S(a) => S(a * (1 + amt))
}
val ralf: Employee = E(P("Ralf", "Amsterdam"), S(8000))
我尝试引发Salary
:
inc(.1)(S(8000)) // S(8000) <= no change
但是,除非我明确指出以下类型:
inc(.1)[Salary](S(8000)) // S(8800.0)
但当我这样做时,我只能将指定类型的对象作为输入传递:
inc(.1)[Salary](ralf) // does not compile
这显然违背了目的。
我的想法是,因为MakeTransform
的apply
方法接受类型参数,所以输入类型将通过传递给它的值来推断,但情况似乎并不总是这样。更让我困惑的是Boolean
和Salary
示例之间的不一致行为。你知道为什么吗?另外,在调试这样的事情时,是否有方法可以看到推断的类型?调试器会显示变量的运行时类型,但如果有方法在运行时查看哪些类型参数,这将是很有帮助的。
更新:新的想法,这是否与S <: Salary
而不是S =:= Salary
有关?
2条答案
按热度按时间mcvgt66p1#
您似乎又错过了一个隐式参数(Haskell术语中的约束)
会商
使用
整个代码
https://scastie.scala-lang.org/DmytroMitin/v82LGbOtRieGmJX7gCb99A/1
关于调试,您可以打开
在
build.sbt
中。ykejflvf2#
你的
mkT
看起来和报纸上的完全不同。以下是我对此的看法:当您将
incS
的类型从Salary => Salary
更改为S => S
时,它可以很好地工作,因为在您的例子中,S
是Salary
的一个子类型,它不等于Salary
。