来世から頑張る!!

技術ブログを目指して

ScalaでゆるふあにDB接続


この記事はScala Advent Calendar 2015 12日目です。

www.adventar.org

昨日は同じく私による今日から始めるScalaプログラミングでした。

前置き

私はアプリケーションにSQLを自分で書きたい派です。
参考: ScalaでSQLが書きたいんだ!!

なので積極的に自分で書けるライブラリーが好きです。

  1. DBに直接繋いで、取得結果や処理速度などを調整するクエリーを書く。
  2. 出来上がったクエリーを作成するためのScalaコードを書く。

こんな風に作業したいのです。

doobie or not DB

ということでdoobieです。

doobieによるシンプルなDBアクセスコードは以下のような感じです。

import doobie.imports._

case class User(id: String, password: String, email: Option[String])

object User {
  def byId(id: String): ConnectionIO[Option[User]] =
    sql"SELECT id, password, email FROM users WHERE id = ${id}".query[User].option
}

呼び出す側はこちら

import doobie.imports._
import scalaz.effect.IO

val xa = DriverManagerTransactor[IO](
  "com.mysql.jdbc.Driver", "jdbc:mysql://example.com:3306/example", "user", "password"
)
val a: ConnectionIO[Option[User]] = User.byId("kazzna")
val b: IO[Option[User]] = a.transact(xa)
val c: Option[User] = b.unsafePerformIO

これの素晴らしいところは、接続情報がxaにまとめられていて、それを明示的に渡すということですね。

ちなみにDriverManagerTransactorの型引数はscalaz.Catchableを要求してきたりするので、 IO以外ではscalaz.concurrent.Taskぐらいしか使える型を知りません。
(きっとガチ勢がコメントしてくれるはず・・・)

テストを書いてみる

最初に書いた通り、SQLが正しいことは確認してからScalaコードを書きたいので、 ユニットテストでDBになんて繋ぎたくないんですね。

ユニットテストで確認するのは「使用したいSQLが出力されているか」までで、 外部との接続はもうちょっと結合度の高いテストでやりたいわけです。

ですが世間一般のライブラリーはいつの間にかDB接続が暗黙的に渡されていたりして、 テストしようと思うと色々なテクニック(モックとか)を使わないと難しかったりします。

ところがこのdoobieはゆるふあ仕様で、それが簡単にできるのです!!!

val a = User.byId("kazzna") // ConnectionIO[Option[User]]
val b = a.transK[IO] // Kleisli[IO, java.sql.Connection, Option[User]]

Kleisli[M[_], A, B]とはゆるふあに言えばA => M[B]です。
なのでこの場合はb: java.sql.Connection => IO[Option[User]]ですね。

つまりjava.sql.Connectionを渡せばテストできるわけです。
このinterfaceを実装しましょう。

import java.sql._

import org.scalatest._

class QueryChecker(sql: String, params: Map[String, Any], result: ResultSet) extends Connection with Matches {
  override def prepareStatement(s: String): PreparedStatement = new PreparedStatement {
    s should ===(sql)
    override def setString(i: Int, s: String): Unit = {
      params(i) should ===(s)
    }
    override def executeQuery(): ResultSet = result
    // override def ... = ??? を必要なだけ書く
  }
  // override def ... = ??? を必要なだけ書く
}

class DummyResultSet(v: List[Map[Int, Any]]) extends ResultSet {
  var rowIndex = -1
  override def next(): Boolean = {
    rowIndex = rowIndex + 1
    rowIndex < v.length
  }
  override def getString(i: Int): String = v(rowIndex)(i).asInstanceOf[String]
  // override def ... = ??? を必要なだけ書く
}

気を付けるべきことは、import java.sql._java.sql.Arrayがimportされるため、 scala.Array[A]Arrayと書けないことです。
scala.Arrayと全部ちゃんと書く必要があります。もしくは別名import。

あと、closeとかもoverrideしないとランタイムエラーが出ると思いますので、 エラーが出たところから修正していきましょう。

なお、このあたりのコードはJava用なのでmutableに書いたほうが楽だと思われます。

import org.scalatest._

class UserSpec extends FlatSpec with Matches {
  "Sql" should "be checked by test Connection" in {
    val id = "kazzna"
    val query = "SELECT id, password, email FROM users WHERE id = ?"
    val map = Map(1 -> id, 2 -> "pass", 3 -> "email@example.jp")
    val expected = Some(User(map(1), map(2), Some(map(3))))
    val a = User.byId(id).transK[IO]
    val b = new QueryChecker(query, Map(1 -> id), new DummyResultSet(Seq(map)))
    val actual = a(b).unsafePerformIO
    actual should ===(expected)
  }
}

