从 DSL 到函数的 Scala 自然转换
Scala Natural Transformation from DSL to Function
我正在尝试在各种 * -> * -> *
之间执行自然转换
所以我想 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$$Lambda16/700824958@6f3aa425
它甚至适用于返回函数是采用 2 个类型参数的方法的情况。
当我尝试定义一个使用一种类型参数的泛型方法进行转换的 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 中有红线,因为它没有意识到即使添加 "is a" MyDSL[Int, Int]。虽然 Scala 对此没问题。
类型参数对应用于自然变换的方法签名的所有可能性都是开放的,但在这种情况下它需要某种限制。我的猜测是因为 DSL case class 中没有值来限制类型参数,它归结为模式匹配,这已经超过了 Scala 解释方法签名的地方,因此它期望一个不同的输入 B 它会叫。
我当然可以通过 .asInstanceOf 肮脏的方式解决这个问题,但我的意思是来吧。
如果有任何不同的策略来实现这一点,我们将不胜感激。
这是该语言当前版本的类型推理系统中的一个已知限制,应该在未来的版本中取消。
在这种情况下,您可以在模式匹配中使用类型变量来解决此限制:
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
}
}
}
本质上:如果没有 "place" 编译器可以存放更多特定类型信息的地方,只需在模式中给它一个类型变量,以便它可以将推断的类型信息附加到它。
我正在尝试在各种 * -> * -> *
之间执行自然转换
所以我想 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$$Lambda16/700824958@6f3aa425
它甚至适用于返回函数是采用 2 个类型参数的方法的情况。
当我尝试定义一个使用一种类型参数的泛型方法进行转换的 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 中有红线,因为它没有意识到即使添加 "is a" MyDSL[Int, Int]。虽然 Scala 对此没问题。
类型参数对应用于自然变换的方法签名的所有可能性都是开放的,但在这种情况下它需要某种限制。我的猜测是因为 DSL case class 中没有值来限制类型参数,它归结为模式匹配,这已经超过了 Scala 解释方法签名的地方,因此它期望一个不同的输入 B 它会叫。
我当然可以通过 .asInstanceOf 肮脏的方式解决这个问题,但我的意思是来吧。
如果有任何不同的策略来实现这一点,我们将不胜感激。
这是该语言当前版本的类型推理系统中的一个已知限制,应该在未来的版本中取消。
在这种情况下,您可以在模式匹配中使用类型变量来解决此限制:
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
}
}
}
本质上:如果没有 "place" 编译器可以存放更多特定类型信息的地方,只需在模式中给它一个类型变量,以便它可以将推断的类型信息附加到它。