使用类型成员减少Scala中的类型详细程度是否可行?

Owe*_*wen 13 polymorphism scala type-inference

所以,这听起来像是关于语言设计的一般问题,但我认为这里有一些具体的东西.具体来说,我感兴趣的是哪些技术挑战可以防止随之而来的kludgy代码普遍有用.

我们都知道"Scala的类型推断不如Haskell的那么好",并且有许多原因它不能很好,并且仍然可以完成Scala所做的所有事情.但是,在Scala编程足够长的时候,很明显的是,糟糕的类型推断并不是那么糟糕,而是指定一些常见类型所需的详细程度.那么,例如,在多态tail函数中,

def tail[A](ls: List[A]) =
    ls match {
        case Nil     => sys.error("Empty list")
        case x :: xs => xs
    }
Run Code Online (Sandbox Code Playgroud)

为了使方法有用,需要显式命名一个类型参数; 没办法解决它.tail(ls: List[Any])不会起作用,因为Scala无法弄清楚结果类型是否与输入类型相同,即使对于人来说这是"显而易见的".

因此,受到这种困难的启发,并且知道Scala有时会对类型成员比使用类型参数更聪明,我写了一个List使用类型成员的版本:

sealed trait TMList {
    self =>
    type Of
    def :::(x: Of) = new TMCons {
        type Of = self.Of
        val head = x
        val tail = (self: TMList { type Of = self.Of })
    }
}
abstract class TMNil extends TMList
def ATMNil[A] = new TMNil { type Of = A }
abstract class TMCons extends TMList {
    self =>
    val head: Of
    val tail: TMList { type Of = self.Of }
}
Run Code Online (Sandbox Code Playgroud)

好的,定义看起来很糟糕,但它至少是直截了当的,它允许我们tail按如下方式编写我们的方法:

def tail4(ls: TMList) =
    ls match {
        case _: TMNil => sys.error("Empty list")
        case c: TMCons with ls.type => c.tail
    }
Run Code Online (Sandbox Code Playgroud)

美丽的是,这是有效的,所以我们可以写(head按照你的期望定义)

val ls = 1 ::: 2 ::: ATMNil
val a = tail4(ls)
println(head4(a) * head4(a))
Run Code Online (Sandbox Code Playgroud)

并且Scala 知道输出类型成员仍然是Int.我们不得不写一些有趣的东西TMCons with ls.type,并且Scala抱怨这个匹配并不详尽,但是Scala为我们插入了一些代码,因为当然,当你匹配ls任何情况时都必须这样ls.type,并且当然这场比赛是详尽无遗的.

所以我的问题是:什么是捕获?为什么我们不这样做我们所有的多态类型,只修改语言,所以语法看起来不那么糟糕?我们会遇到哪些技术问题?

显然,有一个类在其类型成员中不能协变; 但我对此并不那么感兴趣; 我认为这是一个单独的问题.假设我们暂时不关心方差.还有什么会出错?

我怀疑这可能会引入类型推断的新问题(比如我必须ATMNil为示例定义工作方式)但我不太了解Scala的类型推断,以便知道它们会是什么.

编辑以回应0__:我想你可能已经找到了它.具有类型参数的版本有效,

def move2[A](a: TMList { type Of = A }, b: TMList { type Of = A }) = b match {
    case c: TMCons with b.type => c.head ::: a
    case _                     => a
}
Run Code Online (Sandbox Code Playgroud)

但有趣的是,如果没有明确的返回类型,curried dependently类型的版本不会:

def move3(a: TMList)(b: TMList { type Of = a.Of }) = b match {
    case c: TMCons with b.type => c.head ::: a
    case _                     => a
}
Run Code Online (Sandbox Code Playgroud)

Scala推断返回类型为TMList; 这两种类型案例的上限,TMList { type Of = a.Of }并且a.type.当然,TMList { type Of = a.Of }也是一个上限(和我想要的那个,这就是为什么添加一个显式的返回类型有效),而且,我认为,一个更具体的上限.我想知道为什么Scala不会推断出更具体的上限.

psp*_*psp 6

您将希望阅读类型细化做一切.


0__*_*0__ 2

尝试用 重写以下内容TMList

def move[A](a: List[A], b: List[A]): List[A] = b match {
   case head :: _ => head :: a
   case _ => a
}

move(List(1,2,3),List(4,5,6))
Run Code Online (Sandbox Code Playgroud)