これでDB接続情報を一度も書かずにユニットテストができました!!簡単!!

例によって例の如くQueryにはCypherを使用していますが、 一応動くサンプルをgithubにあげておきました。

github.com

最後に: 教えて怖い人!

ConnectionIOOptionT[ConnectionIO, A]EitherT[ConnectionIO, String, A]なんかにして、 2つ3つ組み合わせて途中で失敗したら実行をやめるようなqueryを書くときに型引数つらいのですが、 なんとかなる方法はありませんか?

コメントお待ちしております(>﹏﹏<);;

以上でScala Advent Calendar 2015 12日目は終了です。
お付き合いいただきありがとうございます。

明日はOE_uiaさんによる「Scala AndroidかDeepLearning的な何か」です。
レベルが高そうですが、楽しみです。

今日から始めるScalaプログラミング!!


この記事はScala Advent Calendar 11日目です。

www.adventar.org

昨日はdakatsukaさんによるScalaでLuaスクリプトを動かして遊ぶでした。
Luaでしたね!!
ゲーム業界すごいですね!

さて、Scala Advent Calendarですが、まだまだ空きがあります。
難しいことを書かなきゃならないという空気を吹き飛ばすため、 Scalaをやったことのない人向けの記事を書くことによってハードルを下げます。

窓口が広いほど学びも増えるはずです!!

ということで本日はScalaを始めるための環境づくりです。

インストールせずに試す!!

オンラインならIDEONE

ideone.com

左下のChoose LanguageでothersのところにScalaがあります。
ライブラリーを使わないような入門書のコードなどは、ここで入力して試せますね。
もちろんLuaもあるのでLet's challenge!!

ローカルにScalaコーディング環境を構築する!!

ここからの説明は一番初心者向けということで、 Windows 10ユーザーを想定して進めます。

その他のOSの方はbrewなりportageなり環境にあったものをご使用ください。

Javaのインストール

ScalaJava VM上で動く言語です。
そのためまずはJavaのインストールが必要です。

JavaにはJavaで作られたプログラムを動かすためのJRE(Java Runtime Edition)と、 Javaでプログラムを作るためのJDK(Java Development Kit)があります。

今回はプログラミングを行うのでJDKですね。
JDKにはJREが含まれていますので、作ることも動かすことも可能です。

では、「JDK ダウンロード」などで検索してJavaダウンロードページにたどり着いてください。
プログラミングではGoogle力が試されることがよくありますので、 できる限り自力でたどり着くことが大切です。

f:id:kazzna:20151210221302p:plain

今回ダウンロードするのはJava SE(Standard Edition)のJDKです。 いろいろなJavaが出てきてもう挫折しそうですよね。
細かい名前は覚えなくてもあまりScalaには関係ありません。

さて、ダウンロードページにたどり着くと、さらに多数の選択肢が出て来ます。

f:id:kazzna:20151210222043p:plain

今回は最新版(数字が最も大きいもの)を導入します。 Java SE Development Kit 8u66ですね。
直接Java言語でプログラミングする予定はないので、DemosやSamplesはいらないでしょう。

ライセンスに同意しろと書かれているので、きちんとライセンスを読んだ上で、 同意できるならAccept License Agreementを選択します。

その下に並んでいるOSの一覧から、自分のOSを選択します。
WindowsにはWindows X86Windows x64がありますが、 自分のOSにあった方を選択してください。


x86x64かの確認方法

  1. Windowsのタスクバーの左端「Windowsマーク」を右クリックし、「システム(Y)」を選択します。
  2. 「システム」が開くので、真ん中ぐらいにある「システムの種類」を確認します。
  3. ここが「32ビット...」ならx86、「64ビット...」ならx64です。
  4. その他だった場合は自分でググってください。初心者用ではありません。

Edgeだと、ダウンロードが完了したらそのまま実行が選べます。

f:id:kazzna:20151211002247p:plain

インストールは何も考えずすべて「次へ」で大丈夫です。
インストールが終わったら、環境変数Pathにコマンドを追加します。

環境変数の編集はRapid Environment Editorというソフトが使いやすいらしいです。
使ったことはありませんが。

ここでは通常のWindowsの機能でPathを追加する方法を記述しておきます。
既にユーザー環境変数Pathが存在する場合は、この記事の下の方にある「パスに追加」を見てください。


