我正在尝试在各种* -> * -> *
之间进行自然转换
所以我想拿F[A, B] => G[A, B]
具体来说,我正在尝试定义一个DSL,然后将其转换为实际的功能定义,因此MyDSL[A, B] => Function[A, B]
这是自然转化的定义:
trait ~~>[F[_, _], G[_, _]] {
def apply[A, B](fab: F[A, B]): G[A, B]
}
object ~~> {
def apply[F[_, _], G[_, _]](implicit f2g: F ~~> G): F ~~> G = f2g
}
DSL看起来像这样:
sealed trait MyDSL[A, B]
object MyDSL {
case object Add1 extends MyDSL[Int, Int]
case object Show extends MyDSL[Int, String]
implicit def dsltoF: MyDSL ~~> Function = new ~~>[MyDSL, Function] {
override def apply[A, B](fab: MyDSL[A, B]): Function[A, B] = fab match {
case Add1 => i => i + 1
case Show => i => i.toString
}
}
}
直接使用自然变换可以正常工作:
dsltoF(Add1)
输出:res0: Function[Int,Int] = MyDSL$$anon$2$$Lambda$1816/700824958@6f3aa425
甚至在返回的函数是带有两个类型参数的方法的情况下也可以使用。
当我尝试定义使用一种类型参数的通用方法进行转换的DSL对象时,就会遇到问题。
case class Id[A]() extends MyDSL[A, A]
implicit def dsltoF: MyDSL ~~> Function = new ~~>[MyDSL, Function] {
override def apply[A, B](fab: MyDSL[A, B]): Function[A, B] = fab match {
case Id() => identity[A] _
case Add1 => i => i + 1
case Show => i => i.toString
}
}
我收到found A required B
编译错误。
Scala无法识别B在这种情况下是A。
我明白了为什么类型参数A和B不一定与我要返回的函数的定义正确相关,因此即使这样写:
case Add1 => i => i + 1
IntelliJ中有红线,因为即使“添加”是MyDSL [Int,Int],也没有意识到。尽管Scala对此表示满意。
类型参数对在自然转换上应用的方法签名开放所有可能,但在这种情况下,它需要某种限制。我的猜测是,由于DSL案例类中没有值来限制类型参数,它归结为模式匹配,这已经超出了Scala解释方法签名的位置,因此它期望使用不同的类型B,并且树皮。
我当然可以通过.asInstanceOf棘手的方式解决这个问题,但是我的意思是加油。
任何将其付诸实践的不同策略的想法都会受到赞赏。
答案 0 :(得分:2)
在语言的当前版本中,这是类型推断系统中的已知限制,在将来的版本中应取消限制。
在这种情况下,您可以在模式匹配中使用类型变量来解决此限制:
import scala.language.higherKinds
trait ~~>[F[_, _], G[_, _]] {
def apply[A, B](fab: F[A, B]): G[A, B]
}
object ~~> {
def apply[F[_, _], G[_, _]](implicit f2g: F ~~> G): F ~~> G = f2g
}
sealed trait MyDSL[A, B]
object MyDSL {
case class Id[A]() extends MyDSL[A, A]
case class Const[A, B](constantResult: B) extends MyDSL[A, B]
case object Add1 extends MyDSL[Int, Int]
case object Show extends MyDSL[Int, String]
implicit def dsltoF: MyDSL ~~> Function = new (MyDSL ~~> Function) {
override def apply[A, B](fab: MyDSL[A, B]): Function[A, B] = fab match {
case _: Id[x] => identity[x] _
case c: Const[a, b] => (_ => c.constantResult)
case Add1 => i => i + 1
case Show => i => i.toString
}
}
}
本质上:如果没有“位置”可供编译器存放更多特定的类型信息,只需在模式中为其提供类型变量,以便可以将推断出的类型信息附加到该位置。