13

所以,这听起来像是一个关于语言设计的普遍问题,但我认为这里有一些具体的东西。具体来说,我感兴趣的是哪些技术挑战会阻止随后的笨拙代码普遍有用。

我们都知道“Scala 的类型推断不如 Haskell 的好”,并且它不能那么好有很多原因,并且仍然可以做 Scala 所做的所有事情。但是,在对 Scala 进行足够长的编程之后,很明显的一点是,糟糕的类型推断并没有那么糟糕,就像指定一些常见类型所需的冗长一样。因此,例如,在多态tail函数中,

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

为了使方法有用,需要显式命名类型参数;没有办法解决它。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 }
}

好的,这个定义看起来很糟糕,但它至少简单明了,它允许我们编写tail如下方法:

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

美妙的是,这行得通,所以我们可以写(head定义如你所料)

val ls = 1 ::: 2 ::: ATMNil
val a = tail4(ls)
println(head4(a) * head4(a))

Scala知道输出类型成员仍然是Int. 我们不得不用 写一些有趣的东西TMCons with ls.type,而 Scala 抱怨匹配并不详尽,但那段代码可能只是由 Scala 为我们插入的,因为当然当你匹配ls任何情况时都必须是 of 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
}

但有趣的是,如果没有明确的返回类型,咖喱依赖类型的版本不会:

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

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

4

2 回答 2

6

你会想要阅读类型细化做所有事情

于 2012-07-08T05:10:09.527 回答
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))
于 2012-07-08T11:11:44.953 回答