パスを設定

  1. Windowsのタスクバーの左端「Windowsマーク」を右クリックし、「システム(Y)」を選択します。
  2. 「システム」が開くので、左端にある「システムの詳細設定」を選択します。
  3. 「システムのプロパティ」が開くので、詳細設定タブを選択します。
  4. 一番下の「環境変数」をクリックします。
  5. 上段の「ユーザー環境変数」の「新規」をクリックします。
  6. 変数名に「Path」、変数値に先ほどインストールしたJDKのパスを設定します。
    (インストール時にすべて「次へ」を選んでいれば「C:\Program Files\Java\jdk1.8.0_66\bin」です。)
  7. 順番にOKを押してウィンドウを閉じます。

インストールが完了したら、コマンドプロンプトを起動してインストールが成功したことを確認しましょう。
後からやると、どこで失敗したのかわからなくてリカバリーが大変です。

スタートメニューからコマンドプロンプトを起動します。
(同じくマークを右クリック -> コマンドプロンプト(C))
すると、下記のような表示の黒い画面が表示されるはずです。

C:\Users\kazzna>

kazznaの部分はログインしているユーザーのユーザー名になっているはずです。
この部分は勝手に出てくるので、>より後ろを入力してエンターを押すと入力した内容が実行されます。

さて、インストール成功かどうかの確認です。
こういう場合、普通はversionオプションを使用します。
javaではなくjavacなことに注意してください。

C:\Users\kazzna> javac -version
javac 1.8.0_66

version番号が表示されれば成功です。

versionオプションは「バージョン番号を表示して終了する」という動作をするプログラムがほとんどなので、 インストールされているかどうかを悪影響なく確認できます。

自分がプログラムを作るときもそういう風にするといいと思います。

javac -versionを実行してもバージョンが表示されずエラーメッセージが出たら?
Javaのインストールの初めからやり直すしかありません。

Activatorのインストール

Scalaでよく使われるツールsbtがあります。
sbtを使うと、ライブラリーのインストールやコンパイル・テストなどが簡単にできます。

今回はsbtにいろいろな機能を追加したActivatorというツールを使用します。
Activatorは内部にsbtを使用している(らしい)ので、 sbtでできることはすべてActivatorでできるはずです。

まずは先程と同様に「sbt activator ダウンロード」等で検索して、 Activatorのダウンロードページにたどり着きましょう。

f:id:kazzna:20151210230052p:plain

ここでDOWNLOADを選べばWindowsではzipファイルがダウンロードされるはずです。
この記事を書いていて初めて知りましたが、sbtの500倍ぐらいのサイズですね。
何が入ってるんだろう???

これをどこか好きな場所に展開してください。
「どこか」と言われても初心者は困るでしょうから、 決められない人はコマンドプロンプト>より左に書かれているフォルダに展開してください。

展開したら、activator-dist-1.3.7というフォルダができるはずです。
後ろの番号はバージョン番号なので、もしかしたら新しいバージョンになっているかもしれませんが。
フォルダの中にactivator.batが入っていることを確認してください。

このフォルダのパスを環境変数に追加します。


パスに追加

  1. Windowsのタスクバーの左端「Windowsマーク」を右クリックし、「システム(Y)」を選択します。
  2. 「システム」が開くので、左端にある「システムの詳細設定」を選択します。
  3. 「システムのプロパティ」が開くので、詳細設定タブを選択します。
  4. 一番下の「環境変数」をクリックします。
  5. 上段の「ユーザー環境変数」にある「Path」をクリックして選択します。
  6. 「編集」を押すと新規作成と同じようなウィンドウが開きます。
  7. 既に入っている変数値の末尾にセミコロン「;」を追加した後、activator.batがあるフォルダのフルパスを追加します。
    (ユーザー名に注意して「C:\Users\kazzna\activator-dist-1.3.7」みたいなのを入れてください。)
  8. 順番にOKを押してウィンドウを閉じます。

コマンドプロンプトを一度閉じて、もう一度開きなおしてください。
そして、以下のコマンドを実行し、Activatorがインストールできたことを確認します。

C:\Users\kazzna> activator --version
~~ もしかしたら何行かのメッセージ ~~
sbt launcher version 0.13.8

バージョンが表示されれば成功です。

Activatorを使ってみよう!!

早速Activatorを使用してみましょう。

まずは作業用フォルダを作成します。

C:\Users\kazzna> mkdir activatorwork

C:\Users\kazzna> cd activatorwork

C:\Users\kazzna\activatorwork>

