戦略 Scala 日記

素人プログラマの思考のセンス

IntelliJでScalaのシンタックスハイライト、サジェストがおかしいときに疑うこと

IntelliJ IDEAのシステムキャッシュを削除してみる

システムキャッシュの問題でシンタックスハイライトが正常に動作しない現象もまれに起こるため、まずはこれを疑ってみます。

メインメニューからFile | Invalidate Caches/Restartを選択し、表示されるタイアログのInvalidate Caches and Restartを選択します。

Cleaning System Cache

JDKのバージョンを疑ってみる

Project Structureで設定しているJDKと、JAVA_HOMEに指定しているJDKのバージョンが違う可能性があります。

例えば、明示的にJDK1.7を使いたい場合の設定は下記のようになります。

Project Structureに設定されているJDKの確認方法は、メインメニューからFile | Project Structureを選択し(もしくはCmd+;)、Project Settingを表示します。

ここではbrew caskJavaをインストールしている前提で解説します。

brew cask install java7でインストールした、JDK/Library/Java/JavaVirtualMachines/jdk1.7.0_72.jdk/にインストールされています。

これに合わせて、~/.bash_profileJAVA_HOMEを設定します。

$ echo 'export JAVA_HOME=`/usr/libexec/java_home -v 1.7.0_72`' >> .bash_profile

.bash_profileを読み直すと、java -versionでバージョンが変わっていることがわかります。

$ source ~/.bash_profile
$ java -version
java version "1.7.0_72"
Java(TM) SE Runtime Environment (build 1.7.0_72-b14)
Java HotSpot(TM) 64-Bit Server VM (build 24.72-b04, mixed mode)

Javaのバージョンの変更が終わったら、前述の手順のとおりに、IntelliJ IDEAのシステムキャッシュを削除し再起動すると、シンタックスハイライトが正常に動作しているはずです。

ScalaのGlobal Librariesは最新か

Project Structureに設定されているJDKの確認方法は、メインメニューからFile | Project Structureを選択し(もしくはCmd+;)、Platform SettingsのGlobal Librariesを表示します。

scala-sdk-2.11.6の用に最新のものが用意されていない場合は、+マークから追加をします。

.ivy/cacheを削除

SBTを利用している場合、~/.ivy2/cacheいかに、パッケージのキャッシュが展開されます。 これが原因となることは、今回のようなケースではあまり考えられませんが.ivy2/cache/org.scala-lang以下のキャッシュがなんらか問題に関わっているケースもあるかもしれません。

Scalaでシステムプロパティを読み込むときの定石

例えばpropnameという名前でJVMシステムプロパティを取得したい場合、指定したシステムプロパティが存在しない場合にはデフォルト値を返すというように、次のように記述することができる。

Option(System.getProperty("propname")).getOrElse("defaults")

System.getPropertynullを返すケースもあるので、Optionに包んでしまうと便利。

Scalaでモナドを、なんとなくイメージするためのヒント

Scalaモナドを説明する際には、まずリストを用いるのがイメージしやすい。

scala> List(2, 3, 5, 7, 11, 13, 17, 19).foreach(println)
2
3
5
...

このListの値に効果を与えて、与えられたものを関数(ここではprintln)でシーケンシャルに評価している。

次にScalaの代表的な型としてOptionがあるが、これもモナドである。 Optionは値をもつ、持たないそれぞれを、SomeNoneという箱で表す。

scala> Option(10)
res: Option[Int] = Some(10)

scala> Option(null)
res: Option[Null] = None

これに対して、リストと同じようにmapや、filterfoearchなどのメソッドを使うことができる。

scala> Option(10).foreach(println)
10

scala> Option(10).map(_ * 2).foreach(println)
20

また、例外処理が発生する場合に、このOptionをつくるための方法として、scala.util.Tryがある。 これによって、生成されるOptionに対して、リストと同様な処理をすることができる。

scala> import scala.util.Try
import scala.util.Try

// 例外が発生しないパターン

scala> Try( "10".toInt ).toOption
res: Option[Int] = Some(10)

scala> Try( "10".toInt ).toOption.foreach(println)
10

// 例外が発生するパターン

scala> Try( "fuga".toInt ).toOption
res: Option[Int] = None

scala> Try( "fuga".toInt ).toOption.foreach(println)

また、Optionと同様に、Futureもモナドとして扱うことができる。 Furute[Any]に対して、やはりリストと同様に、mapforeachなどを適用できる。 Futureの場合は、処理が行われた後に与えた関数を評価していく。

