ときどきみかけるコードなのですが、パターンマッチを使ったコードで、次のようなものがあります。
exp match { case A => { expA1 expA2 ... } case B => { expB1 exbB2 ... } }
Scalaでは、case節に複数の式を続けて書くことができるのでこのようなブレースは不要です。
exp match { case A => expA1 expA2 ... case B => expB1 expB2 ... }
としてしまいましょう。
ときどきみかけるコードなのですが、パターンマッチを使ったコードで、次のようなものがあります。
exp match { case A => { expA1 expA2 ... } case B => { expB1 exbB2 ... } }
Scalaでは、case節に複数の式を続けて書くことができるのでこのようなブレースは不要です。
exp match { case A => expA1 expA2 ... case B => expB1 expB2 ... }
としてしまいましょう。
皆さんご存知の通り、Scalaにはパターンマッチという機能があります。この機能、しばしば型に対するswitch-caseとして使われていることがあるようです。
たとえば、
sealed trait class Value case class Hoge(value: Int) extends Value case class Foo(value: String) extends Value case class Bar(value: List[String]) extends Value
というコードがあったとき、しばしば
value match { case hoge:Hoge => doHoge(hoge.value) case foo:Foo => doFoo(foo.value) case bar:Bar => doBar(bar.value) }
のような形で使われることがあります。このような使い方は場合によって必要なのですが、上記のようなコードでは全く不要です。以下のように機械的に書き換えてしまいましょう。
value match { case Hoge(value) => doHoge(value) case Foo(value) => doFoo(value) case Bar(value) => doBar(value) }
今回のようなケースではさほど差はあらわれませんが、より複雑なデータ構造をパターンマッチで分解するとき、型に対するswitch-caseを使うのとそうでないのとでは大きな差が出てきます。型に対するswitch-caseはあくまで必要悪です。使わずに済むならそれにこしたことはないということをよく覚えておいてください。
ではでは、また。
Scalaでnull
を使っていいのは小学生までだよねー、というのは冗談ですが、Scala文化圏ではOption
型です。しかし、Scalaでは困ったことにnull
を返すJavaのメソッドを呼び出さなければいけない場面がたくさんあります。
たとえば、以下のようなコードを書かなければいけない場面にはしばしば遭遇します。
val javaMap: java.util.Map[String, String] = javaObject.getMapping() val value: String = javaMap.get(key) // 型注釈はあえて付けている if(value != null) { ... } else { ... }
さて、ここで困るのが、javaMap.get(key)
はkey
に対応する値が入っていない場合null
を返すため、value
にはnull
が入る可能性があるという点です。こんなときは、Option.apply
の出番です。上のコードは次のように書き換えることができます。
val javaMap: java.util.Map[String, String] = javaObject.getMapping() val value: Option[String] = Option(javaMap.get(key)) // 型注釈はあえて付けている value match { case Some(value) ... case None => ... }
Scalaからnullを返すかもしれないJavaのメソッドを呼び出すときは、このようにOption.apply
で浄化することをこころがけましょう(もちろん、対象とするJavaのメソッドがnull
を返さないと確信できるケースではその限りではありません)。
それでは、また。
今日のScala勉強会第124回 in 本郷で話題に出てたことの備忘録。Scala 2.11で、scala.util.parsing.combinator
とscala.xml
、scala.util.continuations
が標準のライブラリから分離されたのだが、これを今まで通りに使うにはどうすればいいのか。実は、分離されたライブラリの内、前者二つは以下に
https://oss.sonatype.org/content/repositories/releases/org/scala-lang/modules/
continuations
は
https://oss.sonatype.org/content/repositories/releases/org/scala-lang/plugins/
に入っているので、これらを今まで通りに使うにはsbtの設定ファイルに
libraryDependencies ++= Seq( "org.scala-lang.modules" %% "scala-parser-combinators" % "1.0.1", "org.scala-lang.modules" %% "scala-xml" % "1.0.1", "org.scala-lang.plugins" %% "scala-continuations-library" % "1.0.1" )
という記述を追加してやればよい。
追記:コメントにある通り、継続(continuations)プラグインを使うには、
autoCompilerPlugins := true addCompilerPlugin("org.scala-lang.plugins" % "scala-continuations-plugin_2.11.0" % "1.0.1") scalacOptions += "-P:continuations:enable"
という記述が追加で必要になる。今までもcontinuation pluginを使うには必要な記述であったが、2.11.0ではcontinuation pluginの置き場所が異なっている点に注意。
唐突に始まったScala Tipsコーナー、初学者が陥りがちなScalaのコードパターンを例にして、より良い方法を解説していきます。第一回はOption
クラスのmap()
メソッドを使おうというものです。知ってる人には今更ですが、Option#map(f)
は、レシーバーの値がSome(v)
の場合は、f
にv
を渡した結果のSome(f(v))
が、None
の場合はNone
が返ります。したがって、パターンマッチを使った以下のようなコードは
val result = exp1 match { case Some(v1) => val vx = //expression using v1 Some(vx) case None => None }
常に以下のような形に書き換えることができます。
val result = exp1.map{v1 => val vx = //expression using v1 vx }
より一般的なコードでは、flatMap()
を使う事が有用な事がありますが、それはまた今度。
昨日、Scala 2.11.0がリリースされたということで、自分(たち)がメンテしているライブラリnscala-timeのScala 2.11.0対応版をSonatype Nexus Repository(これ、正式名称は何なのだろうか未だに悩んでいる)にアップロードしようとしていたのだが、
な感じで何故か301が返ってきてはまっていた。
原因はわかってみると簡単。どうも、いつのまにかSonatypeがhttps://で始まるURLしか受け付けないようになっていたのに、アップロード先のURLがhttp://のままなのであった。
修正は簡単で、
publishTo <<= version { v => val nexus = "http://oss.sonatype.org/" if (v.endsWith("-SNAPSHOT")) Some("snapshots" at nexus + "content/repositories/snapshots") else Some("releases" at nexus + "service/local/staging/deploy/maven2") }
となっていたのを
publishTo <<= version { v => val nexus = "https://oss.sonatype.org/" if (v.endsWith("-SNAPSHOT")) Some("snapshots" at nexus + "content/repositories/snapshots") else Some("releases" at nexus + "service/local/staging/deploy/maven2") }
にするだけ。
何を当たり前のことを、と言われそうだが、最近の静的型付き言語のIDEには、「名前変更」のリファクタリングを自動的に行ってくれる機能があり(リフレクションなどIDEが追跡可能な範囲を超える場合を除いて)、後から簡単に名前を変更することができる。このような機能は動的型付き言語用のIDEにもみられるものではあるが、IDEが追跡できる範囲がより限定的であるため、結局、IDEによる変更が正しいかどうかは目視に頼らざるを得ない。
静的型付き言語であっても、文字列を使ってリフレクションAPIを使用する場合など注意すべき点はあるのだが、その範囲が限定的であるため、名前の変更を安心して行うことができる。名前というのは後になってから変更したくなるものの一つであるため、これは静的型付き言語用の高度なIDE(Eclipse, NetBeans, IntelliJ IDEA等)の利点という事ができるだろう。
特にプロジェクトの初期に恥ずかしいtypoあるいは勘違いをしてしまい、その名前をあちこちで使った後に間違いに気づいた時にはとてもありがたい(動的型付き言語のIDEでこのテの、全体に広まってしまったtypoを直すのは結構大変である。たとえば、筆者はRubyMineというRuby on Rails用IDE( このIDE自体は非常によくできている)で、Ruby on Railsで作られたアプリケーションに対してこのような作業を行った事があるが、なかなか大変な作業であった…)。