最初のコマンドmkdir(make directory)でactivatorworkというフォルダをC:\Users\kazznaに作成しました。
次のコマンドcd(change directory)で、直下に作成したactivatorworkフォルダに移動しています。
3行目で左側のフォルダ名が変わっていますね。ここが現在いるフォルダです。

では、まずactivator new フォルダ名で新しいプロジェクト用フォルダを作ってみましょう。 ここではフォルダ名はactivatortestとします。

C:\Users\kazzna\activatorwork> activator new activatortest
~~ 何行かのメッセージ ~~
Browse the list of templates: http://typesafe.com/activator/templates
Choose from these featured templates or enter a template name:
  1) minimal-akka-java-seed
  2) minimal-akka-scala-seed
  3) minimal-java
  4) minimal-scala
  5) play-java
  6) play-scala
(hit tab to see a list of all templates)
>

activator newを実行すると、どのテンプレートを使用するか確認されます。
テンプレート一覧をダウンロードするため、少し時間がかかるかもしれません。

tabを押すと全てのテンプレートの一覧が表示され、名前か番号を入力すると次に進みます。
今回は4と入力してminimal-scalaを使用しましょう。

> 4
OK, application "activatortest" is being created using the "minimal-scala" template.

To run "activatortest" from the command line, "cd activatortest" then:
/home/kazzna/tmp/activatortest/activator run

To run the test for "activatortest" from the command line, "cd activatortest" then:
/home/kazzna/tmp/activatortest/activator test

To run the Activator UI for "activatortest" from the command line, "cd activatortest" then:
/home/kazzna/tmp/activatortest/activator ui

C:\Users\kazzna\activatorwork>

これでactivatorworkフォルダにactivatortestフォルダーが作成されました。
cdコマンドで移動してください。

C:\Users\kazzna\activatorwork> cd activatortest

C:\Users\kazzna\activatorwork\activatortest>

ここからはコマンドプロンプトはこのまま開いたままで、エクスプローラーで作業しましょう。
コマンドプロンプトが得意な方はこのまま作業していただいても構いません。

エクスプローラーを開いたら、アドレスバーにコマンドプロンプト>より左の部分を貼り付けてください。

f:id:kazzna:20151210233823p:plain

ちなみに、コマンドプロンプトからのコピーは

  1. 右クリック -> 範囲指定
  2. 左ドラッグで文字を反転
  3. キーボードでエンターキーをたーんっ!

です。ご注意ください。

f:id:kazzna:20151211003948p:plain

フォルダを開くと、ここにもactivator.batなどがあるのがわかりますね。
Activatorをインストールしていない人でもプロジェクト途中から参加できるように、なのでしょうか?

ファイルがおっきいので、ここのactivatorから始まるやつは全部消してOKだと思います。

さて、この中身の説明です。

  • build.sbt
    sbtの設定ファイルです。ここにいろいろ書くことでライブラリーやコンパイルオプションなどを変更できます。
  • project
    このフォルダの中にsbt自身の設定やsbt用のプラグインの設定を書きます。
    今回の記事では触りません。
  • src
    この中に自分で作るプログラムのソースコードを置きます。
    Activatorコンパイルしてくれます。 srcの中には以下のようなフォルダがあります。
    • main
      プログラム本体のコードを格納します。
      この中にはデフォルトで次の3種類のフォルダを配置できます。(プラグインで拡張可能)
    • test
      プログラムをテストするためのコードを格納します。
      こちらもmainと同じフォルダが作成可能です。

srcmainにあるHelloworld.scalaをメモ帳などで開けばもうコーディングが可能ですね。

さて、Activatorを使ってコンパイルしてみましょう。
コマンドプロンプトに戻ります。

C:\Users\kazzna\activatorwork\activatortest> activator compile
~~ 何行か表示 ~~
[success] Total time: 124 s, completed 2015/12/11 1:01:27

初回の起動のみ、いろいろなファイルをダウンロードしてくるので時間がかかると思われます。

次は実行です。

C:\Users\kazzna\activatorwork\activatortest> activator run
~~ 何行か表示 ~~
[info] Running com.example.Hello
Hello, world!
[success] Total time: 0 s, completed 2015/12/11 1:02:18

同様にテストを実行。

C:\Users\kazzna\activatorwork\activatortest> activator test
~~ 何行か表示 ~~
[info] HelloSpec:
[info] Hello
[info] - should have tests
[info] Run completed in 375 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[success] Total time: 6 s, completed 2015/12/11 1:02:54

ライブラリーを使う

