Skip to content

Scala とか Play の Future と ExecutionContext について

Scala とか Play の Future と ExecutionContext について published on Scala とか Play の Future と ExecutionContext について へのコメントはまだありません

背景

Scala や Play! framework で Future 使うときに、ExecutionContext を渡せと言われる。よく分からない場合は、とりあえず

import scala.concurrent.ExecutionContext.Implicits.global

とやっておけば、とりあえずコンパイルは通るんだけど、よく分からずにやっている人も多いと思う(ノ)。

今回は、そっから先の話。

Continue reading Scala とか Play の Future と ExecutionContext について

Scala の並列コレクションが直列でしか動かないと思ったら・・・

Scala の並列コレクションが直列でしか動かないと思ったら・・・ published on Scala の並列コレクションが直列でしか動かないと思ったら・・・ へのコメントはまだありません

追記

やっぱり、並列コレクションを使うのはやめて、Future 使って、Execution Context とかをちゃんと設定する方法にした。

が、一応、以下の内容はそのまま残しておく。

並列コレクションを使う動機

今、いろんな API からデータを取ってきて、それらを検索可能にするっていう(元々は自分用の)サービスをちょこちょこ作っている。(興味のある方は以下のリンクより使ってみて、是非フィードバックを下さいませ↓)

Track Down Anything on GitHub, Slack or Google Drive | Commet

いろんな API からデータを取ってくるので、直列で実行すると、どっか1箇所の API が不調だったりしてレスポンスが遅いと、それに引きずられてしまうので、並列化しようと思った。

コード

元々のコードは、大雑把にはこんな感じ。

SomeDatabase.findAllApiEndpointsToCall.foreach { apiCall =>
  apiCall.execute
}

これを並列化しようとして、以下のコードにした。

# par をつけた
SomeDatabase.findAllApiEndpointsToCall.par.foreach { apiCall =>
  apiCall.execute
}

ログをみると、

02:08:35.008 [ForkJoinPool-3-worker-5] INFO xxxxx

という感じで、別スレッドで動くようになったようだけど、スレッド名が常に同じ=1つのスレッドしか使われていないらしい。

以下のドキュメントを見ると、設定変更出来るらしいので、やってみた。

Parallel Collections – Configuring Parallel Collections – Scala Documentation

val apiCalls = SomeDatabase.findAllApiEndpointsToCall.par

# TaskSupport なるものを作成して、コレクションのプロパティにセット
val taskSupport = new ForkJoinTaskSupport(new scala.concurrent.forkjoin.ForkJoinPool(4)) # 4並列
apiCalls.tasksupport = taskSupport

apiCalls.foreach { apiCall =>
  apiCall.execute
}

これで、とりあえず並列で処理されるようになった。

Continue reading Scala の並列コレクションが直列でしか動かないと思ったら・・・

Java (Scala) で言語判定

Java (Scala) で言語判定 published on Java (Scala) で言語判定 へのコメントはまだありません

やりたい事

  • Java (Scala) で、ある文字列が何語(日本語、英語、など)なのかを判別する
  • 入力文字列は以下の2通り
    • ユーザーから入力された検索文字列(1単語、数文字〜数単語、数十文字)
    • 検索対象となる文章(数十単語〜数十ページ程度)
  • 対応する言語
    • 当初は日本語と英語
    • 今後は5言語程度

なぜこれをやりたいかは、以下のエントリーを参照。

Elasticsearch多言語化その2 – K blog

Continue reading Java (Scala) で言語判定

Play! frameworkのJsPath.readNullableの挙動

Play! frameworkのJsPath.readNullableの挙動 published on Play! frameworkのJsPath.readNullableの挙動 へのコメントはまだありません

はじめに

Play! framework の JSON ライブラリって、慣れればまぁ普通に使えるけど、ドキュメントがイマイチだし、やりたいことをどう書けばいいかが分からなくて時間を使うことが多い。

今回書くのは、JSONデータである要素が存在しない場合にそれをどう扱うか、という話題。

「なんだ readNullable 使うだけでしょ?」

と思う人もいるかもしれない。まぁ実際にはそうなんだけど、readNullable の挙動が若干分かりにくかったので、それについて。

やりたいこと

JSONデータで、ある要素が存在する場合と存在しない場合が考えられるとき、その要素が存在する場合は Some 、存在しない場合は None としてパースしたい。

