Haxeで仕方なくnullと付き合う

Haxeは現時点で選択しうるaltJSの中では型システムが一番出来が良く、代数的データ型(Haxeではenum)が扱える点が素晴らしい。しかし、元々はFlashを前提とし、今はマルチターゲット(JavaScriptPHPC++C#等)にコンパイルする言語として設計されているため、どうしてもnulを扱わなければならない。

これはHaxeに限った話ではなく、F#やScalaなどでも同様の問題があるのだが、これらの言語と比較すると、Haxeはすこしnullが全面に出てしまっている(むしろnullを許容する言語設計にすることで、学習時の敷居を下げているのではないかとも思えるが…)ので、nullを根絶したい勢にすると、ちょっとぐぬぬとなる面がある。

少し前置きが長くなったが、Haxeで仕方なくnullを付き合うための方法の1つを書いてみる。

そもそも、nullを使わないコードとは?

Option型(言語によってはMaybe型)を使うコードである。Option型とは、「値が存在しない可能性があるデータ」を表現するための型である。

最近普及期に入ってきた型システムがリッチな言語(Scala、F#、OCamlHaskell等)では当たり前に利用できる。

要は「nullの代わりに使いましょう」という型なのだが、nullのと大きな違いは、型安全である(「いわゆるnullチェック」にあたるコードを書かないとコンパイルすら通らない)という利点がある。nullを許容する場合だと、nullチェックを書かなくてもコンパイルが通ってしまうため、ケアレスミスによるバグが混在してしまう可能性がある。

蛇足ではあるが、リッチな型システムを使うメリットは「計算機がチェックできる範囲を広げることで、人間が楽をできる」ことであるので、食わず嫌いをして使わないのは勿体がない。

HaxeのOption型

Haxe 3.0から、haxe.ds.Optionという、まさにそのものが標準ライブラリとして用意された。(ちなみにEitherはない。)

Haxeでnullを使わざるをえないところ

大きく2つある。

1. デフォルト引数

Haxeは関数やメソッドにデフォルト引数が設定できるのだが、デフォルト引数にOption型を指定することができない。そのため、どうしても次のような定義を書かざるをえないことがある。

function foo(option: String = null): Void { ... }
2. 環境依存や外部ライブラリを利用するケース

JavaScriptでDOMやjQueryなどを扱わなければならない場合に多発する。

var elem = js.Browser.document.querySelector("#hoge");
// valueにはnullが入る可能性
function setProperty(name: String, value: String): Void {
    var node = new JQuery("#hoge");
    node.prop(name, value);
}

これらの他にも、コールバック関数で受け取る値がnullというようなケースも多々ある。

ちょっとした工夫をする

まず次のようなHelperを作成する。ScalaのOption型まんまである。

class OptionHelper {
    public static function create<T>(x: T): Option<T> {
        return (x != null) ? Some(x) : None;
    }

    public static function getOrElse<T>(a: Option<T>, b: T): T {
        return switch (a) {
            case Some(x): x;
            case None: b;
        }
    }
}

これだけあれば、結構いける。どうしてもコーディング規約的なものになってしまうが、無いものは仕方がない。

function foo(rawOption: String = null): Void {
    var option = OptionHelper.create(rawOption);
    ...
}
using OptionHelper; //using mixin

function setProperty(name: String, value: Option<String>): Void {
    var node = new JQuery("#hoge");
    node.prop(name, value.getOrElse(""));
}

OptionHelperにmap()等のメソッドを追加すると更に便利である。本当はこのあたりを全て標準ライブラリで用意してもらいたい…。

IntelliJ IDEA 13のJVM設定

初期値のままだと結構IntelliJスワップするので、idea.exe.vmoptionsをこんな感じにしてある。

-server
-Xms512m
-Xmx2048m
-XX:MaxPermSize=512m
-XX:ReservedCodeCacheSize=128m
-ea
-Dsun.io.useCanonCaches=false
-Djava.net.preferIPv4Stack=true
-XX:+UseCodeCacheFlushing
-XX:+UseConcMarkSweepGC
-XX:SoftRefLRUPolicyMSPerMB=50

Windows用のPostgreSQL 9.3でUUIDを使えるようにする

シーケンスの代わりにuuidをIDとして使う | Siguniang's Blogを見ながら必要なことだけをメモ。

uuid-osspを有効にする

Windows用のPostgreSQLには最初からライブラリが同梱されているので、有効化する。

CREATE EXTENSION "uuid-ossp";

UUIDを生成する

v1, v3, v4, v5が使えるらしいけど、実質v1とv4ぐらいしか使わないよね。

select uuid_generate_v1();
select uuid_generate_v4();

UUIDをプライマリキーにしたテーブルを作る

CREATE TABLE sample (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    value text
)

Windows 8.1上にScalatra開発環境を作る

基本手順は公式ページに書いてある通りだが、Windowsだと多少面倒が発生する。

JDKのインストール

OracleのページからJDK 7u51をダウンロードしてインストール。

Conscriptのインストール

Conscriptとは、GitHubでホストされているScalaプログラムの管理(インストール/アップデート)を行うためのツールのようだ。GitHubにアクセスする関係上、Gitコマンドに依存しているらしい(未確認)ので、予めGit for Windows等をインストールしないとダメかもしれない。

Windowsでインストールする場合は、ConscriptのGitHubページからダウンロードできるJava製のインストーラを利用する。おそらくjarをダブルクリックでインストーラが起動できるはずだが、インストーラが起動しない場合は、次のようなコマンドを実行する。

$ java -jar conscript-0.4.4.jar

インストール処理が完了しても、渋いインストーラ画面は表示されっぱなしなので注意が必要である。なお、%USERPROFILE%\bin に対してPATHが通ってない場合、インストール完了時に次のような画面が表示される。

f:id:terurou:20140221184751p:plain

このような画面が出た場合は、PATHに %USERPROFILE%\bin を通しておく。

giter8のインストール

giter8はテンプレートからScalaプロジェクトのスケルトンを作成するツールのようだ。

インストールは次のコマンドを実行するだけで良い。

$ cs n8han/giter8

sbtのインストール

次のsbtのダウンロードページから、MSIをダウンロードしてきてインストール。

Setup — sbt Documentation

scalatraプロジェクトの作成

適当なディレクトリに移動して、次のコマンドを実行する。

$ g8 scalatra/scalatra-sbt

プロジェクトのひな形を作るための設定の入力を求められるので、適当に入力するとプロジェクトが作成される。設定内容についてはFirst steps | Scalatraを参照。

プロジェクトが作成されたら、ルートディレクトリ内に、次の内容で sbt.bat ファイルを作成する。

@echo off
java -Dinput.encoding=Cp1252 -Xms512M -Xmx1536M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256M -jar "%SBT_HOME%bin\sbt-launch.jar" %*

scalatraプロジェクトの起動

次のコマンドを実行すると、 http://localhost:8080/ で作成したscalatraアプリにアクセスできる。sbt初回起動時は依存ライブラリのダウンロードで非常に長い時間待たされるので注意…。

$ sbt
> container:start

また、container:start した後で、sbt上で次のコマンドを実行すると、ファイルを編集した際に自動コンパイルしてくれるようになる。

> ~ ;copy-resources;aux-compile

IDE用の設定

EclipseIntelliJEmacs用のプロジェクトファイルを出力することができる。また、EclipseIntelliJではデバッグも可能になる。詳細は次のページを参照。

IDE Support | Scalatra

Scalatra + ScalaTestでunit test

公式ドキュメントの記載通りに設定すれば動く。

ScalaTest | Testing | Scalatra guides

設定

project/build.scalaのlibraryDependenciesに、次を追加。

"org.scalatra" %% "scalatra-scalatest" % "2.2.2" % "test"

テストコード

package test.app

import org.scalatra.test.scalatest._
import org.scalatest.FunSuite

class TestServletTests extends ScalatraSuite with FunSuite {
  addServlet(classOf[TestServlet], "/*")

  test("simple get") {
    get("/") {
      status should equal (200)
      body should include ("Hello")
    }
  }
}

テスト実行

$ sbt
> test

特定のテストだけ動かしたい時は、次のようにする。

$ sbt
> test-only org.acme.RedSuite org.acme.BlueSuite
> test-only *RedSuite

細かい点については、ScalaTestのページを参照。

ScalaTest

Debian wheezy(Debian 7.4)にPostgreSQL 9.3(最新版)をインストール

公式ページの手順通り。

PostgreSQL: Linux downloads (Debian)

PostgreSQLのaptリポジトリを追加

/etc/apt/sources.list.d/pgdg.list を作成して、次の通り記述する。

deb http://apt.postgresql.org/pub/repos/apt/ wheezy-pgdg main

リポジトリのキーを追加して、パッケージリストを更新。

$ wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
$ sudo apt-get update 

PostgreSQL 9.3をインストール

$ sudo apt-get install postgresql-9.3