20

SML、Erlangなどの言語では、次のような関数を定義できます。

fun reverse [] = []
|   reverse x :: xs  = reverse xs @ [x];

私はこのようにScalaでアナログを書くことができることを知っています(そして私は知っています、以下のコードには多くの欠陥があります):

def reverse[T](lst: List[T]): List[T] = lst match {
  case Nil     => Nil
  case x :: xs => reverse(xs) ++ List(x)
}

しかし、Scalaで前者のコードを、おそらく後者を脱糖して書くことができるのではないかと思います。

将来実装されるそのような構文に基本的な制限はありますか(つまり、本当に基本的なものです-たとえば、型推論がscalaで機能する方法、または明らかにパーサーを除く他の何か)?

UPD
これはどのように見えるかのスニペットです:

type T
def reverse(Nil: List[T]) = Nil
def reverse(x :: xs: List[T]): List[T] = reverse(xs) ++ List(x)
4

4 に答える 4

14

それは本当にあなたがファンダメンタルによって何を意味するかに依存します。

あなたが本当に「この機能を実装することを妨げる技術的なショートッパーがあるかどうか」を尋ねているなら、私は答えがノーであると言うでしょう。あなたは脱糖について話している、そしてあなたはここで正しい軌道に乗っている。基本的に、いくつかの個別のケースを1つの関数につなぎ合わせるだけで、これは単なる前処理ステップとして実行できます(これには構文の知識のみが必要であり、意味の知識は必要ありません)。しかし、これが意味をなすために、私はいくつかのルールを定義します:

  • 関数の署名は必須です(例としてHaskellでは、これはオプションですが、関数を一度に定義する場合でも、複数の部分で定義する場合でも、常にオプションです)。署名なしで生活するように手配し、さまざまな部分からそれを抽出しようとすることもできますが、型情報の不足はすぐに私たちを悩ませます。より単純な議論は、暗黙の署名を推測しようとする場合、すべてのメソッドに対してそれを実行したほうがよいということです。しかし、真実は、scalaに明示的なSingatureを含めるのには非常に正当な理由があり、それを変更することは想像できません。
  • すべてのパーツは同じスコープ内で定義する必要があります。まず、各ソースファイルは個別にコンパイルされるため、同じファイルで宣言する必要があります。したがって、この機能を実装するには、単純なプリプロセッサでは不十分です。第二に、最終的には1つのメソッドになってしまうため、すべてのパーツを同じスコープに含めるのは当然のことです。
  • このようなメソッドではオーバーロードはできません(そうでない場合は、プリプロセッサがどの部分がどのオーバーロードに属しているかを認識できるように、各部分の署名を繰り返す必要があります)
  • パーツは、宣言された順序で生成されたものに追加(ステッチ)されますmatch

それで、これはそれがどのように見えるかです:

def reverse[T](lst: List[T]): List[T] // Exactly like an abstract def (provides the signature)
// .... some unrelated code here...
def reverse(Nil) = Nil
// .... another bit of unrelated code here...
def reverse(x :: xs ) = reverse(xs) ++ List(x)

これは簡単に次のように変換できます。

def reverse[T](list: List[T]): List[T] = lst match {
  case Nil     => Nil
  case x :: xs => reverse(xs) ++ List(x)
}
// .... some unrelated code here...
// .... another bit of unrelated code here...

上記の変換は非常に機械的であり、ソースAST(この新しい構成を受け入れるわずかに変更された文法によって生成されたAST)を操作し、それをターゲットAST(によって生成されたAST)に変換するだけで実行できることは簡単にわかります。標準のscala文法)。その後、通常どおり結果をコンパイルできます。

これで、いくつかの簡単なルールを使用して、この新機能を実装するためのすべての作業を実行するプリプロセッサを実装できます。


基本的に、「この機能を場違いにするものはありますか」と質問している場合、これはあまりスカラとは感じられないと主張することができます。しかし、もっと重要なことは、それはテーブルにそれほど多くをもたらさないということです。Scalaの作者は、実際には言語を単純化する傾向があり(組み込み機能が少ない場合や、組み込み機能の一部をライブラリに移動しようとする場合など)、実際には読みにくい新しい構文を追加することは、単純化の目標に反します。

于 2013-02-11T10:48:31.270 に答える
5

SMLでは、コードスニペットは文字通り単なる構文糖衣構文(言語仕様の用語では「派生形式」)です。

val rec reverse = fn x =>
    case x of [] => []
            | x::xs  = reverse xs @ [x]

これは、表示するScalaコードに非常に近いものです。したがって、Scalaが同じ種類の構文を提供できなかったという「根本的な」理由はありません。主な問題は、Scalaがより多くの型アノテーションを必要としていることです。これにより、この省略構文は一般的にはるかに魅力的でなくなり、おそらくしばらくの間価値がありません。

また、1つのケースバイケースの関数定義を2つのオーバーロードされた関数と構文的に区別する方法がないため、提案する特定の構文はうまく機能しないことにも注意してください。""を使用するSMLと同様に、おそらくいくつかの代替構文が必要になります|

于 2013-02-11T10:57:02.443 に答える
3

少なくとも2つの問題があります。

  1. [および]は型引数に使用されるため、予約文字です。コンパイラーはそれらの周りにスペースを許可するので、それはオプションではありません。
  2. もう1つの問題は、を=返すことUnitです。したがって、後の式は|結果を返しません

私が思いつくことができる最も近いものはこれです(あなたの例に非常に特化していることに注意してください):

// Define a class to hold the values left and right of the | sign
class |[T, S](val left: T, val right: PartialFunction[T, T])

// Create a class that contains the | operator
class OrAssoc[T](left: T) {
  def |(right: PartialFunction[T, T]): T | T = new |(left, right)
}

// Add the | to any potential target
implicit def anyToOrAssoc[S](left: S): OrAssoc[S] = new OrAssoc(left)

object fun {

  // Use the magic of the update method
  def update[T, S](choice: T | S): T => T = { arg =>
    if (choice.right.isDefinedAt(arg)) choice.right(arg)
    else choice.left
  }
}

// Use the above construction to define a new method
val reverse: List[Int] => List[Int] =
  fun() = List.empty[Int] | {
    case x :: xs => reverse(xs) ++ List(x)
  }

// Call the method
reverse(List(3, 2, 1))
于 2013-02-11T10:32:07.690 に答える
3

SMLやErlangは知りませんが、Haskellは知っています。これは、メソッドのオーバーロードのない言語です。このようなパターンマッチングと組み合わせたメソッドのオーバーロードは、あいまいさをもたらす可能性があります。次のコードを想像してみてください。

def f(x: String) = "String "+x
def f(x: List[_]) = "List "+x

それはどういう意味ですか?これは、メソッドのオーバーロードを意味する場合があります。つまり、メソッドはコンパイル時に決定されます。また、パターンマッチングを意味する場合もあります。マッチングを行うaf(x:AnyRef)メソッドがあります。

Scalaには名前付きパラメーターもありますが、これもおそらく壊れているでしょう。

Scalaは、あなたが一般的に示しているよりも単純な構文を提供できるとは思いません。より単純な構文は、一部の特殊な場合にのみIMHOが機能する場合があります。

于 2013-02-11T10:28:52.277 に答える