PureScriptサイトまとめ 2014年12月04日

PureScript Advent Calendar 2014 - Qiita 4日目の記事です。

僕が知っているPureScriptのサイトについて、コメント付きで紹介していきたいと思います。

公式

PureScript

Home · purescript/purescript Wiki

Read PureScript by Example | Leanpub

PURSuit

Try PureScript!

Github

PureScript

PureScript Contrib

joneshf (Hardy Jones)

bodil (Bodil Stokke)

24 Days of PureScript

コミュニティ

purescript - Google グループ

Browse #purescript

Phil Freeman(@paf31)さん | Twitter

PureScript Commits(@purescript)さん | Twitter

PureScript

PureScript: a statically typed language which compiles to JavaScript | Hacker News

日本語情報

PureScript:JavaScriptにコンパイルされるHaskellライクな言語

PureScript使ってみた - Qiita

Purescript with Monad

拙作のスライド

PureScript Advent Calendar 2014 - Qiita


函数型なんたらの集いで発表してきました 2014年11月29日

函数型なんたらの集い 2014 in Tokyo - connpass の発表資料をブログに載せ忘れていたので、今更ですが載せておきます。

Pure boys script

しかし「そろそろ出るとか出ないとか…(何が?)」とか書いておいて、一ヶ月経ってもまだ出てないんですよね…(何が?)


Scalaz勉強会でEitherとValidationについて説明してきました。 2014年07月16日

Scalaz勉強会でEitherとValidationについて説明してきました。

Scalaz勉強会 - connpass

Scalaz勉強会を開催しました - scalaとか・・・

吉田さんも書いてますが、結構人が集まって良かったと思います。Scalazでこんだけ人が集まるっていうのはもちろん驚きですが、関数型系の大規模勉強会もあんまないですからね。もっと増えてほしいところです。

で、僕の発表というか解説ですが、

EitherとValidation - Scalaz勉強会

おそらく発表の中では断トツに簡単な内容だと思われるので、Scalazをまだ全然知らないって人におすすめです。

ただ、Eitherのfor文中のif文でおかしな挙動をすることについて、Monadなら問題が起きないというような記述がありますが、これは不正確でした。 吉田さんの昔の記事に詳しい解説があるようです。

HaskellのdoとScalaのfor式とEitherとMonadPlus - scalaとか・・・

Scalazは非常に難解なライブラリなのに勉強会で大人数が集まるということで、すぐに使えて役に立つような発表も必要だと考えてEitherとValidationを題材に選びました(それと募集開始から開催まで2週間弱しかないという時間の都合もありますが…)。 しかし今度は内容のレベルが低すぎて、普段関数型の人達は小難しいことばっか言ってるのに、聴衆に媚びて難易度下げすぎじゃないの?と思われる方もいらっしゃるかもしれません。 でも僕なりに一つ気をつけたことがあって、それは、もうモナド自体をテーマにすることはやめようってことです。

今回の発表でも、がくぞさんの発表が前にあるという前提ですが、「モナドだから便利」とか「モナドだから簡単に書ける」という表現をさらっと使っています。これまでによくあるような「モナド=難しいもの」を解説ということではなくて、 「あ、これモナドか → じゃあ、わかりやすい&つかいやすい」ということを前提に発表内容を組み立てています。これは関数型に慣れた人ならみんな持ってる感覚だと思います。 僕は、もうどんなに初心者向けの発表であっても、そういう感覚を聴衆と共有していきたいと思っています。

圏論からモナドを考えるような発表は全然あっていいと思いますが、モナドを過剰にピックアップするような解説はもういいんじゃないかという気がしてします。それが初心者を関数型に導いているとはどうも思えないんですよね。 変にメタファーで考えたり、これからはモナドだ!みたいに持ち上げたりするのは、入門者に配慮しているようで入門者を逆に遠ざけるんじゃないかと思います。 モナドは多少ややこしいですけど、特にScalaはIOモナドとかいらないですし、必要なときに便利なものとしてあってほしいなと思っています。


関数型LT大会でPureScriptの発表してきました 2014年05月13日

5月11日にクックパッド社で開催された関数型LT大会でPureScriptについて発表してきました。

関数型LT大会 - connpass

PureScript - 関数型LT大会

今回はHaskellの勉強会のLT大会ということで、Haskeller向けにPureScriptで特徴的な型システムの話をしました。

PureScriptは関数型的にも面白いんですけど、綺麗でコンパクトなJavaScriptを生成する実用的な言語でもあります。今後一般向けのPureScript入門記事や発表なんかもしていきたいですね。

今回の関数型LT大会は面白い発表がたくさん聴けて楽しかったです。何でもあり関数型イベントってあまりないので、今後もこういうイベントが開かれると嬉しいですね。


Scala.js 0.4.0新機能の紹介 2014年03月14日

Scala.jsの0.4.0がリリースされました。0.3のリリースが一ヶ月前なので、いいペースで開発されているようです。

その0.4.0の新機能の紹介をしたいと思います。基本的には↓こいつの和訳みたいなもんです。

Scala.js v0.4.0 release! - Google グループ

0.4.0は大きく三つの新機能が追加されました。

  1. @JSExportアノテーションが追加され、Scala.jsのライブラリをJavaScriptから呼び出せるようになった。
  2. 新しいデッドコード削除アルゴリズムにより、最適化が高速になり、サイズも小さくなった。
  3. また新しいデッドコード削除アルゴリズムにより、JSに移植されていないJava標準ライブラリを参照した場合に警告が出るようになった。

@JSExportアノテーション

@JSExport
object Foo
// or
@JSExport("FooBar")
object Bar

みたいにアノテーションを付けると、JavaScriptからその名前で参照できるようになります。 今までもJavaScriptからScala.jsのコードを参照することはできなくはなかったのですが、Google Closure Compilerの最適化により動かなくなっていました。今回の@JSExportアノテーションにより、その問題も解決しました。 また、今までのScala.jsアプリケーションでは同じようにGoogle Closure Compilerの最適化を回避するためにstartup.jsというJSが必要だったのですが、それも不要になりました。

詳しくは以下を参照してください。

Export Scala.js APIs to JavaScript

最適化が高速化

tototoshiさんのツイートにもあるように新しいデッドコード削除コマンドのpreoptimizeJSが追加されました。これにより通常のpackageJSの出力が22MBのところがpreoptimizeJSにより1.5MBまで小さくなるようです。 しかもpreoptimizeJSはpackageJSと同じくらい速いらしいです。これに対してoptimizeJSが走るので劇的に最適化時間が短縮したわけです。さらに最適化後のコードも33%ほど小さくなるようです。

JSに移植されていないJava標準ライブラリを参照した場合に警告

これは地味に嬉しいと思います。というのも、このケース結構あります。 Scala標準ライブラリはほとんどすべてJSにコンパイルされますが、逆にJavaの標準ライブラリはほとんど移植されてないと言ってもいい状態なので、かなりひっかかります。しかも実行時エラーです。 コンパイル時に警告が出るのはありがたいですね。


sbt 0.13.2-M3の新機能AutoPluginを使ってみる 2014年03月12日

昨日 sbt 0.13.2-M3 が出たのですが、その新機能のAutoPluginに興味を持ったので、試しに使ってみることにしました。

AutoPluginの機能

AutoPluginには以下の機能があるようです。

  1. 設定の自動読み込み
  2. Pluginの依存関係を書くことができる
  3. プロジェクトごとにどのプラグインを使うか使わないかの設定を細かくおこなえるようになる

1は、たとえば sbt-assembly というプラグインで考えてみますと、このプラグインを使うには project/assembly.sbt に

addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.11.1")

と書いて、build.sbt に

import AssemblyKeys._

