Scala 从 DSL 到函数的自然转换

标签 scala generics functional-programming pattern-matching type-inference

我正在尝试在各种 * -> * -> * 之间执行自然转换 所以我想采用 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:函数[Int,Int] = MyDSL$$anon$2$$Lambda$1816/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 中存在红线,因为它没有意识到即使 Add"is"MyDSL[Int, Int]。尽管 Scala 对此没问题。

类型参数对自然变换上 apply 的方法签名的所有可能性都是开放的,但在这种情况下,它需要某种限制。我的猜测是,由于 DSL case 类中没有值来限制类型参数,因此它归结为模式匹配,这已经过去了 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
    }
  }
}

本质上:如果编译器没有“地方”可以存放更具体的类型信息,只需在模式中为其提供一个类型变量,以便它可以将推断的类型信息附加到其中。

关于Scala 从 DSL 到函数的自然转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53054458/

相关文章:

c# - S : new() in c# 是什么意思

performance - Haskell中合理有效的纯功能矩阵产品?

ios - 使用开关控制流从函数推断返回类型

scala - 为什么我不能声明用 null 初始化的参数化类型的变量?

java - 强制正则表达式匹配可选组

android - Android XmlResourceParser 的方便使用?

javascript - 如何使用 RxJS 将我的数据映射为正确的格式

functional-programming - 函数式编程 - 使用折叠实现扫描(前缀和)

java - 使用 typesafe/akka config 合并多级配置

scala - 如何正确使用 scalac -Xlint