具体的な例で説明すると、Facebook APIから以下のようなJSONが返ってくるとする。内容はあるFacebookグループへの書き込み。

Continue reading Play! frameworkのJsPath.readNullableの挙動

Comparing nullable columns in Squeryl

Comparing nullable columns in Squeryl published on Comparing nullable columns in Squeryl へのコメントはまだありません

How to compare nullable columns in Squeryl

Suppose we have a table definition like this:

case class T1 (
  id: Int,
  col1: Option[Int]
)
object FooDb extends Schema {
  val t1 = table[T1]("t1")
}

And, we want to execute a query like the following:

select * from t1 where col1 < 10

The correct statement in Squeryl is shown below:

from(FooDb.t1)( t1 =>
  where(t1.col1 lt Some(10))
  select(t1)
)

This isn’t very intuitive, and actually, it took me a lot of time to get to this answer.

Here are some that I tried and failed

The most intuitive one doesn’t compile:

  where(t1.col1 lt 10) // -> doesn't compile

This one causes NoSuchElementException:

  where(t1.col1.get lt 10) // -> NoSuchElementException

Another one that causes NoSuchElementException:

  where(t1.col1.map(_ lt 10).get) // -> NoSuchElementException

Official site should have example

I’ve found some guys that were having the same issue:

I think the Squeryl web site should have an example that uses nullable column in the where clause.

Scala や Play! framework で DI

Scala や Play! framework で DI published on Scala や Play! framework で DI へのコメントはまだありません

自分用メモ

 

elastic4s でエラー処理

elastic4s でエラー処理 published on elastic4s でエラー処理 へのコメントはまだありません

Scala から Elasticsearch を操作するクライアントライブラリ

Elasticsearch のサイトには、以下の4つが紹介されている

更新具合その他を考えて、elastic4sを使うことにした。

Elastic4s の使い方 → RTFM

基本的な使い方は以下を参照。

エラー処理

ドキュメントだと、エラーが起きた時にどう処理をすれば良いかが書いていない。

例えば、documentを追加するコードは

esClient.execute {
   index into s"index1/type1" fields ("field1" -> "val")
}

みたいに書くけど、エラーが起きたらどうするんだろうと思った。

上の式のシグニチャーをみると Future[IndexResponse] らしい。IndexResponse は Elasticsearch の API で提供されている。ドキュメントはこの辺

ドキュメントに追加できなかった場合は、isCreated メソッドが false を返す。それ以外に、サーバー側でエラーが起きたりとかした場合には例外が投げられる。

ということで、例えば以下のように処理できる。

import com.sksamuel.elastic4s.ElasticDsl._
import org.elasticsearch.action.index.IndexResponse
import org.elasticsearch.transport.RemoteTransportException

val f: Future[Boolean] = esClient.execute {
   index into s"index1/type1" fields ("field1" -> "val")
} map (_.isCreated) recover {
  case e: RemoteTransportException =>
    Logger.error(e.getMessage)
    false
}

この辺はニーズに応じて好きなようにすれば良いかなと。

その他

コードをみたけど、結構ちゃんと書かれていた(と個人的には思った)。

まとめ?

Elasticsearch 便利。elastic4s も便利。

Scalaの型引数とかサブ型とか

Scalaの型引数とかサブ型とか published on Scalaの型引数とかサブ型とか へのコメントはまだありません

コンパイルが通らない

以下のコードのコンパイルが通らないので、どうしたものかと質問した。

abstract class C
case class C1() extends C
case class C2() extends C

trait Service[T <: C] {
  def getFoo(): Option[T]
}

object Service {
  // found   : List[Option[Any]]
  // required: List[Option[T]]
  def getAllFoos(services: List[Service[_]]): List[Option[C]] = {
    services.map(_.getFoo())
  }
}

(答えだけ知りたい場合は、@gakuzzzz さんのコメントが簡潔かつ完全な内容なのでそちらを参照。)

エラーの原因

さて、エラーの原因は @gakuzzzz さんの解説にある通り。

ポイントは trait Service[T <: C] にした場合、 Service[C] と Service[C1] と Service[C2] が全然関係ない型になってしまうので、統一的に扱えないって所ですね。

(なぜ全然関係ない型になってしまうのだろう。調べないと。)

試行錯誤

@xuwei-k さんに教えてもらった方法

@xuwei-k さんに教えてもらった方法だとコンパイルは通った。

object Service {
  def getAllFoos[A <: C](services: List[Service[A]]): List[Option[C]] = {
    services.map(_.getFoo())
  }
}

ただ、元のgistで端折ってしまったんだけど、@gakuzzzz さんが書いたように、実際にはServiceは以下のように継承して使うことを前提としてる。

object C1Service extends Service[C1] { def getFoo(): Option[C1] = None }
object C2Service extends Service[C2] { def getFoo(): Option[C2] = None }

servicesにC1ServiceとC2Serviceが混ざったものを渡すとエラーになった。

scala> Service.getAllFoos(List(C1Service))
res4: List[Option[C]] = List(None)

scala> Service.getAllFoos(List(C2Service))
res5: List[Option[C]] = List(None)

scala> Service.getAllFoos(List(C1Service, C2Service))
<console>:17: error: no type parameters for method getAllFoos: (services: List[Service[A]])List[Option[C]] exist so that it can be applied to arguments (List[Service[_ >: C2 with C1 <: Product with Serializable with C]])
 --- because ---
argument expression's type is not compatible with formal parameter type;
 found   : List[Service[_ >: C2 with C1 <: Product with Serializable with C]]
 required: List[Service[?A]]
              Service.getAllFoos(List(C1Service, C2Service))
                      ^
<console>:17: error: type mismatch;
 found   : List[Service[_ >: C2 with C1 <: Product with Serializable with C]]
 required: List[Service[A]]
              Service.getAllFoos(List(C1Service, C2Service))

エラーの意味がよく分からないが、「Service[C] と Service[C1] と Service[C2] が全然関係ない型になってしま」ってるという事なのかな。

@gakuzzzzさんに書いてもらった方法1

こちら

abstract class C
case class C1() extends C
case class C2() extends C

trait Service[+T <: C] { // ここがポイント
  def getFoo(): Option[T]
}

object Service {
  def getAllFoos(services: List[Service[C]]): List[Option[C]] = {
    services.map(_.getFoo())
  }
}

object C1Service extends Service[C1] { def getFoo(): Option[C1] = None }
object C2Service extends Service[C2] { def getFoo(): Option[C2] = None }

Service.getAllFoos(List(C1Service, C2Service))

うまく行った。

これを trait Service[+T <: C] とすれば、Service[C1]Service[C2]Service[C] のサブ型と見做せるようになるので Service[C] として統一的に扱えるようになります。

とのこと。

@gakuzzzzさんに書いてもらった方法2

gistはこちら

abstract class C
case class C1() extends C
case class C2() extends C

trait Service {
  type T <: C
  def getFoo(): Option[T]
}

object Service {
  def getAllFoos(services: List[Service]): List[Option[C]] = {
    services.map(_.getFoo())
  }
}

object C1Service extends Service { type T = C1; def getFoo(): Option[C1] = None }
object C2Service extends Service { type T = C2; def getFoo(): Option[C2] = None }

Service.getAllFoos(List(C1Service, C2Service))

こちらに関しても先生のコメント。

型引数を止めて、抽象メンバ型でやれば、Service という型は一つになるので、こちらも Service で統一的に扱えるようになります。 type T = C1 が必要なのはこの場合ですね。

使い分け

これも先生のコメントそのまま書いとく。

共変を使うか、抽象メンバ型を使うか、の判断基準としては、「C1に特化したServiceの型を扱いたいかどうか」になるかと思います。

なるほど。

考えたこと

これだけだと、@xuwei-k さんと @gakuzzzz さんのコメントを切り貼りしただけなので、もう少し書いておく。

そもそもの動機

実際のコードだと、C1とかC2はユーザーの外部アカウントとの連携情報で、例えば以下の様なもの(簡略化してるけど)。どれとも連携していない人もいれば、全てと連携している人もいる。

  • Facebook連携の情報: UserFacebookInfo
  • Twitter連携の情報: UserTwitterInfo
  • GitHub連携の情報: UserGitHubInfo

それぞれが抽象クラスUserExternalAccountInfoを継承している。

で、Serviceに当たるのはいくつかあるけど、例えばDAOで、それぞれ違うテーブルにデータが入っているので(それぞれ user_facebook_info, user_twitter_info, user_github_info テーブル)、別々のDAOを用意した。以下の感じ。

trait ExternalAccountInfoDao[T <: UserExternalAccountInfo] {
  def getInfo(userId: Int): Option[T]
}

object ExternalAccountInfoDao {
  def getAllAccounts(userId: Int): List[Option[UserExternalAccountInfo]] = {
    val daos = List(UserFacebookInfoDao, UserTwitterInfoDao, UserGithubInfoDao)
    daos.map(_.getInfo(userId))
  }
}

object UserFacebookInfoDao
 extends ExternalAccountInfoDao[UserFacebookInfo] {
  def getInfo(userId: Int): Option[UserFacebookInfo] = { /* code */ }
}
// UserTwitterInfoDao, UserGithubInfoDao も同様

(多分もっと良い設計がありそうなので、コメント大歓迎です。)

その他

コメントで書いてもらったコードを見れば内容は分かるんだけど、自分であういうコードがパッと出てこないところが実力不足だと思った。

人のコードを沢山読んだり、定石(いわゆるデザインパターン)をもっと勉強しないといけないなと思った。

sbtでstacktraceを全部表示させる

sbtでstacktraceを全部表示させる published on sbtでstacktraceを全部表示させる への2件のコメント

自分用メモ。

Play! frameworkを使った開発で、エラー発生時にstacktraceが途中で途切れてしまう事がある、

ScalaTestの場合は、こちらにあるように以下のように指定する。

testOptions in Test += Tests.Argument("-oD")

sbt自身の話だが、デフォルトでは大抵のstack traceは隠されてしまう。

By default, sbt hides the stack trace of most exceptions thrown during execution. It prints a message that indicates how to display the exception. However, you may want to show more of stack traces by default.

表示するためには以下のようにする。詳しくはマニュアル参照。

> set every traceLevel := 0

最初からonにしててくれればいいのに。

追記:

xuwei_kさんからのコメントの通り、lastとやると、最後に実行したコマンドに関するログが出力される。マニュアルの一番上に書いてある。

When a command is run, more detailed logging output is sent to a file than to the screen (by default). This output can be recalled for the command just executed by running last.

Slick 2.0.xをPlay! framework2.2から使う

Slick 2.0.xをPlay! framework2.2から使う published on Slick 2.0.xをPlay! framework2.2から使う へのコメントはまだありません

はじめに

タイトルにPlayも含めてるけど、今回のエントリーは主にSlick 2.0.xの話。

そして、最初に一言言わせて欲しい。Slickのドキュメントは分かりにくい or 不十分。ただ、「お前が貢献しろよ」、という真っ当な指摘が来そうなので、これに関してはあまり深入りはしない。

まずは今回の目的を。

目的・ゴール

簡単に今回の目標を書いておく。

  • Play! framework 2.2.xで、Slickを使ってDBにアクセスする。
  • DBのスキーマからコードを自動生成する機能を使う。

後者に関してだけど、DBのテーブルは生のSQLを使うことが多いので、あると非常に便利。幸い、Slickにはコード自動生成の機能があるので、それを使う。

ScalaのDB library

本題に入る前に、今回のエントリーの背景を少し。

今実験的に動いているプロジェクトでは、前回のプロジェクトと同じくPlay! 2.2を使うことにした。慣れてるし。でも、せっかくだし何か新しいことも試さないと、ということで、DBライブラリーは今まで使ったこと無いSlickを使うことにした。

今まではSquerylScalikeJDBCを使った事があって、どちらにも大きな不満はそれほどなかった。実際、両方共いいライブラリだと思う。後から出てきたScalikeJDBCの方が設計は綺麗だと思うけど。

その他、Play!の場合Anormという選択肢もあるけど、知り合いの信頼の置ける開発者から「論外」みたいな話を聞いたことがあるし、ドキュメントを見る限り基本的というか低レベルなので、あまり開発効率向上にはつながらなさそう、ということで除外。

環境

さて、本エントリーの環境。

  • Play! framework 2.2.2
  • play-slick 0.6.0.1 (Play!でSlickを使うためのプラグイン)
  • Slick 2.0.0 (play-slickのバージョンによって使えるSlickのバージョンが異なる)

以下、本題。

Continue reading Slick 2.0.xをPlay! framework2.2から使う