assemblySettings

みたいに書く必要があるのですが、AutoPluginにした場合には後半のbuild.sbtの記述が不要になります。

2は、たとえば Playの機能を拡張するようなsbtプラグインを作った場合、まずPlayのsbtプラグインが読み込まれることを期待すると思いますが、その依存関係を記述することができます。 それで今までの場合、ちゃんとプラグインを読み込まないと、Keyが見つかりませんみたいなわかりづらいスタックトレースが出ると思いますが、AutoPluginを使った場合はわかりやすいエラーメッセージを表示したりしてくれるらしいです。 設定も正しい順番で読み込んでくれるらしいです。

3は、設定が自動的に読み込まれるということはマルチプロジェクトなんかの場合、プロジェクトごとの設定はどうなるんだとなると思いますが、

Project.plugins(Web && Javascript && !MyPlugin)
// or
Project.addPlugins(Web, Javascript).disablePlugins(MyPlugin)

みたいにどのプラグインを使うか使わないかの設定ができるようになるらしいです。

詳しくは以下に書いてあります。

User Stories: AutoPlugins · sbt/sbt Wiki

sbt/main/src/main/scala/sbt/Plugins.scala at 0.13 · sbt/sbt

サンプルプラグイン

hexx/sbt-list-assets-plugin

sbt-webのAssetファイルを全部表示するだけのシンプルなプラグインです。既存のプラグインへの依存も書けるのかと思ってsbt-webを使ってみたのですが、それは書けないようです。

既存の書き方

// normal/src/main/scala
import sbt._
import sbt.Keys._

import com.typesafe.sbt.web.SbtWebPlugin.WebKeys

object SbtListAssetsPlugin extends Plugin {
  object ListAssetsKeys {
    val listAssets = taskKey[Unit]("List all assets.")
  }

  import WebKeys._
  import ListAssetsKeys._

  lazy val listAssetsSettings: Seq[Setting[_]] = Seq(
    listAssets := (unmanagedSources in Assets).value foreach (f => streams.value.log.info(f.getName()))
  )
}

こういう感じが今までの簡単なsbtプラグイン書き方だと思います。sbt.Pluginを継承して、プラグインの設定(listAssetsSettings)を書いて、他のプラグインに依存する場合はKeyを参照するだけという感じだと思います。

使い方はproject/plugins.sbtなどでプラグインを読み込んで、build.sbtに以下のような設定を書くと

// normal-tester/build.sbt
webSettings

listAssetsSettings

sbtのプロンプトでlistAssetsというコマンドが使えるようになります。

AutoPluginを使った書き方

// auto/src/main/scala
import sbt._
import sbt.Keys._

import com.typesafe.sbt.web.SbtWebPlugin.WebKeys

object SbtListAssetsPlugin extends AutoPlugin {
  // def select = Web
  def select = Plugins.empty

  object ListAssetsKeys {
    val listAssets = taskKey[Unit]("List all assets.")
  }

  import WebKeys._
  import ListAssetsKeys._

  override def projectSettings = SbtWebPlugin.webSettings ++ Seq(
    listAssets := (unmanagedSources in Assets).value foreach (f => streams.value.log.info(f.getName()))
  )
}

AutoPluginを使う場合でも、そんなには変わらないです。 def selectで他のプラグインへの依存関係を書くのと、projectSettingsというメソッドをオーバーライドして設定を記述するくらいです。

sbt-webプラグインがAutoPluginだったらdef select = Webみたいに書くと思うのですが、まだそうではないのでPlugins.emptyを指定します。

使い方はbuild.sbtに

// auto-test/build.sbt
webSettings

とAutoPluginではないsbt-webの設定を書くだけです。今回作ったプラグインの設定は書く必要ありません。

まとめ

AutoPluginは単独で使ってもそんなに嬉しくないと思うので、微妙なサンプルになった気はしますが、既存のsbtプラグインをAutoPlugin化するのは簡単だと思います。 sbtのソースを読む限りAutoPluginに全面的に以降するらしいので、AutoPlugin化は必須だと思われます。

問題は既存のsbtプラグインを依存関係に指定できないので、sbt 0.13.2 が正式リリースされて他のプラグインがAutoPluginに対応されるのを待って、自分のプラグインの依存関係を記述するということになることですね。

sbt 0.13.2はこの間のインクリメンタル・コンパイルの改善といい、マイナーバージョンアップとは思えない大規模な変更が入ってますね。 でも、これで一応0.13.2に対する機能の追加は終わりらしいので、あとはドキュメント変更とリリース待ちですね。


Scala.jsのFutureとPromiseを使ってみる 2014年03月07日

Scala.jsFiddle というScala.jsをWebで編集して実行できるという面白いサイト最近できたのですが、 そのソースを見ていたらScala.js 0.3から入ったFutureとPromiseがわかりやすい形で使われていたので、その部分を取り出してちょっと使ってみることにしました。

今回作ったサンプルコードは以下にあります。

hexx/scala-js-future-example

XMLHttpRequestをFutureでラップするというコードです。

サーバ

サーバ側から見ますと、

// server/src/main/scala/Server.scala
import unfiltered.request._
import unfiltered.response._
import unfiltered.jetty._
import unfiltered.filter._

object ACAO extends HeaderName("Access-Control-Allow-Origin")

object Json {
  def apply(json: String) = new ComposeResponse(JsonContent ~> ResponseString(json))
}

object Server extends App {
  Http(8080).filter(Planify {
    case GET(Path("/")) =>
      Ok ~> ACAO("*") ~> Json("""{"name":"Martin"}""")
    case req @ POST(Path("/")) =>
      Ok ~> ACAO("*") ~> Json(s"""{"posted":"${Body.string(req)}"}""")
  }).run()
}

unfilteredを使ったコードで、 GET / で {"name":"Martin"} というJSONを、 POST / で {"posted":"ポストされたデータ"} というJSONを返します。

クライアント

クライアント側のXMLHttpRequestをラップするコードは以下のような感じです。Scala.jsのコードなのでScalaで書かれていますが、JavaScriptに変換されます。

// client/src/main/scala/Ajax.scala
package com.github.hexx

import scala.scalajs.js
import scala.concurrent.{Promise, Future}
import org.scalajs.dom.{XMLHttpRequest, Event}

case class AjaxException(xhr: XMLHttpRequest) extends Exception

object Ajax {
  def get(url: String): Future[XMLHttpRequest] = apply("GET", url)

  def post(url: String, data: String): Future[XMLHttpRequest] = apply("POST", url, Option(data))

  def apply(method: String, url: String, data: Option[String] = None): Future[XMLHttpRequest] = {
    val req = new XMLHttpRequest()
    val promise = Promise[XMLHttpRequest]

    req.withCredentials = true

    req.onreadystatechange = (e: Event) => {
      if (req.readyState.toInt == 4) {
        if (200 <= req.status && req.status < 300) {
          promise.success(req)
        } else {
          promise.failure(AjaxException(req))
        }
      }
    }

    req.open(method, url, true)

    data match {
      case Some(d) => req.send(d)
      case None => req.send()
    }

    promise.future
  }
}

XMLHttpRequestとPromiseの両方を知ってる人にとってはわかりやすいコードだと思います。 Promiseはsuccessメソッドか、failureメソッドを使ってFutureの値を確定することができます。 その処理をXMLHttpRequestのonreadystatechangにコールバックで登録しています。

使い方

使い方は以下のような感じです。

// client/src/test/scala/AjaxTest.scala
import scala.scalajs.js
import scala.scalajs.test.JasmineTest

import scala.concurrent.Await
import scala.concurrent.duration.Duration

import com.github.hexx.Ajax

import scala.scalajs.concurrent.JSExecutionContext.Implicits.runNow

object HtmldaJsJasmineTest extends JasmineTest {
  describe("Futures and Promises") {
    it("should work on Scala.js.") {
      val json = for {
        res1 <- Ajax.get("http://localhost:8080/")
        json = js.JSON.parse(res1.responseText)
        res2 <- Ajax.post("http://localhost:8080/", s"${json.name}-san")
      } yield js.JSON.parse(res2.responseText)

      val posted = Await.result(json, Duration.Zero).posted

      expect(posted).toEqual("Martin-san")
    }
  }
}

ScalaのFutureはfor構文を使うことができます。まず、 GET / をしてJSONを受け取り、その結果を POST / に投げています。その一連の処理がfor構文に書かれています。

JavaScriptでのFutureの実行は二種類あります。JavaScriptはマルチスレッドではないので、 import scala.scalajs.concurrent.JSExecutionContext.Implicits.runNow の場合はその場で実行されて、 mport scala.scalajs.concurrent.JSExecutionContext.Implicits.queue の場合は setTimeout 0 で実行されるということになります。

結構ScalaのFutureがそのまま使える感じで、いいのではないかと思います。 Scala.jsFiddleのコードでは、FutureのApplicative構文のようなものが使えるScala Asyncも使っていました。

scala/async

ただ、テスト環境ではEnvJSを使っているんですが、異常系が動かないことが多くて困りました。実際のブラウザ環境ならもっと動くと思うんですが、まだ調査が足りてない感じです。


Scala.jsとJVMの両対応コードとScala.jsのテストの書き方 2014年02月26日

Scala.jsの開発は最近も活発で、0.3が出てFutureなど色々機能が増えたり、Jasmineを使ってScala.jsのテストを書けるようになったり、Scala.jsとJVMの両対応のライブラリが出てきたりしました。

そこで僕もためしに、前にScalaカンファレンスJapanのハッカソンで書いた、簡単なHTML DSLのライブラリ「htmlda」のScala.js対応をやってみました。

hexx/htmlda

build.sbtからJVM用のプロジェクトとScala.js用のプロジェクトを取り出してみます。

// for JVM
val jvmSettings = baseSettings ++ Seq(
  libraryDependencies += "org.scalatest" %% "scalatest" % "2.0" % "test",
  version := htmldaVersion
)

lazy val htmlda = project.in(file(".")).settings(
  jvmSettings ++ Seq(
    name := "htmlda",
    unmanagedSourceDirectories in Compile <+= baseDirectory(_ / "shared" / "main" / "scala")
  ) : _*
)

// for Scala.js
val jsSettings = baseSettings ++ scalaJSSettings ++ Seq(
  version := htmldaJsVersion
)

lazy val htmldaJs = project.in(file("js")).settings(
  jsSettings ++ Seq(
    name := "htmlda",
    unmanagedSourceDirectories in Compile <+= baseDirectory(_ / ".." / "shared" / "main" / "scala"),
    libraryDependencies ++= Seq(
      "org.scala-lang.modules.scalajs" %% "scalajs-jasmine-test-framework" % scalaJSVersion % "test",
      "org.webjars" % "jquery" % "2.1.0-2" % "test",
      "org.webjars" % "envjs" % "1.2" % "test"
    )
  ) : _*
)

一番のポイントはScalaのソースコードをsrc/main/scala からshared/main/scalaに移して、それぞれのプロジェクトでunmanagedSourceDirectoriesとして読み込んでいるところです。 プレーンなScalaのコードならこれでそれぞれにコンパイルできるようになります。テストのコードは共通化できないので、これはそれぞれのプロジェクトで用意します。 またWebJarを使ってScala.jsのテスト用にJQueryとJavaScriptでDOMをエミュレートするEnvjsというライブラリを読み込んでいます。

続いてScala.jsのテストの書き方ですが、最近Scala.js標準ライブラリにjasmine-test-frameworkというものが入りました。 Scala.jsのコードをJavaScriptのBDDフレームワークのJasmineを使ってテストできるようにするというものです。 テストコードを見てみますと、

import scala.scalajs.js
import scala.scalajs.js.Dynamic.global
import scala.scalajs.test.JasmineTest
import com.github.hexx.htmlda.PlainHtmldaDSL._

object HtmldaJsJasmineTest extends JasmineTest {
  global.importScripts("jquery.js")

  describe("HtmldaJS") {
    it("should render HTML") {
      val jq = global.jQuery

      val node = dsl {
        %p {
          %a ($("href" -> "http://www.scala-js.org/"), t("Scala.js"))
        }
      }

      jq("body").append(jq(node.render))

      val body = global.document.getElementsByTagName("body").asInstanceOf[js.Array[js.Dynamic]].head

      val p = body.innerHTML.toString

      expect(p).toMatch(""".*

<a href=\"http://www.scala-js.org/\">Scala.js

.*""") } } }

一見普通のScalaで書かれたrspec系のBDDのテストコードに見えますが、実際にはJavaScriptにコンパイルされて、JavaScriptのJasmineを使って実行されます。 このテストではHtmldaのDSLで作られたHTMLをJQueryを使って正常にAppendできているかのテスト行っています。

以上、Scala.jsのクロスコンパイルとテストコードの書き方を見ましたが、とても簡単にできるのがわかったと思います。 まだまだコードサイズやコンパイル時間など多数の問題を抱えていますが、Scala.jsの進歩のスピードは早く、どんどん便利になってきています。 用途を限定すれば実用になる日がくるかもしれません。


Unfilteredでモナド構文が使えるUnfiltered-Directives 2014年02月23日

Play2以降、Playばかり気にしていましたが、Unfilteredでモナド構文が使えるUnfiltered-Directivesというのが一年弱くらい前に入っていたようです。

unfiltered/directives at master · unfiltered/unfiltered

Unfiltered — Directives and Validation

もう公式のドキュメントにもしっかり書いてあって、ここであらためて書くこともないんですが、あまり知られてないようなので紹介しておきます。

UnfilteredはパターンマッチでHTTPのメソッドやパスを指定できるわけですが、書きやすい代わりにパターンマッチに失敗すると何でも404 Not Foundを返してしまうという問題があります。 公式にあるサンプルですが、

def intent = {
  case req @ POST(Path("/example")) & Accepts.Json(RequestContentType("application/json")) =>
    Ok ~> JsonContent ~> ResponseBytes(Body.bytes(req))
}

こういうふうに全部パターンマッチで書いた場合、HTTPのメソッドが間違っていた場合でも、Content-Typeが間違っていた場合でも全部404になってしまいます。 そこでUnfiltered-Directivesの出番になります。Unfiltered-Directivesを使うとモナド構文を使って以下のように書くのですが、

import unfiltered.directives._, Directives._

def contentType(tpe:String) =
  when{ case RequestContentType(`tpe`) => } orElse UnsupportedMediaType

def intent = Directive.Intent {
  case Path("/example") =>
    for {
      _ <- POST
      _ <- Accepts.Json
      _ <- contentType("application/json")
      req <- request[Any]
    } yield Ok ~> JsonContent ~> ResponseBytes(Body.bytes(req))
}

チェックに失敗した場合、それぞれで405 Method Not Allowed、406 Not Acceptable、415 Unsupported Media Typeを返すようになります。 デフォルトである程度エラーコードを返すように定義されていますが、contentTypeメソッドを見ればわかるように自分で定義するのも簡単にできます。

また、このUnfiltered-Directivesを使って、パラメータのチェックもできます。たとえば、

