詩と創作・思索のひろば

ドキドキギュンギュンダイアリーです!!!

Fork me on GitHub

sbt の依存ライブラリを一括 ghq get する

Scala を書くときは IntelliJ Idea みたいな IDE を使っていればドキュメントを確認しやすいのだけど、やっぱ手慣れたものを使いたい。というわけでだいたいの時間はテキストエディタ(Vim)を使っている。そういう時、ソースコードが手元にあると開発の助けになる。

updateClassifiers タスクを使うことですべての(推移的)依存をダウンロードできるらしいが、この場合ファイルは ~/.ivy2/cache/{organization}/{module}/docs/{artifact}-{revision}-javadoc.jar といった場所に保存される。IDE か何かを使ったら閲覧できるのだろうけど、普段使いでは jar 形式で置かれるのは少し面倒だ。

公開されているプロジェクトは全部 ghq という自作ツールでもって手元に整理された状態でクローンするようにしているので、Scala/Java のソースコードもこれにのっとって手元に置きたい。

というわけで sbt プラグインを書いたという話です: sbt-ghq-get-dependencies

使い方

これは性質的に各プロジェクトのプラグインとして設定するよりは、グローバルなところに置くのが良いでしょう。

~/.sbt/0.13/plugins/ghq.sbt みたいなファイルを作って以下のように書く。

resolvers += Resolver.url("bintray-motemen-sbt-plugins", url("https://dl.bintray.com/motemen/sbt-plugins"))(Resolver.ivyStylePatterns)

addSbtPlugin("com.github.motemen" % "sbt-ghq-get-dependencies" % "0.2.0")

sbt から使えるキーは以下のふたつ。

  • ghqGetDependencies …… TaskKey[Unit]。現在のプロジェクトの(直接の)依存ライブラリの SCM リポジトリを探してきて、あれば ghq get する。
  • ghqFilterModule …… SettingKey[sbt.ModuleID => Boolean]。依存モジュールを ghq get すべきかどうかを判断する関数。デフォルトでは configurationsNoneSome("test") であるものだけダウンロード対象とする。

リポジトリの URL は ~/.ivy2/cache 以下の ivy-*.xml.original ファイルに書かれているものを使用するので、あらかじめ update しておく必要がある。

実行するとこんな感じ。

> ghqGetDependencies
[info] org.scala-lang#scala-library;2.11.7 :: git://github.com/scala/scala.git
    exists /Users/motemen/dev/src/github.com/scala/scala
[info] org.scala-lang#scala-library;2.11.7 :: git://github.com/scala/scala.git
    exists /Users/motemen/dev/src/github.com/scala/scala
[info] org.scalatra#scalatra_2.11;2.4.0 :: git://github.com/scalatra/scalatra.git
    exists /Users/motemen/dev/src/github.com/scalatra/scalatra
[info] org.scalatra#scalatra-scalate_2.11;2.4.0 :: git://github.com/scalatra/scalatra.git
    exists /Users/motemen/dev/src/github.com/scalatra/scalatra
[info] org.scalatra#scalatra-scalatest_2.11;2.4.0 :: git://github.com/scalatra/scalatra.git
    exists /Users/motemen/dev/src/github.com/scalatra/scalatra
[info] org.scalaj#scalaj-http_2.11;1.1.6 :: git@github.com:scalaj/scalaj-http.git
    exists /Users/motemen/dev/src/github.com/scalaj/scalaj-http
[info] org.json4s#json4s-jackson_2.11;3.3.0 :: git://github.com/json4s/json4s.git
    exists /Users/motemen/dev/src/github.com/json4s/json4s
[info] org.scalaz#scalaz-core_2.11;7.1.4 :: git@github.com:scalaz/scalaz.git
    exists /Users/motemen/dev/src/github.com/scalaz/scalaz
[info] org.scalaz#scalaz-concurrent_2.11;7.1.4 :: git@github.com:scalaz/scalaz.git
    exists /Users/motemen/dev/src/github.com/scalaz/scalaz
