본문 바로가기

Web Dev/Scala

Scala생략법(일본어번역안함)

このルールさえ押さえておけば、読んでいるコードが省略記法を使っていてもほぼ読めるようになります。

メソッド定義

def concatAsString(a: Int, b: Int): String = {
  val a_ = a.toString();
  val b_ = b.toString();
  return a_.+(b_);
}

セミコロンは省略できます。

def concatAsString(a: Int, b: Int): String = {
  val a_ = a.toString()
  val b_ = b.toString()
  return a_.+(b_)
}

引数を持たない且つ、定義時に () ありで定義したメソッドは、呼び出し時に () を省略できます。

def concatAsString(a: Int, b: Int): String = {
  val a_ = a.toString
  val b_ = b.toString
  return a_.+(b_)
}

なぜその様な特殊なルールになっているか、というと、理想としては

  • 副作用を伴わないメソッド(getterみたいなやつ)は括弧を付けない
  • 副作用を伴うメソッドは括弧をつける

という方向にしたいからです。

単純に定義時に () つけたメソッドは省略不可、定義時に () つけなかったメソッドは () の記述不可 としてしまうと、Java のライブラリを呼び出すときに整合性がつかなくなるので、 Javaとの互換を意識して一見すると妙なルールになっています。

そして、ブロックの最後の式の結果が、ブロックの結果となるので、return が省略できます。

def concatAsString(a: Int, b: Int): String = {
  val a_ = a.toString
  val b_ = b.toString
  a_.+(b_)
}

単一の引数をとるメソッドは、ドットと引数グループの括弧を省略できます。

def concatAsString(a: Int, b: Int): String = {
  val a_ = a.toString
  val b_ = b.toString
  a_ + b_
}

ドットを省略した場合、ドットを使った呼び出しよりも結合が弱くなります。

def concatAsString(a: Int, b: Int): String = {
  a.toString + b.toString
}

ブロックが1つの式しか持たない場合、ブロックにする必要がなくなります。

def concatAsString(a: Int, b: Int): String = a.toString + b.toString

再帰したメソッドなどを除いて、戻り値の型が自明の場合は、戻り値の型アノテーションを省略できます。

def concatAsString(a: Int, b: Int) = a.toString + b.toString

できますが、public なメソッドに関しては、省略をおすすめしません。

関数にまつわる省略

val f: Function1[Int, String] = new Function1[Int, String] {
  def apply(arg: Int): String = arg.toString
}
f.apply(10) // "10" が得られる

Function0 ~ Function22 の型は => を使って記述する事ができます。

val f: (Int) => String = new Function1[Int, String] {
  def apply(arg: Int): String = arg.toString
}
f.apply(10) // "10" が得られる

Function0 ~ Function22 のインスタンスは、 => を使って記述することができます。

val f: (Int) => String = (arg: Int) => arg.toString
f.apply(10) // "10" が得られる

引数の型が自明の時は、型アノテーションを省略する事ができます。

val f: (Int) => String = (arg) => arg.toString
f.apply(10) // "10" が得られる

引数が一つの場合(つまりFunction1の時)、引数グループの括弧を省略できます。

val f: Int => String = arg => arg.toString
f.apply(10) // "10" が得られる

全ての引数が、1回のみ使われる場合は、引数の宣言を省略し、_ で表現することができます。

val f: Int => String = _.toString
f.apply(10) // "10" が得られる

apply という名前のメソッドは省略することができます。

val f: Int => String = _.toString
f(10) // "10" が得られる

パターンマッチ無名関数

match 式と同じ書き方で、Function1 もしくは PartialFunction を定義することができます。

val pf: PartialFunction[Int, String] = {
  case 1 => "AAA"
  case 2 => "BBB"
}
pf(1)  // "AAA"
pf(3)  // MatchError が投げられる

PartialFunction は Function1 のサブトレイトで、とりうる引数の値のうち、一部の値のみ結果を返す関数を表します。

Seq(1, 2, 3) map {
  case 1 => "AAA"
  case 2 => "BBB"
  case _ => "ZZZ"
}

Function1 などを要求するメソッドの引数で、直接パターンマッチが使えるので非常に便利です。

メソッドから関数の生成

メソッドから FunctionN のインスタンスを生成できます。

def add(a: Int, b: Int): Int = a + b

というメソッドがあった時に、後ろに _ を付けると Function2 のインスタンスがとれます

val f = add _    // f: (Int, Int) => Int = <function2> と評価される

明確に FunctionN が要求されている事が判明している場合は、 _ を省略できます。

val f: (Int, Int) => Int = add

大抵の場合は FunctionN を引数にとるメソッドに渡す感じになるので _ はあまり使わないです。 じゃあなんでそんなルールになっているかというと、

println(add)    // 引数を与え忘れ

とかした時に (Int, Int) => Int = <function2> とか出力されても何も嬉しくないので、 明示的にFunctionN を要求していない時は変換せずにコンパイルエラーにしてくれるように そういったルールになっています。

'Web Dev > Scala' 카테고리의 다른 글

ScalaのPerson  (0) 2015.06.24
ScalaのTest2  (0) 2015.06.24
Scala 24일 과제 3번까지  (0) 2015.06.24