def intent = Directive.Intent {
  case Path("/") =>
    for {
      in <- data.as.Int.fail { (k, v) =>
        BadRequest ~> ResponseString(s"'$v' is not a valid int for $k")
      } named "in"
      req <- data.Requiring[String].fail{ name =>
        BadRequest ~> ResponseString(name + " is missing")
      } named "req"
    } yield ResponseString(s"in: $in, req: $req")
}

こう記述することにより、inというパラメータがIntかどうかチェックされ、reqというパラメータが必須パラメータになります。 他にも色々入力の加工やチェックする方法があるみたいです。詳しくは公式のドキュメントを見てみてください。

モナド構文は簡単にComposableな仕組みが作れるので、活用するライブラリが増えてほしいですね。


Play 2.3から採用されるというsbt-webをUnfilteredで使う 2014年02月20日

最近Play 2.3から採用されるというsbt-webをちょっと見てます。

sbt-web pluginとplayframework2.3 - scalaとか・・・

sbt/sbt-web

sbt-web 1.0.0-M1 is available - Google グループ

Typesafeがsbt-webを作ってるのは、PlayからJavaScriptやCSSなど(以下アセット)のクライアントサイドの機能を分離したいという意向があるみたいですね。

sbt-webは単体で使うものではなく、他のプラグインに対する基盤の機能を提供しています。

などの機能があるようです。

それで、このsbt-webの上にJavaScriptを実行するためのsbt-js-engineというプラグインがあったり、 そのsbt-js-engineを使ってLESSをコンパイルするためのsbt-less-pluginや、 JavaScriptにJSHintを通すsbt-jshint-pluginなどが作られていたり、機能が階層的に作られてるみたいです。

sbt-webはPlay用だと思うのですが、クライアントサイドの機能は他のScalaのフレームワークでも弱い部分だと思うので、ためしに別のWebフレームワークのUnfilteredでsbt-less-pluginを使ってみました。

unfiltered/unfiltered

sbt/sbt-less-plugin

基本的にはほとんどそのままで使えるのですが、sbt-web標準のディレクトリ構成だとコンパイルされたCSSをJavaのリソースとして読めないので、アセットの生成先の設定だけ変更します。

resourceManaged in WebKeys.Assets := (classDirectory in Compile).value / "public"

というコードをbuild.sbtに書きます。これで、たとえば src/main/assets/css/app.less に置いたファイルがコンパイルされて、 getClass.getResource("/public/css/app.css")という感じに読み込めるようになります。

Unfilteredで使えるようにするには

object Server extends App {
  Http(8080).resources(getClass.getResource("/public")).filter(Planify(Directive.Intent {
    case Path("/") =>
      for {
        _ <- GET
      } yield Html(
        <html>
          <head>
            <link rel="stylesheet" type="text/css" href="/css/app.css" />
          </head>
          <body>
            <h1>Title</h1>
            <div id="container">
              <p><a href="https://github.com/unfiltered/unfiltered">unfiltered/unfiltered</a></p>
            </div>
          </body>
        </html>
      )
  })).run()
}

という感じにresourcesメソッドにディレクトリを指定すると読めるようになります。 sbt-webによりディレクトリ構成が標準化されているのでsbt-less-plugin以外の他のプラグインでも同様に使えるはずです。

全体のサンプルはこちらになります。

hexx/unfiltered-sbt-web-sample

簡単に使えるんで、これからsbt-web関連のプラグインが増えてきたら色々便利そうな気がしますね。


Scala勉強会第119回 in 本郷 2014年02月13日

rpscalaことScala勉強会第119回 in 本郷に参加してきました。

Scala勉強会第119回 in 本郷 - PARTAKE

この日はScalaの高速化の話が多かったですね。

Scalaの初心者向けのテキストを書くとしたらどのようなものが良いか

今回は初心者の方がいなかったので、上記のテーマでちょっと話をしました。

以上のような話がありました。僕もそろそろドキュメント書いていきたいですね…。

MD5のパスワードハッシュを総当たりで調べる | ぽんこつさん(ponkotuy)

少し前にJALの不正ログイン問題で数字6ケタのパスワードが話題になりましたが、そのパスワードのハッシュを総当たりで調べるというプログラムが流行っていたようです。

パスワード問合せシステムを作る (clojureのreducers) - Qiita

徳丸浩の雑記帳: 数字6桁パスワードのハッシュ値の総当たり、PHPなら約0.25秒で終わるよ

Java - String.formatが遅い理由 - Qiita

それに影響されてぽんこつさんがScala版を作り発表されました。

6桁数字をMD5で総当たりしてみた

なんかすっかりJavaみたいなコードになっていますが、1秒を切るスピードが出たようです。

noboxとIArrayというArrayライブラリについて | 吉田さん(xuwei_k)

吉田さんが最近作っているArrayのライブラリについて解説してもらいました。

自作のnoboxというライブラリの現状報告 - scalaとか・・・

noboxは、BoxingとUnboxingがパフォーマンスの問題になりやすいということで、プリミティブごとにコード生成するようにしたArrayのライブラリらしいです。

IArrayという名前で、またScalaのImmutableなArrayのライブラリを作った - scalaとか・・・

IArrayは、Scalaz用のArrayライブラリで、常にBoxingをすることでClassTagを使わないようにして、Functorなどの型クラスを作れるようにしたライブラリらしいです。

最近のScalaライブラリ&コンパイラの高速化とバグについて | 吉田さん(xuwei_k)

吉田さんから最近のScalaの細かい高速化と、バグについて紹介してもらいました。

たとえば Avoid generic collections operations hot paths by retronym · Pull Request #3444 · scala/scala ではListのnonEmptyメソッドを使うかわりにne Nilとするなど相当細かい高速化が行われているようです。これで 2% 高速化されるらしいです…。

それと、最近吉田さんがすごいバグを発見されて、

ScalaのHashSetのバグ見つけた - scalaとか・・・

2.11.0-M8で、

scala> Set(-2147483648, 1, -45023380, -1, 1971207058, -54312241, -234243394) - -1
res0: scala.collection.immutable.Set[Int] = Set(-2147483648, 1, -45023380, 1971207058, -54312241, -234243394)
scala> res0 == res0.toList.toSet
res1: Boolean = false

scala> res0.toList.toSet == res0
res2: Boolean = true

というふうに操作するとSetのEqualityが壊れるらしいです。 Hash値の関連のバグに見えますが、これはScalacheckじゃないと発見できなかったでしょうね。Scalacheck重要ですね。


Scala 2.11から導入されるBlackboxMacroとWhiteboxMacro 2013年12月16日

Scala Advent Calendar 2013 - Qiita [キータ]の16日目の記事です。

Scala 2.11から導入されるBlackboxMacroとWhiteboxMacroについて簡単に解説します。

最近出た2.11.0-M7でscala.reflect.macros.ContextがDeprecatedになり、 scala.reflect.macros.BlackboxContextscala.reflect.macros.WhiteboxContextが増えました。

詳しくは以下のドキュメントに書いてあります。

["macros"] - Blackbox Vs Whitebox - Scala Documentation

現在のマクロの型チェックは結構怪しい動作をしているという話で、 そのお陰でImplicitマクロやType Providerもどきのようなテクニックがあるんですが、動作が直感に反してることもあります。

Static return type of Scala macros - Stack Overflow

このStack Overflowの質問はマクロで返値の型指定ができないという話です。

import language.experimental.macros
import scala.reflect.macros.Context

class Foo
class Bar extends Foo { def launchMissiles = "launching" }

object FooExample {
  def foo: Foo = macro foo_impl
  def foo_impl(c: Context): c.Expr[Foo] =
    c.Expr[Foo](c.universe.reify(new Bar).tree)
}

このマクロで返値の型をFooと指定しているのに

scala> FooExample.foo
res0: Bar = Bar@4118f8dd