[info] com.typesafe.slick#slick_2.11;3.1.1 :: git@github.com:slick/slick.git
    exists /Users/motemen/dev/src/github.com/slick/slick
[info] org.postgresql#postgresql;9.4.1207 :: (not found)
[info] com.github.tarao#slick-jdbc-extension_2.11;0.0.3 :: git@github.com:tarao/slick-jdbc-extension-scala.git
    exists /Users/motemen/dev/src/github.com/tarao/slick-jdbc-extension-scala
[info] net.debasishg#redisclient_2.11;3.1 :: git@github.com:debasishg/scala-redis.git
    exists /Users/motemen/dev/src/github.com/debasishg/scala-redis
[info] org.pegdown#pegdown;1.6.0 :: git@github.com:sirthias/pegdown.git
     clone ssh://git@github.com/sirthias/pegdown.git -> /Users/motemen/dev/src/github.com/sirthias/pegdown
       git clone ssh://git@github.com/sirthias/pegdown.git /Users/motemen/dev/src/github.com/sirthias/pegdown
Cloning into '/Users/motemen/dev/src/github.com/sirthias/pegdown'...
[info] org.mockito#mockito-core;2.0.36-beta :: (not found)
[success] Total time: 10 s, completed 2016/02/08 1:28:30

プラグインの依存を取得する

reload plugins でプラグインのプロジェクトに移動した後、ghqGetDependencies することで addSbtPlugin したライブラリのソースコードを取得できる。reload return で元のプロジェクトに復帰。

sbt Reference Manual — Plugins に書いてあるけど偶然発見できたという感じだった。知らなかった……。

sbt で Vim のスワップファイルが watch されるのを抑制する

Scala の話。sbt~compile とかしてるときに Vim で新しいファイルを編集しはじめるとコンパイルが始まってなんだこりゃとなることがある。いまいちまとまった情報がないのでここに記しておく。

こういう場合 watchSources というキーに変更を加えてやるとよい。

グローバルに適用するために、~/.sbt/0.13/global.sbt などのファイルに以下のように記述する:

watchSources ~= {
  _.filterNot {
    f =>
      f.isDirectory ||
        """^\..*\.sw.$""".r.findFirstIn(f.getName).isDefined
  }
}

場合によっては ~/.sbt/0.13.9 以下に配置する必要があるかもしれない。

~=

どういう訳かはわからないけれど 0.13.x 系のドキュメントには ~= の説明がない。0.12.x 系には載っている。

More Kinds of Setting — sbt Documentation

参考

ブラウザで動くラムダ計算器を作った(Scala で)

最近 Types and Programming Languages を読んでいて、はじめは我慢していたものの、やはりラムダ式の簡約をコンピュータで確認したい気持ちが高まってきたので、ブラウザで動くものを書いてみた。この本には OCaml による実装の章がときどき挟まれるので、演習の一環ともいえる。

Lambda Calculator

"(λx.λy.x y)(λz.z)" といったラムダ式を入力して送信すると(λ\ で代用可能)、解析された項が出力される。その後1ステップずつ簡約して、項が評価されていく過程を眺められる。評価戦略は TAPL にしたがって call-by-value と call-by-name を提供してるつもりです。毎回どのサブ項が評価されたのかがハイライトされるので、実用的。

f:id:motemen:20140731133327p:plain

同じ項を何度も入力するのが辛いので文字列マクロを導入してあり、"$omega" などと書くと展開した上で解析される。グローバル変数のような概念がないので、文字列置換です。いくつかのよく使う(?)式を定義しておいた

パーマリンクもあるので、ラムダ式を SNS で共有することもできる。

http://motemen.github.io/lambda-calculator/untyped.html?s=call-by-value#$omega

実装は JavaScript……ではなく主に Scala で行い、Scala.js で JS に変換した。ハマると解決できなそうなので動かなかったら諦めようと思っていたんだけど、普通に動いてしまったので感動です。とはいえいくつか工夫しなくてはならなかった点があるのでここに記す。

Scala.js で parser-combinators を使う

id:xuwei さんのアドバイスにしたがって、bintray にアップロードされているものを使うよう変更できました。あざーす!