scala> import scala.concurrent._

scala> import ExecutionContext.Implicits.global

scala> val f: Future[Int] = future { (1 to 100).foldLeft(0)((x: Int, y: Int) => x + y) }
warning: there was one deprecation warning; re-run with -deprecation for details
f: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise@ca213f

scala> f.foreach(println)
5050

ライブラリや自分のコードの動きを確認したいときに"sbt console"が便利

自分のプロジェクトで使っているライブラリや、書いているコードをちょっとだけテストしたい場合などに、sbt consoleを利用する。 本稿では、sbt.version = 0.13.5を想定している。

今回は、Scalaの非同期HTTP通信用ライブラリのDispatchを例に、 このライブラリをsbt consoleから利用する方法をまとめた。

まずは、build.sbtの依存ライブラリにDispatchを追加しておく。

// build.sbt

name := "sbt-sample"

version := "1.0"

scalaVersion := "2.11.6"

libraryDependencies +=
  "net.databinder.dispatch" %% "dispatch-core" % "0.11.2"

build.sbtを用意したら、プロジェクトのディレクトリに移動して次のコマンドを実行。

$ sbt console

すると、ライブラリの依存解決と、src以下のコンパイルが始まり、終わるとScalaREPL(対話式実行環境)が立ち上がる。 これは通常のScalaREPL(対話式シェル)と同じように見えるが、build.sbtに記述しているライブラリ及び、プロジェクト内のパッケージを利用できる。

まずは、Dispatchのパッケージをインポートしてみる。

scala> import dispatch._, Defaults._
import dispatch._
import Defaults._

このように、インポートが実行される。 すると次のように、REPLでDispatchを利用したプログラムが実行できる。

scala> val svc = url("http://api.hostip.info/country.php")
svc: dispatch.Req = Req(<function1>,Properties(NoBody))

scala> val country = Http(svc OK as.String)
country: dispatch.Future[String] = scala.concurrent.impl.Promise$DefaultPromise@33304f78

scala> country.foreach(println)
XX

同じように、自身で定義したObjectや、Classも実行できるので、簡易な動作確認には役に立つ機能である。

Tuple3以上をもつListから、Tupleの値をキーにMapを作成する

たとえば、次のようなリストがあったとする。

val members = List(
  ("Momota", "red", 1994),
  ("Tamai", "yellow", 1995),
  ("Sasaki", "pink", 1996), 
  ("Ariyasu", "green", 1995), 
  ("Takagi", "purple", 1995)
)

これを、Map("Momota" -> ("red", 1994), "Tamai" -> ("yellow", 1995), ...)というMapの形にしたい。 このような場合は、Listに対してmapメソッドで処理をかけるのだが、パターンマッチを利用して次のように記述する方法がある。

val membersMap = members.map{ case (key, val0, val1) => key -> (val0, val1) }.toMap

このとき、list.map( case (a, b, c) => a -> (b ,c) )のように()を使うとエラーとなるので、{}を使うようにする。

ActorSystemはインポートして使う。遅延評価する。

まず、必要なActorSystemをobjectで作っておく。 この時、ActorSystemは遅延評価(lazy val)するとよい。

// MyActorSystems.scala

object MyActorSystems {
  lazy val someActorSystem = ActorSystem("some", config)
}

ActorSystemは読み込んで使うようにする。

import MyActorSystems.someActorSystem

val actor = someActorSystem.actorOf(Props[SomeActor])
actor ! SomeActorMessage(
  //  
)

Doubleにべき乗演算子をつくるには?finalクラスを拡張する方法

Scalaの暗黙の型変換については、以前にも扱った。

形式知にして使う、暗黙のimplicit - 戦略 Scala 日記

今回は、implicit classを使って、クラスを拡張する方法を考える。 また継承が禁止されているJavaのfinalクラスを拡張して、オリジナルなメソッドをつくる例を紹介する。

scalaDoubleでは階上計算(^)のメソッドが用意されていない。 そこで、^を独自に定義してみる。

次のように、implicit classでクラスを定義し、引数に拡張したいクラスをとることで、これは暗黙のクラスとして、暗黙の型変換同様にクラスを変換する。

implicit class HatDouble(d: Double)

ここで、階上計算をするためのメソッド^を定義する。 すると、0.3^3のように自然な計算ができるようになる。

implicit class HatDouble(d: Double)  {
  def ^(idx: Int) = List.fill(idx)(d).fold(1.0)((d1, d2) => d1 * d2)
}

println(0.3^3) // => 0.027