実際に呼び出すと型がBarになってしまうということです。 マクロの型の指定はあくまでマクロ展開時の型チェックであり、展開後の型には反映されないんですね。

こういう問題は新しく導入されたBlackboxマクロを使うと解消できます。 マクロの型指定がマクロ展開後の型に影響するようです。 Scala 2.11.0-M7以降で以下のようなコードに変更すると、

import language.experimental.macros
import scala.reflect.macros.BlackboxContext

class Foo
class Bar extends Foo { def launchMissiles = "launching" }

object FooExample {
  def foo: Foo = macro foo_impl
  def foo_impl(c: BlackboxContext): c.Expr[Foo] =
    c.Expr[Foo](c.universe.reify(new Bar).tree)
}

ちゃんと型がFooになります。

scala> FooExample.foo
res0: Foo = Bar@c2030ed

しかし、同時にBlackboxマクロにすると今のScalaのマクロで許されている怪しいテクニックも使えなくなります。

Whiteboxマクロは2.10のマクロと同じ動作になります。互換性を考えるとこちらを使いたくなりますが、 現在の予定ではScala 2.12でBlackboxマクロだけになり、Whiteboxマクロ=現行のマクロの動作はとりあえずなくなるようです。

TypeマクロやUntypedマクロの提供をやめた今までのマクロの方針から考えると納得ではあります。 しかし、逆に言うと今のマクロのテクニックは型チェックしないことで許されてるものが多いということもわかりました。 個人的にはマクロはもうちょっと型がゆるくてもいい気がするんですけどねえ。


Scala勉強会第114回 in 本郷 2013年11月08日

Scala勉強会第114回 in 本郷に参加してきました。

Scala勉強会第114回 in 本郷 - PARTAKE

Scala勉強会第114回 in 本郷 #rpscala - Togetter

DbUnitのようなテストツールはScalaにあるか

DbUnitについてあまり詳しくないんですが、データベースのfixtureについての話題ということですかね。

テストのモデルデータ作成というとRubyのFactoryGirlが有名ですが、Scalaにもそれを真似たFactoryPalというがあります。 とTwitterで書いたらseratchさんからリプライを頂き、Skinny FrameworkのSkinny ORMにもFactoryGirlという名前のツールがあり、こちらはDBにデータ投入もできるということでした。

tototoshiさんもScalikeJDBCとPlay用にプラグインを作成したそうです。

scalikejdbc-play-fixture-plugin を作りました - tototoshiの日記

こちらはSQLを直接書いてテストデータの管理をするようです。

tototoshiさんが作ったPHP Parser Combinatorの話題

tototoshi/php-parser-combinator

Scalaの勉強会でPHPの話題なんですが、tototoshiさんはScalaのパーサコンビネータを参考に作ったということで、Scalaと比較して苦労した点などが語られました。 PHPにはパターンマッチがないのでパースされた構文木を変換するのが大変であるということと、名前渡しやlazy valのような処理を遅延させる機能がないので、 再帰的なルールを書くと簡単に無限ループになってしまうということを語られていたと思います。

ScalaのType DebuggerのScaladの話

僕が今年のScala2013で発表があったScaladの紹介をしました。

lampwww.epfl.ch/~plocinic/type-debugger-tutorial/

hubertp/prefuse-type-debugger

hubertp/type-debugger-tutorial

lampwww.epfl.ch/~hmiller/scala2013/resources/pdfs/paper8.pdf

Scalaのコンパイラがどのように型チェックをしているのかということを対話的に調べられるツールです。 面白いツールなんですが、Scala 2.11用であるということと、1年くらいメンテナンスされていないということで、あまり実用的ではない感じです。

どうしても型エラーがわからない場合などにピンポイントで使うような感じですかね。 ちなみに僕はHaskellのライブラリをScalaに移植するときにそういうことがかなり多いので活用していこうかなと思います。

ScalaTest 2.0の話

ScalaTest 2.0が今週リリースされたということであらためてScalaTest 2.0の新機能の話題になりました。

ScalaTest 2.0 Release Notes

これを見ると新機能は非常にたくさんあるんですが、わかりやすいところだとSelenium DSLが便利そうだねーとか、HTMLでテスト結果を出力してくれるんだ、

ScalaTest Results

とか、そんな感じの話でした。

takezoux2さんが作った lift-mapper model generatorのコードを見ながらAngularJSの話

lift-mapper model generator

AngularJSを使ってlift-mapperのモデルを生成するコードみたいです。 このHTMLの中に全部JSのコードが入っているらしいです。

この後AngularJSがいかに便利かという話が長々とあったのですが、さすがに割愛します。 新しい参加者の方がいないときはディープすぎる話題か、Scalaとは関係ない話になりがちですね…。


Play Framework 2.2用のHaxeプラグインとJSXプラグインをリリース&sbt-github-repoのメンテナンス中止 2013年11月02日

Play Framework 2.2用のHaxeプラグインとJSXプラグインをリリースしました。Sonatype OSSにリリースしましたが、Maven Central Repositoryと同期するまで少し時間がかかるかもしれません。

hexx/play-haxe

hexx/play-jsx

これはPlayプロジェクトのapp/assets/javascriptsにHaxeファイルやJSXファイルを放り込んでおくとJSファイルに変換してくれるというものです。 ずっと前に作っていたまま放置していたんですが、 KAWACHI Takashi (kawachi)さん からPRをいただきリリースできました。ありがとうございました。

あとsbt-github-repoのメンテナンスをやめました。と言ってもこれも放置していたタイプなんですが。

hexx/sbt-github-repo

これはGithub PagesをMavenリポジトリにするというsbtプラグインなんですが、Github Pagesがあまり安定しないので僕はもう使わないと思います。Mavenリポジトリを作る場合はAmazon S3にします。 sbt-github-repoは僕が作ったものの中ではそこそこ反響があるほうなんでちょっと残念ですが。


Scala勉強会第113回 in 本郷 2013年10月25日

Scala勉強会第113回 in 本郷に参加してきました。

Scala勉強会第113回 in 本郷 - PARTAKE

残念ながら大幅な電車遅延で半分くらいしか参加できなかったので何をやったかよくわかってないんですが、 たぶん前半はScalaUtilsの話をしたんじゃないかと思います。

ScalaUtils

ScalaTestから主にEqualityだけを取り出したライブラリで、最近サイトができたことで話題になりました。

あとはsbtの内部実装を見たり、 seratch/skinny-framework を触ってみたりということをやっていたと思います。 僕も最後に Scala.js の現状について軽く解説しました

Scala.js - 第113回 Scala 勉強会

ScalazとjQueryを組み合わせた簡単な例もあります。

hexx/scala-js-example

他にもいくつかScala.js関連のリポジトリを紹介します。

sjrd/scala-js-ts-importer

scala-js-ts-importerはTypeScriptの.d.tsファイルをScalaに変換するものですが、現状ほとんど使いものにならないです…。

lihaoyi/scala-js-dom

DOM用のライブラリ。scala-js-ts-importerを使ったけど、相当手を加えたとありますね。

jonas/scalajs-benchmarks

ベンチマーク測定用ライブラリ。

Scala.jsはポテンシャルは高いと思うんですけどね。 周辺ツール、ライブラリがまだまだ足りない感じですね。


怖くないScala勉強会 2013年10月21日

怖くないScala勉強会に参加してきました。

怖くないScala勉強会 - connpass

先月の 歌舞伎座.tech#1 - connpass に続き、二ヶ月連続でScalaの大規模イベントがおこなわれるってすごいですよね。

僕もLTやったんですけど、ちょっとLTには合ってないネタでしたね。

5分でわかるScalaのマクロ - 怖くないScala勉強会