Scala.js には Scala のパーザコンビネータは含まれていない(JS にコンパイルできない)が、2.11 になって切り出されたもののフォークが存在するので、これを利用する。(参考

今のところ公開された場所に登録はされていないらしいので、依存プロジェクトとして sbt のビルド定義に含んでやる。この時、サブプロジェクトの plugins.sbt は読み込まれないのでルートプロジェクトのプラグインとして指定する必要がある。コアプロジェクトをこれに依存させる。(build.sbt

JS として切り出すプロジェクトを分ける

はじめ Scala で実装してテストも Scala で書いてたんだけど、このプロジェクトをそのまま Scala.js のプロジェクトとすると sbt test の挙動も変わってしまった。これは意図的なもので 、Scala がコンパイルするものと Scala.js がコンパイルするものとの間に互換性がないためらしい(JVM のバイトコードを JS に変換してるわけではないってことかな)。なので JS 側へのインターフェースだけ別プロジェクトに切り出し、コア部分に依存させるということにする。

ちなみに Scala.js でクライアントサイドの JS を全て書くこともできるんだけど、そこまでの意欲はないのでコア部分のインターフェースを書いたら、あとは生の JS でやることにした。

結果、こんな感じの構成になった。

. [Scala, depends modules/scala-parser-combinators]
|-- js [Scala.js, depends root]
|-- modules
    `-- scala-parser-combinators

gulp でビルドしたい

sbt が律速(重い)なのでビルドは sbt に一元化したくなるけれど、とにかく重いしこの前段階でもう飽き飽きしたので、gulp から sbt を呼ぶようにした。まあ必要なのはほぼ sbt ~fastOptJS だけなので spawn するだけ。Scala.js はコンパイル時、生成先のファイルにどんどん追記して完成させるらしいのだけど、これが gulp.watch と相性悪い。内部ではファイルシステムのイベントに対して debounce と呼んでる遅延処理がデフォルト 500ms かかっているんだけど、どちらかというと throttle と呼ぶべき代物で(underscore のネーミングを拝借すると)、困るのは、最後の書きこみが行われたあとにイベントが発生しないことがある。そこでコールバックのほうで 1s ほど _.debounce してやることにした。

そのほか

本体の話全然書いてないけどライブラリ使ったらパーザもだいぶ楽できた。愚直に書いたら無限に再帰しだして、久しぶりに左再帰とかいう言葉を思い出した。項を簡約するときに木のどの位置が簡約されたのか示すあたりのコードがあんまり綺麗ではない。

結局のところ評価して得られたこの値って何なの、というのがどうしても分からないことがあるので、ラムダ式を JS の関数に評価する API も作ってある。チャーチ数の確認とかにご利用ください。

> λ('λs.λz.s (s z)')
function (s) { return (function (z) { return s(s(z)) }) }
> λ('λs.λz.s (s z)')(function (x) { return x+1 })(0)
2

最後に書くようなことではないけど、実装が合ってるのかどうかイマイチ自信がない。業界で標準的なテストケース集とかあったら教えてほしいです。プルリク待ってます

型システム入門 −プログラミング言語と型の理論−

型システム入門 −プログラミング言語と型の理論−

  • 作者: Benjamin C. Pierce,住井英二郎,遠藤侑介,酒井政裕,今井敬吾,黒木裕介,今井宜洋,才川隆文,今井健男
  • 出版社/メーカー: オーム社
  • 発売日: 2013/03/26
  • メディア: 単行本(ソフトカバー)
  • クリック: 68回
  • この商品を含むブログ (9件) を見る

Scalaスケーラブルプログラミング第2版

Scalaスケーラブルプログラミング第2版

  • 作者: Martin Odersky,Lex Spoon,Bill Venners,羽生田栄一,水島宏太,長尾高弘
  • 出版社/メーカー: インプレスジャパン
  • 発売日: 2011/09/27
  • メディア: 単行本(ソフトカバー)
  • 購入: 12人 クリック: 235回
  • この商品を含むブログ (45件) を見る

はてなで一緒に働きませんか?