简体   繁体   English

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

[英]Keep deduced higher-kinded type in scala

I have a higher-order type and work to build some DSL with it.我有一个高阶类型,并致力于用它构建一些 DSL。 And I'm searching for a way to define function that can accept the type without explicit specifying this type.我正在寻找一种方法来定义可以接受类型而无需显式指定此类型的函数。

Self-describing example:自描述示例:

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()
}

I've tried 4 different methods and got 4 different errors:我尝试了 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

All errors are very descriptive.所有错误都非常具有描述性。 I have no objection towards scala's awkward type system and the limitations.我不反对 Scala 笨拙的类型系统和局限性。 But I'm still far from my object and I'm positive about searching for another hacks to implement desired functionality.但我离我的目标还很远,我很积极地寻找另一个黑客来实现所需的功能。

Is there any other methods that I've overlooked?有没有我忽略的其他方法?

See my answer here (and this other answer linked there) for a discussion of the type inference limitations that are causing you problems here.在此处查看我的答案(以及此处链接的其他答案),以讨论导致您在此处出现问题的类型推断限制。 Your best bet is probably the view bound approach:您最好的选择可能是视图绑定方法:

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
  }
}

Note that your <: has been replaced by <% .请注意,您的<:已被替换为<% This will work as expected.这将按预期工作。

@Travis-Brown suggested a solution that I was unable to incorporate in my code since I need to keep all typing information. @Travis-Brown 提出了一个解决方案,我无法将其合并到我的代码中,因为我需要保留所有输入信息。

The renewed example incorporate new requirements:更新的示例包含新的要求:

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
}

I need to replace <<= method with something working.我需要用一些有效的方法替换<<=方法。 View bounds could not supply me with enough type information for calling realWork.视图边界无法为我提供足够的类型信息来调用 realWork。

I've discovered that just doubling arguments works fine and sends to the compiler needed type information.我发现只需将参数加倍就可以正常工作并将所需的类型信息发送给编译器。

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

The usage is inconvenient but may be easily improved:使用不方便,但可以很容易地改进:

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)

There is one drawback in the implicit conversion: Tuple2 is a very popular type and casting any single to the tuple is dangerous.隐式转换有一个缺点:Tuple2 是一种非常流行的类型,将任何单个转换为元组是危险的。

So I introduced my own type for implicit conversion only, that doesn't pollute the entire type system.所以我只为隐式转换引入了我自己的类型,这不会污染整个类型系统。

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