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

OK、定義はひどいように見えますが、少なくとも単純明快であり、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は、出力型メンバーが still であることを認識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
}

しかし興味深いのは、明示的な戻り値の型がなければ、カリー化された依存型のバージョンはそうではないということです。

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 }、との 2 つのタイプのケースの上限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 に答える