最近書いている Scalaマクロ Tips と一緒に読んでみてください。


Scalaマクロ Tips: マクロを使った静的チェックの事例紹介 2013年10月18日

今回はマクロを使った静的(コンパイル時)チェックの事例についていくつか紹介したいと思います。

前の記事でScalaのマクロを使う用途の一つとして静的チェックの強化を挙げました。 Scalaは静的型付き言語なのでコンパイル時に型のチェックがおこなわれますが、どうしてもチェックできないところもあります。 たとえば文字列の中身は型ではチェックできないので実行時エラーにするということになります。 そういったところをマクロを使ってコンパイル時に調べるという例を見ていきます。

文字列

一番最初に思いつくのは前述したように文字列です。String Interpolationとの組み合わせも考えられます。

まず紹介するのはScalaの標準ライブラリに入っているString Interpolationのfです。 これはよくあるprintf系のフォーマット文字列のString Interpolation版ですが、マクロによって型チェックされるようになっています。 フォーマットと引数の型が合っていないものはコンパイルエラーになります。 詳しくは公式のドキュメントがあるのでそちらを見てください。

文字列の補間 - Scala Documentation

同じように正規表現についてもマクロでのチェックが考えられます。 検索するといくつか出てくると思いますが、吉田さんが記事を書かれているので紹介します。

Scalaでコンパイル時に正規表現をチェックする「マクロかつString Interpolation」 - scalaとか・・・

単にコンパイル時に実行して例外を上げるだけで、結構便利なものになりますね。

同じく吉田さんがマクロでJSONの文字列をjson4sのオブジェクトにするというライブラリを書かれています。 これも文字列に問題があった場合はコンパイル時のエラーになります。

コンパイル時に、 String を lift json に変換する macro - scalaとか・・・

マクロによってSQLに型をつけようというライブラリもあります。

jonifreeman/sqltyped

文字列の静的チェックというのはマクロの有力な使い道の一つでしょう。

Type Dynamic

ScalaにはType Dynamic、もしくは、Dynamicトレイトと呼ばれる仕組みがあります。 これはメソッド呼び出しをメソッド名の文字列に変換することによって、 動的型付け言語でよくあるように、あらかじめ定義していないメソッドでも自由に呼べるようにするという機能です。 特にDSLで使うと便利な機能だと思います。

このDynamicとマクロを組み合わせることによって自由にメソッドを呼べるようにしつつ静的チェックもおこなえるようになります。 このことについてはseratchさんが記事を書かれているので紹介します。ScalikeJDBCでも使われているようです。

Type Dynamic を type safe に扱う方法 - seratch

他にはアクセッサオブジェクトやデリゲートオブジェクトを作るのに使われていることが多いようです。 関数型プログラミングで使われるLensというアクセッサオブジェクトの生成ライブラリがいくつかあるのですが、その元祖と言ってもいいMacrocosmを紹介します。

retronym/macrocosm

MacrocosmはScalaのマクロ誕生初期に生まれたライブラリなのですが、defマクロでできることがわかりやすい形で一通り書かれているのではないかと思います。 僕は一年くらい前にこのライブラリを読んでマクロを勉強しました。

あ、他のLensの実装も吉田さんがまとめられていますね。

ScalaでのLens実装まとめ - scalaとか・・・

このDynamicとマクロの組み合わせは "Poor man's type macros" とも呼ばれているらしいです。 一応マクロで型らしきものを作ることができるということでしょうね。 他にも色々と応用が考えられそうです。

その他

その他の例をいくつか見ますと、AkkaにTyped Channelsというものがあります。

Typed Channels (EXPERIMENTAL) — Akka Documentation

Actorがやりとりするメッセージは通常コンパイル時にはチェックされません。 これをマクロを使って静的にチェックしようというものです。

これも吉田さんがブログで触れられてますね。さすがですね。

northeast scala symposium 2013 の video を見た感想 - scalaとか・・・

あとAltJSのRoyの作者でScala界隈でも関数型系の怖い人の一人として知られているBrian McKennaさんが作られたWartRemoverというlintツールがマクロでも使えるようです。

Scala Code Linting via WartRemover 0.4 - BAM Weblog

puffnfresh/wartremover

Lint系ツールは元のコードに手を加えたくないという都合上、コンパイラ・プラグインとして提供されることが多いと思いますが(たとえば HairyFotr/linter など)、 構文木のチェックの手法はマクロでも応用できるものだと思います。

今回はマクロを使った静的チェックの事例を紹介しました。 defマクロの性質上、コード生成がやりづらく、型が強いということで、この分野はよく発達してきたと思います。 型がなくコード生成がしやすいマクロアノテーションの登場で、これからどう発展していくのかというのも気になるところです。


Scalaマクロ Tips: Typeの取得 2013年10月16日

今回はマクロの中で型を取得する方法について解説したいと思います。型全般だと書くことが散漫になりそうな感じだったので、取得方法に絞ることにしました。

ここで型と言っているのはTypeオブジェクトのことです。 Typeは型の名前や、どんなフィールド、メソッドを持っているかなど色々な型情報を持っているオブジェクトです。 リフレクションでもTypeを起点にフィールドやメソッドのSymbolを操作するような感じで中心的な役割を担っています。

Typeを取得するにはTypeTagを使うのが一般的です。TypeやTypeTagについては公式のドキュメントもあります。

["reflection"] - シンボル、構文木、型 - Scala Documentation

["reflection"] - 型タグとマニフェスト - Scala Documentation

Typeの用途は以下のようなものが考えられます。

  1. 型の情報を取る
  2. 型同士の比較をする
  3. 型の構文木を使う

具体的な用途についてはまた別の機会に解説しますが、特に型変数を使うようなマクロでは重要になると思います。

IntやListなどScalaで定義されている型はtypeOf[Int]typeOf[List[String]]のような感じで簡単にTypeオブジェクトを取得することができますが、型変数を使った場合はWeakTypeTagを使うか、型つけされた構文木からTypeを取得する必要があります。

WeakTypeTagを使う

TypeTag、WeakTypeTagというのはTypeが入っているだけのコンテナです。Typeを取得するためだけに使われます。 TypeTagとWeakTypeTagの違いはTypeTagは具象型に限定されるのに対し、WeakTypeTagは抽象型も入っていることがあります。 マクロで使うのはWeakTypeTagだけになります。 例を見てみますと、

import scala.reflect.macros.Context

object WTypeTag {
  def wtypetag[T](t: T) = macro impl[T]

  def impl[T: c.WeakTypeTag](c: Context)(t: c.Expr[T]) = {
    import c.universe._
    c.literal(weakTypeOf[T].toString)
  }
}
object Main extends App {
  def f[T](t: T) = WTypeTag.wtypetag(t)
  println(WTypeTag.wtypetag(123))
  println(f(123))
 }

weakTypeOf[T]というのはimplicitly[WeakTypeTag[T]].tpeと同じです。WeakTypeTagオブジェクトはコンパイラが自動で生成してくれるので、implicit parameterにするだけで取得することができます。

注意すべきところは上で述べたようにTypeTagは具体的な型情報が入っていることが保証されていますが、 WeakTypeTagはそうではないというところです。上のコードを実行すると、

Int
T

と表示されます。直接マクロにIntリテラルを渡した場合は具体的なInt型を取得できていますが、 型変数を持つメソッドを経由すると型情報が失われています。 マクロがコンパイル時に展開されることを考えれば当然なんですが、 型の情報を調べようと思うと落とし穴になるかもしれないので気をつけてください。

型つけされた構文木から型を取得する

型つけされた構文木からもTypeオブジェクトを取得することができます。defマクロの引数は型つけされているので直接Typeを取得することができます。

import scala.reflect.macros.Context

object TypeFromTree {
  def typeFromTree[T](t: T) = macro impl[T]

  def impl[T](c: Context)(t: c.Expr[T]) =
    c.literal(t.actualType.toString)

  def intListTypeImpl(c: Context) = {
    import c.universe._
    c.literal(c.typeCheck(q"List(1, 2, 3)").tpe.toString)
  }
}
object Main extends App {
  def g[T](t: T) = TypeFromTree.typeFromTree(t)

  println(TypeFromTree.typeFromTree(123))
  println(g(123))

  println(TypeFromTree.intListType)
}

実行すると以下のような結果になります。

Int(123)
T
List[Int]

t.actualTypeで型を取得できます。これはt.tree.tpeと同じです。 型変数に対して具体的な型を取れないのはWeakTypeTagと同じです。 ちなみにt.staticTypeというのもありますが、これはweakTypeOf[T]と同じです。 actualTypeはリテラルに使うとWeakTypeTagより詳細な型を取れますが、基本的にそんなに違いはないと思います。

ちょっと変わった型の取得方法としては構文木を自分で型チェックして取得する方法があります。 c.typeCheck(q"List(1, 2, 3)").tpeの部分がそうですね。 あまり使わないと思いますが型つけされた構文木からは型が取得できると覚えておけばいいと思います。

今回はTypeオブジェクトの取得方法について解説しました。このあたりはScala 2.10以降で大きく変わったところで、取得できる情報が非常に増えています。 リフレクションがスレッドセーフではないということでちょっと使いづらいのですが、マクロはそういう問題はないので、活用していきましょう。


Scalaマクロ Tips: Enclosing 2013年10月15日

今回はマクロの周辺の情報を取得できるEnclosingについてです。

Scalaのマクロはマクロが使われた場所の位置や、どのクラスで使われたか、どのメソッドで使われたかなどの情報を取得することができます。 それらはscala.reflect.macros.Contextのenclosingを頭に持つメソッドから取得することができます。

enclosingPosition

enclosingPositionはマクロが使われたソースコード上の位置を教えてくれます。

// Enclosing.scala
package com.github.hexx

import scala.reflect.macros.Context

object Enclosing {
  def enclosingPosition = macro enclosingPositionImpl

  def warning = macro warningImpl

  def enclosingPositionImpl(c: Context): c.Expr[(Int, Int)] = {
    import c.universe._
    val p = c.enclosingPosition
    reify((c.literal(p.line).splice, c.literal(p.column).splice))
  }

  def warningImpl(c: Context): c.Expr[Unit] = {
    c.warning(c.enclosingPosition, "Don't mind this message.")
    c.literalUnit
  }
}
// Main.scala
import com.github.hexx._

object Main extends App {
  println(Enclosing.enclosingPosition)
  println(Enclosing.warning)
}

これを実行すると(5,21)と出力されます。これは5行目の21文字目ということですね。 ちょうどMain.scalaのenclosingPositionが使われた位置になっています。 普通はあまり使い道がないと思うのですが、コンパイル時のチェックでエラーや警告を出すときに使います。

このコードをsbt上でcleanしてcompileしてみると、

[warn] /home/seitaro/git/scala-macro-tips/src/main/scala/Main.scala:6: Don't mind this message.
[warn]   println(Enclosing.warning)
[warn]                     ^
[warn] one warning found

という警告が出ます。Enclosing.warningのメッセージです。 Scalaのマクロの用途として静的(コンパイル時)チェックの強化があるのですが、 そのときにabortやwaringと一緒に使います。

enclosingClass, enclosingMethod, enclosingUnit

enclosingClass、enclosingMethod、enclosingUnitは、それぞれマクロが使われたクラス、メソッド、ソースコードのファイルの構文木を取得することができます。

// Enclosing.scala
object Enclosing {
  def enclosingTrees = macro enclosingTreesImpl

  def enclosingTreesImpl(c: Context): c.Expr[(String, String, String)] = {
    import c.universe._
    val k = c.literal(c.enclosingClass.toString)
    val m = c.literal(c.enclosingMethod.toString)
    val u = c.literal(c.enclosingUnit.body.toString)
    reify((k.splice, m.splice, u.splice))
  }
}
// Main.scala
import com.github.hexx._

object Main extends App {
  def method = Enclosing.enclosingTrees
  val (classTree, methodTree, compileUnitTree) = method
  println(classTree)
  println(methodTree)
  println(compileUnitTree)
}

このコードを実行すると

object Main extends App {
  //中略
}
def method = Enclosing.enclosingTrees
package <empty> {
  import com.github.hexx._;
  object Main extends App {
    // 中略
  }
}

みたいな出力になります。それぞれマクロの中からクラス、メソッド、ソースコードのファイルの構文木が取得できているのがわかると思います。

enclosingImplicits

enclosingImplicitsはマクロをimplicitとして定義して使われたときに、その呼び出された構文木を取得できることができます。 言葉で言っても難しいのでコードで説明します。

package com.github.hexx

import scala.reflect.macros.Context

object Enclosing {
  implicit def enclosingImplicits = macro enclosingImplicitsImpl

  def enclosingImplicitsImpl(c: Context): c.Expr[String] = {
    c.literal(c.enclosingImplicits.mkString)
  }
}

まずimplicitとしてマクロを定義します。

object Main extends App {
  import com.github.hexx.Enclosing._
  def method(i: Int)(implicit s: String) = println(s)
  method(123)
}

そして、このマクロをimplicit parameterとして呼び出されるように使います。 このコードを実行すると(String,Main.this.method(123))と表示されます。 つまりimplicit parameterの側から普通の引数などにアクセスできるわけです。 なかなかマニアックな機能だと思いますが、マクロでしかできないことができそうな気がします。

今回はEnclosingについて説明しました。 局所的なマクロから普通の関数では知りえないような色々な情報を取得できるのがわかったと思います。 活用すると力技で色々なことができそうな気がしますね。

今回説明した内容のサンプルコードは

https://github.com/hexx/scala-macro-tips

にあります。

次はWeakTypeTagあたりですかね。


Scalaマクロ Tips: Prefix 2013年10月14日

まだまだ使っている人が少ないと思われるScalaのマクロについて、何回かにわけてTipsを書いていきたいと思います。

最初はPrefixについてです。

Scalaのマクロはコンパイル時に対象の構文木(引数で渡されるものやアノテーションが付けられたもの)を変換して別の構文木にするわけですが、その対象の構文木以外にも様々な情報を取得することができます。 今回解説するPrefixはどういう形でマクロが呼び出されたかという情報を取得することができます。

Prefixについて三つのマクロを作ってみました。

package com.github.hexx

import scala.reflect.macros.Context

trait Prefix {
  def prefix = macro Prefix.prefixImpl
  def prefixTree = macro Prefix.prefixTreeImpl
  def prefixHello = macro Prefix.prefixHelloImpl
}

object Prefix {
  def prefixImpl(c: Context): c.Expr[Any] = {
    import c.universe._
    c.prefix
  }

  def prefixTreeImpl(c: Context): c.Expr[String] = {
    import c.universe._
    c.literal(showRaw(c.prefix.tree))
  }

  def prefixHelloImpl(c: Context): c.Expr[String] = {
    import c.universe._
    c.Expr[String](q"${c.prefix.tree}.hello")
  }
}

それぞれ使ってみましょう。

class Person(name: String, age: Int) {
  def hello = s"Hello, $name"
}

object Main extends App {
  val p = new Person("hogeika", 13) with Prefix

  println(p == p.prefix)
  println(p.prefixTree)
  println(p.prefixHello)
}

これを実行すると、

