繁体   English   中英

在 Scala 中保持推导的更高级类型

[英]Keep deduced higher-kinded type in scala

我有一个高阶类型,并致力于用它构建一些 DSL。 我正在寻找一种方法来定义可以接受类型而无需显式指定此类型的函数。

自描述示例:

class Wrap[T] (val data : T)

class DSL {
  def doSomething[T](x : Wrap[T]) =
    println(x.data)
  def <<=[T,W <: Wrap[T]](arg : W) : W = {
    doSomething(arg)
    arg
  }
  def <<-[T](arg : Wrap[T]) : Wrap[T] = {
    doSomething(arg)
    arg
  }
  def <<+[W <: Wrap[_]](arg : W) = {
    doSomething(arg)
    arg
  }
  def <<~(arg : Wrap[_]) = {
    doSomething(arg)
    arg
  }
}

class ExtendedInt(x : Int) extends Wrap[Int](x) {
  def expose() = println(data)
}

object Test {
  val dsl = new DSL
  val exi = new ExtendedInt(3)

  val x1 = dsl <<= exi
  val x2 = dsl <<- exi
  val x3 = dsl <<+ exi
  val x4 = dsl <<~ exi

  x1.expose()
  x2.expose()
  x3.expose()
  x4.expose()
}

我尝试了 4 种不同的方法,但出现了 4 种不同的错误:

Casting.scala:15: error: no type parameters for method doSomething: (x: Wrap[T])Unit exist so that it can be applied to arguments (W)
 --- because ---
argument expression's type is not compatible with formal parameter type;
 found   : W
 required: Wrap[?T]

    doSomething(arg)
    ^
Casting.scala:32: error: inferred type arguments [Nothing,ExtendedInt] do not conform to method <<='s type parameter bounds [T,W <: Wrap[T]]
  val x1 = dsl <<= exi
               ^
Casting.scala:38: error: value expose is not a member of Wrap[Int]
  x2.expose()
     ^
Casting.scala:40: error: value expose is not a member of Wrap[_$2]
  x4.expose()
     ^
four errors found

所有错误都非常具有描述性。 我不反对 Scala 笨拙的类型系统和局限性。 但我离我的目标还很远,我很积极地寻找另一个黑客来实现所需的功能。

有没有我忽略的其他方法?

在此处查看我的答案(以及此处链接的其他答案),以讨论导致您在此处出现问题的类型推断限制。 您最好的选择可能是视图绑定方法:

class Wrap[T](val data: T)

class DSL {
  def doSomething[T](x : Wrap[T]) { println(x.data) }

  def <<=[T, W <% Wrap[T]](arg : W): W = {
    doSomething(arg)
    arg
  }
}

请注意,您的<:已被替换为<% 这将按预期工作。

@Travis-Brown 提出了一个解决方案,我无法将其合并到我的代码中,因为我需要保留所有输入信息。

更新的示例包含新的要求:

class DSL {
  def doSomething[T](x : Wrap[T]) =
    println(x.data)
  /** Should really receive both types */
  def realWork[T, W <: Wrap[T]]( arg : W ) : W = {
    doSomething(arg)
    arg
  }

  /** syntax sugar for calling realWork*/
  def <<=[T, W <: Wrap[T]](arg : W) : W = realWork[T,W](arg) // This doesn't work
}

我需要用一些有效的方法替换<<=方法。 视图边界无法为我提供足够的类型信息来调用 realWork。

我发现只需将参数加倍就可以正常工作并将所需的类型信息发送给编译器。

  def <<=[T, W <: Wrap[T]](arg : W, trash : Wrap[T]) : W = realWork[T,W](arg)

使用不方便,但可以很容易地改进:

implicit def doubleArg[T](arg : T) : (T,T) = (arg,arg)
...
def <<=[T, W <: Wrap[T]]( args : (W, Wrap[T]) ) : W = realWork[T,W](args._1)

隐式转换有一个缺点:Tuple2 是一种非常流行的类型,将任何单个转换为元组是危险的。

所以我只为隐式转换引入了我自己的类型,这不会污染整个类型系统。

class SugarPair[+T1 <: T2, +T2] (val s1 : T1) {
  def apply() : T1 = s1
}
implicit def toSugarPair[T](sugar : T) : SugarPair[T,T] = new SugarPair[T,T](sugar)
...
def <<=[T, W <: Wrap[T]](arg : SugarPair[W,Wrap[T]]) : W = realWork[T,W](arg() )

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM