繼續我的上一個問題,我正在嘗試在 scala 3中實作Scrap Your BoilerplatemkT ,現在我遇到了論文中描述的功能的問題。給定以下定義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'sapply方法需要一個型別引數,所以輸入型別將通過傳遞給它的值來推斷,但似乎并非總是如此。更讓我困惑的是Boolean和Salary示例之間的不一致行為。任何想法為什么?此外,在除錯這樣的事情時,有沒有辦法查看正在推斷的型別?除錯器顯示變數的運行時型別,但如果有一種方法可以查看運行時的型別引數,那將會很有幫助。
更新:新想法,這是否與事實有關,S <: Salary而不是S =:= Salary?
uj5u.com熱心網友回復:
您似乎又錯過了一個隱式引數(Haskell 術語中的約束)
inc :: Typeable a => Float -> a -> a
-- ^^^^^^^^^^
inc k = mkT (incS k)
授予
def inc[A](amt: Float): A => A = mkT(incS(amt))(_)
inc(.1)(S(8000)) // S(8000.0) -- not changed
和
def inc[A](amt: Float)(using c: Cast[Salary => Salary, A => A]): A => A = mkT(incS(amt))(_)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
inc(.1)(S(8000)) // S(8800.0) -- changed
整個代碼
https://scastie.scala-lang.org/DmytroMitin/v82LGbOtRieGmJX7gCb99A/1
關于除錯,您可以打開
scalacOptions = Seq("-Xprint:typer", "-Xprint-types")
在build.sbt.
uj5u.com熱心網友回復:
你的mkT樣子和紙上的很不一樣。這是我的看法:
import util.NotGiven
case class Cast[A, B](ev: Option[A =:= B])
object Cast:
given cSome[A, B](using t: A =:= B): Cast[A, B] = Cast(Some(t))
given cNone[A, B](using t: NotGiven[A =:= B]): Cast[A, B] = Cast(None)
def cast[A, B](a: A)(using c: Cast[A, B]): Option[B] = c.ev.map(e => e(a))
def mkT[A, B](f: B => B)(a: A)(using c: Cast[A, B]): A =
c.ev match
case Some(aToB) => aToB.flip(f(aToB(a)))
case None => a
def not(a: Boolean): Boolean = !a
println(mkT(not)(true)) // false
println(mkT(not)('a')) // 'a'
sealed trait Salary
case class S(amt: Float) extends Salary
def incS(amt: Float): S => S = {
case S(a) => S(a * (1 amt))
}
def inc[A](k: Float)(a: A)(using c: Cast[A, S]): A = mkT(incS(k))(a)
println(inc(.1)(S(8000))) // increased to `S(8800.0)`
println(inc(.1)('a')) // left as-is
incS當您將from的型別更改為Salary => Salaryto時,它作業得很好S => S,因為在您的情況下,S它的子型別Salary不等于Salary.
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/519545.html