true
Select(This(newTypeName("Main")), newTermName("p"))
Hello, hogeika

と出力されます。つまりマクロのPrefixというのは普通のメソッドの中のthisのように使うことができるというのがわかると思います。 じゃあマクロでもthisと書いたらいいのではないかと思われるかもしれませんが、このマクロでthisと書いてしまうとpではなくobject Mainのほうが参照されてしまいます。 なのでマクロの中でthisのようなものを使いたい場合はPrefixの出番になるというわけです。

注意するべき点はPrefixはあくまで呼び出された構文木を指すということです。たとえば(new Person("hogeika", 13) with Prefix).prefixTree

Block(List(ClassDef(Modifiers(FINAL), newTypeName("$anon"), List(), Template(List(Ident(Person), TypeTree().setOriginal(Select(Select(Select(Ident(com), com.github), com.github.hexx), com.github.hexx.Prefix))), emptyValDef, List(DefDef(Modifiers(), nme.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(Apply(Select(Super(This(newTypeName("$anon")), tpnme.EMPTY), nme.CONSTRUCTOR), List(Literal(Constant("hogeika")), Literal(Constant(13))))), Literal(Constant(())))))))), Apply(Select(New(Ident(newTypeName("$anon"))), nme.CONSTRUCTOR), List()))

というそのままの複雑な構文木に変換されます。通常はどのような形でマクロが呼び出されるかはわからないので、Prefixの構文木自体を解析することはせずに、Prefixを丸ごと使うようにしたほうがいいと思います。

今回はPrefixの解説をしました。まだまだマクロから取得できる情報は色々あるので次回以降で紹介していきたいと思います。

今回の記事のそのまま動作するコードは

https://github.com/hexx/scala-macro-tips

にあります。


Scala勉強会第112回 in 本郷 2013年10月10日

rpscalaことScala勉強会第112回 in 本郷に参加してきました。

Scala勉強会第112回 in 本郷 - PARTAKE

Scala勉強会第112回 in 本郷 #rpscala - Togetter

参加者は9人? いつもと同じように芸者東京エンターテインメント株式会社様の会議室でおこなわれました。

sbt 0.13 を用いた四次元空間内の移動 | eed3si9n

いつも神懸かったテキストを書いているYokotaさんなんですが、新しくsbtの設定値のスコープについてドキュメントを書かれました。 でも僕が読んでもちょっとよくわからなかったので、吉田さんや高橋さんに細かいところを色々と伺いました。 こういう定期的に集まって色々訊ける場があるのはいいですよね。

CRDTs - 第112回 Scala 勉強会

僕の発表です。スライド中にもありますが、Akkaの作者でTypesafe CTOのJonas Bonerさんが一ヶ月くらい前におそらくプライベートで書かれていたと思われる jboner/akka-crdt というのを公開されて、面白そうかなと思って、CRDTの論文とBonerさんのコードを読んで軽く解説するということをやりました。

今度おこなわれる 怖くないScala勉強会 - connpass のLTの練習のつもりで、わかりやすいところから順番に話すような資料にしようと思ったんですが、内容もスライドもあまりrpscala向けっぽくなかったかなと思いました。

- Activate Persistence Framework

RailsのActive Recordのようなフレームワークみたいなんですが、高橋さんが見つけてきてみんなでドキュメントを読むということをやりました。PlayプラグインではPlayのフォーム・コンストラクタで直接データを入出力できるなど、かなり気合を入れて作られているのは感じました。

takezoe/gitbucket を触る

Hacker NewsにとりあげられてGithubのスターが1000個を突破するという大ブレイク中のGitbucketですが、吉田さんがHerokuにデプロイしたものがあったので、あらためてみんなで触ってみました。 本当にちょっと前のGithubにそっくりすぎて動揺が走ってましたね。間違えてGithubのキーボードショートカットを押してしまうとか。Gitbucketを触っていたつもりが間違って本物のGithubを触っていたとか。 超有名人の竹添さんがまたあらためてScalaで世界的なソフトを作ったというのは本当にすごいと思いますね。


Coq'Art読書会あらためSF読む会 2013年10月06日

「Coq'Art読書会あらためSF読む会」に参加してきました。

Coq'Art読書会あらためSF読む会 #1 #readcoqart - PARTAKE

最初『Coq'Art』という本を読む予定だったらしいのですが、まずもっと初歩的な『ソフトウェアの基礎』を読む勉強会になったようです。 僕はCoqを全然知らないので『Coq'Art』を読むのは厳しいかなあと思って二の足を踏んでいたのですが、『ソフトウェアの基礎』は僕でも理解できそうかなと思って参加することにしました。

この『ソフトウェアの基礎』ですが、大変素晴らしいテキストですね。

sfja/sfja

元のテキストが非常に初歩的なところからやってくれているのに加えて、名古屋方面の方が翻訳されており、とても読みやすかったです。 Coqに詳しい人が集まっているので、時折入る解説も大変参考になりました。 今回は Basics_J.v の3分の2くらいをやりました。データ型と関数の定義の仕方、destruct、inductionを使った証明あたりまで。

Proof Generalも簡単な操作はすぐに覚えられました。 ただ、ddskkとccc.elというファイル名がかぶっているらしく、同時に読み込むとエラーになりました。 たぶんCoqを使うだけならccc.elは使わないと思うので削除して対応しました。

僕みたいにCoqが全然わからない人でも参加できる勉強会だと思うので、参加してみるといいと思います。


プログラマの為の数学勉強会 2013年10月04日

最近「プログラマの為の数学勉強会」という勉強会に参加させていただいています。

プログラマの為の数学勉強会 第4回 - PARTAKE

前に「圏論勉強会」をやられていた東大の博士2年の中村晃一さんが引き続き、今度は『パターン認識と機械学習』あたりを読めるようになるために大学数学の基礎を教えてくださるという勉強会です。

僕みたいに圏論勉強会から参加している人もいますが、だいぶ人が入れ替わってる気がしますね。まあ、圏論は数学系や情報系の大学院でもやるかやらないかみたいな内容なんですが、今回は理系の大学なら一年のときに必ずやるだろうという内容なので、一気に難易度が変わった感じはありますね。

僕も一応情報系の修士なんですが、プログラミング以外の数学とか物理は一通り苦手だった覚えがありますね。入った研究室が計算モデルの研究をやっていたので論理、代数あたりは強くなったのですが、解析、線形代数、確率統計などはいまだに苦手意識が強いですね。いい機会なのでその苦手意識を克服しておこうかなと思って参加しています。

ScalaもSpireというScalazやShapelessと並んで紹介される数値ライブラリがあるんですよね。

typelevel.scala | Powerful libraries for Scala

non/spire

知識が身についたらこのへんで遊んでみたい気がしますね。


ブログ引越し&再開 2013年10月03日

ブログ引越し&再開しました。前に使っていたTumblrも好きだったけど、HTMLの編集がやりづらすぎてMiddlemanで作ることにしました。

Middleman: Hand-crafted frontend development

Middlemanいいですね。自分で作りたいところと規約で適当にやってほしいところのバランスが良いです。 Bootstrap使ってSlimでレイアウト書いてmarkdownで記事を書くという、いかにもエンジニアが楽に作った感じになっていますが、まあ楽なのはいいです。一応ソースも上げておきます。

hexx/blog

最初はGithub Pagesで公開しようと思っていたのですが、あまり更新などがうまくいかなかったのでS3を使うことにしました。ドメイン取ったりして面倒くさかったです。

ブログの内容は相変わらずScalaのこととか、行ったイベントのこととか、関数型の話が中心になると思います。 とりあえずはScalaのマクロの話とかScala.jsとか型関連のことなどを書くと思います。

今後ともよろしくお願いします。