ライブラリーを使いたいときはbuild.sbtを編集します。 たとえば、MySQLへ接続するためのmysql-connector-javaを入れたいときは、`以下の記述を追加します。

libraryDependencies += "mysql" % "mysql-connector-java" % "5.1.37"

Scalajsonを扱うためのjson4sならこんな感じです。

libraryDependencies += "org.json4s" %% "json4s-native" % "3.3.0"

Scalaのライブラリーは%%Javaのライブラリーは%です。
なぜそうなってるのかは気になったらググってください。

このlibraryDependenciesに追加したライブラリーは、 ローカルに無ければ次回のコンパイル時などに自動的にダウンロードされます。

他にもいろいろ機能がありますので、「sbt 使い方」で検索してください。
出てきたコマンドのsbtをそのままactivatorに変えると動くはずです。

IDEを使ってみよう!

さて、ここまでの内容で既に書いて・コンパイルして・テストして・実行はできるわけですが、 最後にIDEをひとつご紹介しておきます。

www.jetbrains.com

有料のUltimateと無料のCommunityエディションがありますが、趣味で使う分にはCommunityで十分です。
仕事で使う場合はライセンスを確認してください。

このIDEA(アイディア)さん、 なんと先ほどActivatorで作ったプロジェクトをそのまま読み込んで作業できるのです。
設定したライブラリーなども自動で取得してくれます。

sbt-ideaなんていうsbtプラグインもありますが、使わなくても動くので使っていません。

詳しいインストールなどについてはサムライズムさんのブログなどをご参照ください。


Scala Advent Calendar 11日目は以上です。
お付き合いいただき、ありがとうございました。

これを見て「この記事よりかはいいものを書けるぜ!」と思ったあなた!
今すぐカレンダーの空きを埋めるのです!!Qiita版も空きがありますです!!

明日も2日連続で私による「いちばんゆるふあななにかを」です。

まだ何を書くのかを決めていませんが、とにかく「ゆるふあ」に行きます!
まだ決まってないので遅い時間になるかもです。。。すみません。

ScalaでSQLが書きたいんだ!!

DSLとかOR Mapperとかじゃなくて、SQLが直接書きたいんだ!!

嘘です。タイトル詐欺です。

最近neo4jというグラフ指向データベースがお気に入りでして、 アクセス用のクエリとしてCypherという言語を使います。

基本はHTTP+JSONでRESTにアクセスできるのですが、Java用にneo4j-jdbcというライブラリがあり、これを使ってアクセスしようと思うわけです。

そうすると、当然SQLではないのでDSLSQL自動生成とかは使えず、直接SQL(本当はCypher)を書ける機能が望まれるわけです。

neo4jのダウンロード

neo4jは全部Javaで出来ているらしいので、あなたとJavaで検索してJavaをインストールしてください。

その後、neo4j公式サイトから無料版をダウンロードしてきます。

適当な場所に展開して、bin/neo4j statusみたいな感じで実行するとlocalhost:7474でhttpを待ち受けてくれるようになります。

まずはブラウザでhttp://localhost:7474に繋いでパスワード変更をしましょう。

初期状態のユーザーはneo4j、パスワードも同じくneo4jですが、1度入力するとパスワード変更を要求されます。

ログインするとダッシュボードのような画面が表示され、 この画面からサンプルDBみたいなのの作り方やヘルプなどが見られるので始めは適当に触ってみるのがよいと思います。

仮データの登録

neo4jの画面の上部からCypherが入力できるので、データを登録してみましょう。

CREATE
  (p :Person{name: "kazzna", password: "some-password"}),
  (b :Blog{name: "kazzna's blog", url: "http://kazzna.hatenablog.com/"}),
  (p)-[:WRITES]->(b)
RETURN p, b

p, bが変数ですね。
細かい説明はしませんが、:Person型のノードと:Blog型のノードを1個ずつ作って、作った物を取得しています。

ついでにせっかくですのですてにゃんあたりを追加しておきましょう。

CREATE
  (s :Person{name: "stefafan"}),
  (sb :Blog{name: "すてにゃんのガチ勢日記", url: "http://stefafafan.hatenablog.com/"}),
  (s)-[:WRITES]->(sb)
WITH s, sb
MATCH (k :Person{name: "kazzna"}), (kb :Blog{name: "kazzna's blog"})
CREATE (s)-[:READS]->(kb), (k)-[:READS]->(sb)
RETURN s, k, sb, kb

kazznaはstefafafanのブログを読んで、stefafafanはkazznaのブログを読む、と。

:Personpasswordが無いのが特徴ですね。

Scalaから取得しよう!!

sbtは使える前提で話を進めますので、sbtって何?って人はtypesafe activatorでググってください。 で、以下のコマンド中のsbtactivatorに置き換えれば多分大丈夫です。

まずbuild.sbtファイルの設定。

name := "test"
version := "0.0.1"
lazy val root = (project in file("."))
scalaVersion := "2.11.7"

resolvers ++= Seq(
  "neo4j-public" at "http://m2.neo4j.org/content/groups/public",
  "jitpack" at "https://jitpack.io"
)

libraryDependencies ++= Seq(
  "org.scalikejdbc" %% "scalikejdbc" % "2.3.0",
  "com.github.kazzna" % "neo4j-jdbc" % "2.2-SNAPSHOT_1",
  "ch.qos.logback" % "logback-classic" % "1.1.3"
)

大体こんな感じ。

resolversを2つ追加しないといけないのは理由があって、 公式のneo4j-jdbcを使うだけなら1番上だけでいいのですが、 現在公式の最新版ではScalikeJDBCなどが自動で生成してくれる?を使ったPreparedStatementに対応していないのです。

なのでとりあえず?の自動で対応した形式に置き換える対応を行った自作版を使います。
JitPackという不思議な力を借りて、githubから自動で依存関係ライブラリーを作り出してもらいます。
(mavenのローカルリリースは使い方が分からなくて出来ませんでした。)

公式にはPull Requestがマージされたので、次回のバージョン(2.2系?)のリリースから使えるようになるはずです。

後は普通のSQLの時と同様ですね。sbt consoleして

scala> import scalikejdbc._
import scalikejdbc._

scala> case class Blog(name: String, url: String)
defined class Blog

scala> :paste
// Entering paste mode (ctrl-D to finish)

case class Person(
  name: String,
  password: Option[String],
  blog: Option[Blog]
)

object Person extends SQLSyntaxSupport[Person] {
  def apply(rs: WrappedResultSet): Person = Person(
      rs.string("person.name"),
      rs.stringOpt("person.password"),
      rs.stringOpt("blog.name").flatMap { n =>
        rs.stringOpt("blog.url").map(u => Blog(n, u))
      })
}

// Exiting paste mode, now interpreting.

defined class Person
defined object Person

scala> Class.forName("org.neo4j.jdbc.Driver")
res0: Class[_] = class org.neo4j.jdbc.Driver

scala> ConnectionPool.singleton("jdbc:neo4j://localhost:7474/", "neo4j", "password")
14:09:20.332 [run-main-0] DEBUG scalikejdbc.ConnectionPool$ - Registered connection pool : ConnectionPool(url:jdbc:neo4j://localhost:7474/, user:neo4j) using factory : <default>
14:09:20.338 [run-main-0] DEBUG scalikejdbc.ConnectionPool$ - Registered singleton connection pool : ConnectionPool(url:jdbc:neo4j://localhost:7474/, user:neo4j)

scala> implicit val session = AutoSession
session: scalikejdbc.AutoSession.type = AutoSession

scala> val name = "kazzna"
name: String = kazzna

scala> val k = sql"MATCH (person :Person{name: ${name}}) WITH person OPTIONAL MATCH (person)-[:WRITES]->(blog) RETURN person.name, person.password, blog.name, blog.url".map(rs => Person(rs)).single.apply()
14:14:33.949 [run-main-0] DEBUG s.StatementExecutor$$anon$1 - SQL execution completed

// ScalikeJDBCの長いトレース

k: Option[Person] = Some(Person(kazzna,Some(some-password),Some(Blog(kazzna's blog,http://kazzna.hatenablog.com/))))

とりあえず普通に使えそうですね。

気付いたことと言えば、Cypherは更新+取得という文が書けるのですが、 それらはJDBCではupdateとしてしか発行できない為に戻り値は取れないようです。
(queryで発行するとreadonlyだよって怒られます。)

もう少し使ってみて、問題点が出ればまたプルリク投げようかなあと言う感じですね。

みなさんもぜひグラフDB使いましょう。

あ、今回は取り上げてないけれど、実際にはちゃんとインデックス貼りましょうね?
インデックスはほぼRDBと同じ感覚で張れば問題ないので。

Optionがモナドだったなんて・・・orz

いやね、Scalaの話なんですけどね、何の話かってね、Option型がOption("a")とかってやって生成できるの。。。

> val a = "abc"
a: String = abc

> val o = Option(a)
o: Option[String] = Some(abc)

今までずっとSome(a)NoneでしかOption生成できないと思っていた自分が恥ずかしくて(><;;

いや、ね、Monadには単位元というかreturnpurepoint
とにかくscalaapplyに当たる何かがあることが定義上必要なんですよ。

つまり絶対Option(???)で何か生成できるはずなのに、なぜか出来ないって思いこんで居たっていう。。。

で、なんでこんな話をしているかというと、Javaのライブラリを使う時のnull対策。

> val a: String = null
a: String = null

> val o = Option(a)
o: Option[String] = None

この通り、nullの値の変数をOptionにapplyするとちゃんとNoneを返してくれる!!!

つい先日apache poiのためのscalaを(spoiwoは微妙に古いので)自作していたんだけど、 これが結構nullが返ってくる。

その時書いてたコードがこちら

def getRow(idx: Int)(sh: Sheet): Option[Row] = {
  val r = sh.getRow(idx)
  if (r == null) None else Some(r)
}

これ、全部Optionのapllyでいいじゃんっていう・・・

是非これからやる人は読みやすい方を使ってください。。。

WindowsでGoのクロスコンパイル環境構築(Go Ver 1.5専用)

さすがはWindows!ひと手間かけたおいしさだぜ!!

経過をメモっているので、最後だけ読めば事足りるよ!!

参考にしたのはこのあたり

Go のクロスコンパイル環境構築 - Qiita
Windows7 64bit版でGo言語のクロスコンパイルを試す - taknb2nchのメモ

どうして試行錯誤時にずっと見てたページって再度ググると全然出てこないんだろう?
なんか英語のall.bashを実行してとかそういうページをずっと見ながらやってたんだけど、 見つからないのでリンクを貼れない。公式っぽいページ。

前提

Windowsユーザーなのでgccは使いません。

なのでcgoと呼ばれるCで作られたと思わしきgoは使えません。

前作業

Go1.5をインストロールします。

golang downloadってbingに入力すれば、msiみたいなのを入手できます。

適当にC:\Goとかに入れます。たぶんデフォルトでそうなってたから。

環境変数としては、GOROOT=C:\Goがセットされている状態になっていることを確認してください。

Goのコンパイヨ

クロヌコンパイヨに使用するコマンド自体はGOOS=[OS名] GOARCH=[ARCH名] go ~と簡単なのですが、 この使用したいGOOSとGOARCHの組み合わせ毎にGOそのものをコンパイヨしておく必要があります。

以後の記述が面倒なので、GOOS=darwin, GOARCH=amd64に限定して進めます。

コンパイヨはGOROOT\srcフォルダーに移動して、以下のコマンドを実行!!

GOOS=darwin GOARCH=amd64 make.bash

すると、たとえbashがある環境でもWindowsではmake.bat使えよって怒られるので、 コマンドプヨンプヨを開いて実行します。

SET GOOS=darwin
SET GOARCH=amd64
make.bat

はい。3行です。1行では実行できません。

上記コマンドを実行すると、BootStrap用のGOがないよと怒られます。
なんか設定がないと、%UserProfile%\Go1.4をBootStrapのrootと認識するようです。

GOROOT_BOOTSTRAPの設定

これは今からコンパイヨしようとしているGoをコンパイヨするためのGoなので、 もう1個Goの塊が必要?なのです。
SET GOROOT_BOOTSTRAP=%GOROOT%としても動くのかは確認していません。

1.4を落としてくるのも面倒なので、C:\Goを丸々コピーしてC:\GoBootを作ります。

そして、GOOROOT_BOOTSTRAPを設定して実行!!

SET GOOS=darwin
SET GOARCH=amd64
XCOPY /S /E /F /G /H /R /K /Y C:\Go C:\GoBoot
SET GOROOT_BOOTSTRAP=C:\GoBoot
make.bat

GOOSとかは設定済みであれば再度設定する必要はありません。

これでできるかと思いきや、gccがないからcgoが作れないよ!!と怒られます。

CGOを作らない

作れないなら作らなきゃいいじゃない!!ということで、 CGO_ENABLEDを0に設定します。

SET GOOS=darwin
SET GOARCH=amd64
XCOPY /S /E /F /G /H /R /K /Y C:\Go C:\GoBoot
SET GOROOT_BOOTSTRAP=C:\GoBoot
SET CGO_ENABLED=0
make.bat

あ、同じく一度やったやつは再実行しなくて大丈夫です。

これでコンパイヨに成功するはず!!

少なくともうちではしました。

これで

SET GOOS=darwin
SET GOARCH=amd64
go build hello.go

とかが可能になります。

ちなみにマックは高いので持ってません。

おまけ

なぜか頑張ってコマンヨプヨンプヨで実行したけど、 よくよく考えればgoをやっているみんなはgit for Windowsとか入れているはずなので、 git bash使って以下の処理でいいような気がする。

cp -r C:\Go C:\GoBoot
GOOS=darwin GOARCH=amd64 GOROOT_BOOTSTRAP=C:\GoBoot CGO_ENABLED=0 make.bat

試してないのでうまく動かないかもしれないけど。

goコマンド実行時も、SET GOOS=???とか実行してしまうと元に戻さないといけないので、 bashで1行で書いたほうが良さげです。

あ、GoBootはいらなくなったら消す方がいいと思う。 お好みで。

型安全なリストを作りたい Part. 1

最終的な目標としては、行列の計算がしたい。

  1 2 3       1 4       1*1+2*2+3*3 1*4+2*5+3*6       14 32
(       ) * ( 2 5 ) = (                         ) = (       )
  4 5 6       3 6       4*1+5*2+6*3 4*4+5*5+6*6       32 77

こんな感じのやつ。
2行3列の行列は3行2列の行列との積を求めることができて、結果は2行2列の行列になる。

もう少し一般化すると、m行n列 * n行p列 = m行p列で、 1つ目の行列の列数と2つ目の行列の行数が一致していないといけない。

これを普通にコンパイラが判定してくれるようにできればいいなという話。

ただ、全通りつくるとかはどう考えても現実的ではないので、 サイズ付きの行列を作ろうというのが主題です。

ちなみに、shapelessというライブラリにはSizedListみたいな型があるそうなので、
仕事とかですぐに必要であればそちらのソースを読んだりするべきですが、
今回は思考そのものを楽しむことが目的なので特に原典もなく間違いも気にせず進みます。

1. サイズを型で表現する。

いきなり難しいことは考えずに、とりあえずサイズ付きの集合型を考えることから始めます。
EmptyList, OneItemList, TwoItemsListのようなのがList[0], List[1]ってできればいいのですね。

まずは単純に数値0を表す型を作ってみます。

sealed trait Size
sealed trait Zero extends Size

ここまでは決め事なのでこれでいいとして、問題はこの先です。 One, Twoなんて続けていったらいつまで経っても終わりません。

そこで、ちょっと前に書いたScalaの型推論って難しいのチャーチ数を使ってみます。

final case class Succ[S <: Size]() extends Size

これで1がSucc[Zero], 2がSucc[Succ[Zero]]なんてふうに表現できるようになりませんか?
どんどん長くなっていくけどこの際気にしない。

2. サイズを型引数にもつリストを定義

これが正しく動作するのかを試すために、Listを定義してみます。
名前は標準ライブラリとかぶらないように適当につけます。

trait SizedList[S <: Size, +A] {
  def :+:[B >: A](b: B): SizedList[Succ[S], B] = new :+:(b, this)
}
case object SizedNil extends SizedList[Zero, Nothing]
case class :+:[S <: Size, A](head: A, tail: SizedList[S, A]) extends SizedList[Succ[S], A]

object SizedList {
  def empty[A]: SizedList[Zero, A] = SizedNil
}

replで動作確認。。。

scala> val a = SizedList.empty[Int]
a: SizedList[Zero,Int] = SizedNil

scala> val b = 2 :+: SizedList.empty
b: SizedList[Succ[Zero],Int] = :+:(2,SizedNil)

scala> val c = 9 :+: 1 :+: 2 :+: 4 :+: 7 :+: SizedList.empty
c: SizedList[Succ[Succ[Succ[Succ[Succ[Zero]]]]],Int] = :+:(9,:+:(1,:+:(2,:+:(4,:+:(7,SizedNil)))))

とりあえず問題なく動いてはいそうです。
まだ値を取り出すことすらできませんが。

次回予告(いつ!?)

次回はこれにheadtailを足してみましょう。

まだ漠然としか考えていませんが、値がOrderedじゃないとソートできないとかそういうのと同じ機構を使えばできそうです。

reverseが定義できればきっとそれなりのことができるはずです。

Scala勉強会で発表してきた。

初スライド!!!

for の使い方について。 http://kazzna.jp/slide/scala_for/index.html

Haskellモナド系ブログが大体doを理解するならStateだってなってたので便乗です。

勉強会でも口頭で言ったけれど、 flatMapの定義をするなら Monad を覚えないと危険です!!

あと、カッコの書き方について教わったのであとであとでスライドをなおす予定。

次回のスライドを作成中。