Play2.0でセレクトボックスにEnumを貼れるようにする

スタプラわー!(挨拶) 第8回全国戦優勝者です(本当)
携帯ゲームって初めてやったけど運営が伏せたルールをプレイヤーが当てる遊び的な感じですね。
折角攻略法見つけても「あれ不具合だから修正しました」て言われる恐ろしい展開もある。


閑話休題、"Play 2.0"もとい"Playframewok 2.0"もとい"Play framework 2.0"で*1、勉強も兼ねて自分用のWebアプリをちょこちょこ作ってるんだけど、いろいろプリミティブな感じで歯がゆい。

例えば、データベースの値をフォームに貼付けるのにいろいろ準備が面倒だと思う。computer-databaseサンプルのComputer編集フォームでセレクトボックスでCompanyを選択する場合で説明すると、次のような感じになる。

  1. ケースクラスComputerにOption[Long]のcompanyIdというフィールドを用意する
  2. Computer編集用のフォームcomputerFormのマッピング"company" -> optional(longNumber)というマッピングを加える
  3. ケースクラスCompanyのコンパニオンオブジェクトにoptionsというメソッドを用意して、DBから取得したCompanyオブジェクトを(IDの文字列、名称)のペアを作ってSeq[(String,String)]として戻すようにする。
  4. テンプレート上で以下のようにヘルパーを呼び出す。
            @select(
                computerForm("company"), 
                Company.options, 
                '_label -> "Company", '_default -> "-- Choose a company --",
                '_showConstraints -> false
            )

どの辺がいけてないなーと思うかというと、UI部品であるselectが要求するSeq[(String,String)]をモデルオブジェクト側で用意しているところと、そうやって用意しておいても、結局フォーム受信時にはLongとして受け取っていて、選択されたCompanyの内容を利用しようとするとDBから取得し直さないところ。

このselectを一つの再利用可能な部品としてみた時に、普通に考えれば「Companyのリストを渡したら、ユーザが選択したCompanyが取得出来る」というのが一番便利だと思うんだけど、そんな風に作れないものなのか。

ということで練習としてセレクトボックスにEnumを貼って、選択されたEnumを受け取れるようにしてみた。

Enum(列挙型)およびFormatterの定義

衣装データを形状と色とで表現したい。形状は文字列で入力するとして、色はあらかじめ決められた8色から選択するようにする。

package models

import Color._

case class Costume(shape:String, color:Color)
package models

object Color extends Enumeration {
    type Color = Value
    val Black = Value("黒")
    val White = Value("白")
    val Brown = Value("茶")
    val Green = Value("緑")
    val Yellow = Value("黄")
    val Red = Value("赤")
    val Purple = Value("紫")
    val Blue = Value("青")
}

これを、フォームの値として持てるようにしたい。Formのmappingで扱えるようにするには、bindとunbindを定義したFormatterをその型用に用意する。

package data.format

import scala.util.control.Exception.allCatch

import models.Color
import models.Color._
import play.api.data.format.Formats.intFormat
import play.api.data.format.Formatter
import play.api.data.FormError

object ColorFormat {
    implicit def colorFormat: Formatter[Color] = new Formatter[Color] {
    
        override val format = Some("format.color", Nil)
    
        def bind(key: String, data: Map[String, String]): Either[Seq[FormError], Color] = {
            for ( n <- intFormat.bind(key, data).right;
                 c <- allCatch.either(Color(n)).left.map(_ => Seq(FormError(key, "error.range", Nil))).right)
                yield c
        }

        def unbind(key: String, value: Color) = Map(key -> value.id.toString)
    }
}

フォーム上での値としてはidを文字列化したものを使う。bindでは文字列→Colorの変換を、unbindではColor→文字列の変換を行う。

このFormatterを使って、例えば文字列とColorの二つのフィールドを持つフォームは次のように書ける。

import models.Costume
import models.Color._

import play.api.data.Forms._
import play.api.data.Form
import data.format.ColorFormat.colorFormat  // colorFormatをimportしておくと

...
    val sampleForm = Form(
        mapping(
            "shape" -> text,
            "color" -> of[Color]            // implicitなFormatter[Color]として利用される
        )(Costume.apply)(Costume.unapply)
    )

これで、サーバ側ではColorとして扱い、HTML側ではidの文字列として扱えるようになった。

フォームヘルパーの定義

基本的にはselect.scala.htmlを参考に作りたいけど、HTMLテンプレート上で型パラメータが取れないようなのでScalaで書き下す。

package views.html.helper

import play.api.data.format.Formatter
import play.api.data.Field
import play.api.templates.Html
import play.api.templates.HtmlFormat._
import play.api.templates.PlayMagic._

object dataselect {
    def apply[A](field: Field, options: Seq[A], args: (Symbol,Any)*)(f: A => Html)
            (implicit handler: FieldConstructor, lang: play.api.i18n.Lang, binder: Formatter[A]): Html = {
        // 標準のフィールドコンストラクタで装飾
        input(field, args:_*) { (id, name, value, htmlArgs) =>
            // 開始タグ
            Html("""<select id="%s" name="%s" %s>""".format(escape(id), escape(name), toHtmlArgs(htmlArgs)) +
            // 空値用の選択肢があれば表示
            args.toMap.get('_default).map { defaultValue =>
                """<option class="blank" value="">%s</option>""".format(escape(defaultValue.toString()))
            }.getOrElse("")) + 
            // options分optionタグを繰り返す。中身のレンダリングは引数で受け取った関数でおこなう
            options.map { (a:A) =>
                binder.unbind("_tmpkey_", a).get("_tmpkey_").map {v => 
                    Html("""<option value="%s" %s>""".format(v, if(value == Some(v)) "selected" else "")) +
                    f(a) +
                    Html("""</option>""")
                }.getOrElse(Html(""))
            }.reduce(_ + _) +
            // 閉じタグ
            Html("""</select>""")
        }
    }
}

implicitでスコープ上で定義されたFormatterを取得し、選択肢に対応する値を取り出している。*2 *3 また、optionの中身は関数を渡してレンダリングするようにしている。

これを使って、セレクトボックスを含むフォームを書ける。

@(sampleForm:Form[Costume])
@import helper._
@import views.html.helper.dataselect
@import data.format.ColorFormat.colorFormat

    @form(routes.Sample.update()) {
        @inputText(sampleForm("shape"), '_label -> "衣装の形状")
        @dataselect(sampleForm("color"), Color.values.toSeq, '_label -> "衣装の色") { color =>
          @color色
        }
        <input type="submit" value="送信" />
    }

これで、「黒色」〜「青色」のセレクトボックスが表示され、選択した値がColorとしてフォームから取り出せるようになる。

備考

基本的な問題意識としては

と同じですわ。進歩が無い……

*1:検索しにくいんだよksg

*2:本当はFormのmappingsから取得出来れば良いんだけど、当該フィールドのmappingを型とともに取り出す方法が分からなかった。

*3:変換失敗時は例外を投げるべきな気がするが、何例外が適当か分からないのでスキップしている

Play2.0でURLに一律でパラメータを付ける

"Play 2.0"もとい"Playframewok 2.0"もとい"Play framework 2.0"です。検索しにくいんだよksg。
使ったバージョンは2.0.3です。
あまり良いやり方じゃない気がするので、良いやり方があれば教えて下さい。

目的

Play 2.0でHTMLテンプレート上でアクションを呼び出すリンクを生成する際に、リバースルーティングを使ってとてもDRYで書けて良いと思う。*1
下はサンプルのcomputer-databaseの例。
ルーティング設定ファイル(conf/routes):

...
# Edit existing computer
GET     /computers/:id              controllers.Application.edit(id:Long)
POST    /computers/:id              controllers.Application.update(id:Long)
...

HTMLテンプレート(app/views/list.scala.html):

...
                    <td><a href="@routes.Application.edit(computer.id.get)">@computer.name</a></td>
...

HTMLテンプレート上で@routes.Application.edit(computer.id.get)のように関数呼び出しの形で書くと、routesファイルの定義に従ってURLを生成してくれる。URLを変更したいときは、routeファイルだけ直せばHTMLテンプレートは直すが必要ない。とてもDRYでよい。


ここで、URLに一律で特定のパラメータ(例えばguid=ONなど)を付けたいなあと思ったんだけど、スマートなやり方が思い浮かばなかった。
ダーティーなやり方が以下になります。

一律でパラメータを付加する(ダーティーな方法)

まず、URLの生成に使用されるクラスであるplay.api.mvc.Callを偽装するオブジェクトを定義する。パッケージ名は何でも良いけど、名前は「Call」である必要がある。
app/api/mvc/Call.scala:

package api.mvc

import play.core.parsers.FormUrlEncodedParser

object Call {
   def apply(method: String, url: String) = {
      val params = FormUrlEncodedParser.parse(url.split('?').drop(1).mkString("?"));
      if (params.isDefinedAt("guid")) // 追加済み
         new play.api.mvc.Call(method, url)
      else
          if (params.isEmpty)
             new play.api.mvc.Call(method, url + "?guid=ON") 
          else
             new play.api.mvc.Call(method, url + "&guid=ON")
     }
  }

次に、プロジェクトファイルで、ルーティングファイルのコンパイル(ソースコード生成)時に追加するimport先に、上記のオブジェクトを読み込む設定を追加する。
project/Build.scala

...
    val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA).settings(
       // Add your own project settings here
       routesImport ++= Seq("api.mvc.Call") // この行を追加
    )
...

ここでconf/routesファイルを更新しておく。
playでプロジェクトファイルを再読み込み or 再起動

[computer-database] $ reload
[info] Loading project definition from C:\Projects\repos\Play20\samples\scala\computer-database\project
[info] Set current project to computer-database (in build file:/C:/Projects/repos/Play20/samples/scala/computer-database/)
[computer-database] $ run

--- (Running the application from SBT, auto-reloading is enabled) ---

[info] play - Listening for HTTP on port 9000...

(Server started, use Ctrl+D to stop and go back to the console...)

トップページを見てみると...例えば「Computer name」と「ACE」のリンクのURLが

Before
/computers?s=-2
/computers/381
After
/computers?s=-2&guid=ON
/computers/381?guid=ON

ちゃんとついている。

解説

リバースルーティングは、routeファイルを元にリバースルーティング用のクラスを自動生成して実現してるみたい。
「target/scala-2.9.1/src_managed/」の下にソースコードが生成されている。
target/scala-2.9.1/src_managed/main/controllers/routes.java:

// @SOURCE:C:/Projects/repos/Play20/samples/scala/computer-database/conf/routes
// @HASH:d3cc52ad69d3a86d9c774c3c45e645163f27e021
// @DATE:Sun Sep 23 21:32:28 JST 2012

package controllers;

public class routes {
public static final controllers.ReverseApplication Application = new controllers.ReverseApplication();
public static final controllers.ReverseAssets Assets = new controllers.ReverseAssets();
public static class javascript {
public static final controllers.javascript.ReverseApplication Application = new controllers.javascript.ReverseApplication();
public static final controllers.javascript.ReverseAssets Assets = new controllers.javascript.ReverseAssets();    
}   
public static class ref {
public static final controllers.ref.ReverseApplication Application = new controllers.ref.ReverseApplication();
public static final controllers.ref.ReverseAssets Assets = new controllers.ref.ReverseAssets();    
} 
}
                            

target/scala-2.9.1/src_managed/main/routes_reverseRouting.scala:

// @SOURCE:C:/Projects/repos/Play20/samples/scala/computer-database/conf/routes
// @HASH:d3cc52ad69d3a86d9c774c3c45e645163f27e021
// @DATE:Sun Sep 23 21:32:28 JST 2012

import play.core._
import play.core.Router._
import play.core.j._

import play.api.mvc._

import Router.queryString
...
package controllers {
...
class ReverseApplication {
...
// @LINE:16
def edit(id:Long) = {
   Call("GET", "/computers/" + implicitly[PathBindable[Long]].unbind("id", id))
}

アクションやテンプレートからroute.Application.メソッド名(パラメータ)と呼び出すと、routes.javaで定義されたApplicationというstaticなフィールド(中身はroutes_reverseRouting.scalaで定義されたReverseApplicationというクラス)のメソッドが呼び出され、urlがセットされたCallというケースクラスが生成されることでURLの文字列を生成している。
設定のroutesImportを追加してやることで、Callの呼び出しを挿げ替えるのが今回のハックの内容になる。

import play.api.mvc._
import api.mvc.Call    // ←この行が追加される
...
// @LINE:16
def edit(id:Long) = {
   // ↓の呼び出し先が、play.api.mvc.Callからapi.mvc.Callに変わる
   Call("GET", "/computers/" + implicitly[PathBindable[Long]].unbind("id", id))
}

あとは、api.mvc.CallでURLを操作して元のplay.api.mvc.Callを生成して戻してやることで、無事にパラメータを追加できる。

逆にルーティングの際はパラメータは適当に無視してくれるみたいで、ちゃんとアクションが呼ばれます。

*1:元ネタはrailsかな?

Re: nil に対するメッセージ送信が例外にならない

お題:

nil に対するメッセージ送信が例外にならない

この仕様って、Objective-C以外でほとんど見かけたことがないのだけど、メリットに比べてデメリットが大き過ぎると思う。オブジェクトのメソッドチェインでこの仕様がたまに便利なことはあるけど、ほとんどの場合、バグが発現するタイミングが遅くなるだけに終わるというのが経験則。この辺、熟練のObjective-Cerはどう考えてるのか一度知りたいところ。

Objective-Cで不満に思うこと - kmizuの日記

もう10年以上仕事でObjective-Cは使ってないので、熟練というよりは只のロートルなんだけど、自分なりに考えた事を書いてみようと思う。

随分前の事を思い出しながら書くのと、憶測も交えているので、あまり正確な内容でない事はあらかじめ断っておきます。

Objective-Cの例外について

まず、そもそもObjective-Cのプロパーな例外って存在しないんじゃないかという話をしたいと思う。

Mac OS XiOS用にObjective-Cでプログラムを書く際に良く出てくる例外って、だいたいNSExceptionクラスってやつだと思う*1。 NSExceptionという名前から分かるように、これはFoundationフレームワークの一員だったりする。

Foundation(Foundation Kit)は1995年にNEXTSTEP 3.3がリリースされた時に追加されたフレームワークで、(OpenStepという公開された仕様に基づくとはいえ) NeXT社が独自拡張したクラスライブラリであり、それ以前のObjective-CにはNSExceptionは存在しなかった。

じゃあそれまでObjective-Cに例外処理はなかったかというとそういうわけではなくて、NXHandlerという構造体とsetjmp/longjmpを使って実装された例外処理用のマクロが存在した。今のNS_DURING〜とほぼ同じような構文で使われていた。

NX_DURING
    /* code that may cause an error */
NX_HANDLER
    switch (NXLocalHandler.code)
    case 
        NX_someErrorCode:
            /* code to execute for this type of error */
    default: NX_RERAISE();
NX_ENDHANDLER

じゃあこのNX_DURING〜がObjective-Cのプロパーな例外処理機構というとそうでもなくて、名前から分かる通りNeXT社の独自機能だと思われる。少なくともListクラスやHashTableクラスのようなObjective-Cの基本クラスでは明示的には使われていない。

NeXT/Apple以外のObjective-C処理系ではどうだろうとおもってちょっと調べたけど、それぞれに別々の例外処理を持っているっぽい。例えば、Portable Object Compilerのマニュアルには次のように記載されている

This chapter discusses exception handling for OBJECTIVE-C as outlined in [Cox, 1991].

An exception -- in response to some abnormal program condition -- is raised by sending a error: message to an Object.

if (!h) [ anObject error:"h can't be zero" ];

This is similar to how in Stepstone OBJECTIVE-C an error action (aborting the process) is performed.

However, in our case, the user might substitute a different Block (called exception handler) for the default handler (which aborts the process). This is done by using the method ifError: :

[ { c = [a foo]; } ifError: { :msg :rcv | printf("got an exception"); } ];

Instead of evaluating the default handler, error: will execute the exception handler specified by ifError:. The handler is invoked with a message object and with the receiver of the error: message.

User Manual Portable Object Compiler Version 3.3.10

見るからに、かなり毛色が違っている。オブジェクトの-error:を呼ぶ事で例外を起こし、標準のハンドラではなくBlockを使って処理をおこなうらしい。

要するに言語系で異なっていて、本来のObjective-C的な例外というものはないと思われる。*2

nilの使われ方について

NEXTSTEP 3.3のマニュアルを見ていると、CommonクラスやApplicationKitのクラスで明示的に例外が使われているところはあんまりない*3。ストリームの読み書き系やフォント周り、NXColorなどのidではなく構造体を返す処理、DPS周りなどに少しある程度。それ以外は、例外を起こさずにnilを返しているように見える。

例えば、可変長のコレクションクラスであるListクラスの、指定した位置にオブジェクトを挿入するためのメソッドは、マニュアルに次のように書かれている。

insertObject:at:

- insertObject:anObject at:(unsigned int)index

Inserts anObject into the List at index, moving objects down one slot to make room. If index equals the value returned by the count method, anObject is inserted at the end of the List. However, the insertion fails if index is greater than the value returned by count or anObject is nil.

If anObject is successfully inserted into the List, this method returns self. If not, it returns nil.


List - Common Classes and Functions - NEXTSTEP General Reference (c) 1995 by NeXT Computer, Inc.

要素がnilの場合や、挿入箇所にリストの要素数より大きい数値を指定した場合、例外を起こさずにnilを返す。挿入が成功した場合はselfを返す。

このように、nilは単なる空値ではなく、明らかに失敗系として使われていたと思われる。

このことから推測するに、nilにメッセージを送るとnilを返すのは、失敗系の伝播という意味合いを担っていたのではないか。例えば、次のような架空のコードがあったとして、

    [[self itemList] addObject:[[Item createNewItem] prepareFor:self]];

処理中に、+createNewItemや-prepareFor:が失敗した時にもnilを返す事で、この行の処理全体を中断して抜け出せるように作ることが出来る。

つまり、便利だからメソッドチェーンというのではなくて、制御フローとして使う為のものだったのではないかという想像です。


Foundationフレームワークが導入されたあたりから、return selfするメソッドも減っていって、例えば現在のコレクションクラスであるNSMutableArrayの挿入メソッドは、次のように戻り値の型はvoidになり、異常時には例外を起こすようになっている。

insertObject:atIndex:

Inserts a given object into the array's contents at a given index.

- (void)insertObject:(id)anObject atIndex:(NSUInteger)index

(cut)
Important Raises an NSInvalidArgumentException if anObject is nil.
(cut)
Important Raises an NSRangeException if index is greater than the number of elements in the array.

NSMutableArray Class Reference

個人的には、Foundationに切り替わった段階で、nilにメッセージ送ると例外、という仕様に変わっても良かったような気もする。少なくとも[ [ [Hoge alloc] init] autorelease]などの特定のイディオム以外では、自分は積極的には使わないようになっていった。

追記: Portable Object Compilerでのnilの扱い

オプションで挙動を変えられるみたいですな。

Sending Messages to Nil

The -noNilRcvr option can be used to prevent messages being sent to nil (the NULL pointer). It is in fact a runtime option, since the only effect of this option is that, when the main() of the program is compiled with this option, a nilHandler() function will be registered that stops the process, instead of simply returning nil (as the default handler does).

objc -noNilRcvr main.m

User Manual Portable Object Compiler Version 3.3.10

*1:今はNSExceptionでなくてもスローキャッチできるらしいけど。

*2:出自的にStepstoneのコンパイラの仕様を正統とするべきかもしれないけど、少なくともAppleのものには取り入れられていないと思う。

*3:内部的には使われていたかもしれないけど

Javaで状態モナド

そろそろモナド変換子のことを覚えたいなーと思ったものの、見つけたサンプルが状態モナドを例にとっていたので、学習の準備としてひとまず状態モナドの実装を写経してみることにした。Javaで。

参考にするページ:

ソースコード

例によって「モナドのすべて」から写経するよ。

// libraryDependencies += "org.apache.commons" % "commons-lang3" % "3.0.1"
// libraryDependencies += "com.google.guava" % "guava" % "10.0.1"
import org.apache.commons.lang3.builder.ToStringBuilder;
import com.google.common.base.Function;

/**
 * 状態モナド
 *
 * @param <S> 状態の型
 * @param <A> 値の型
 */
// newtype State s a = State { runState :: (s -> (a,s)) } 
public class State<S, A> {
    /** 状態を渡すと、値と状態を戻す処理 */
    private final Function<S, StatePair<A, S>> runState;

    public State(Function<S, StatePair<A, S>> runState) {
        this.runState = runState;
    }
    // 状態を渡して、値と状態を取り出す
    public StatePair<A, S> runState(S s) {
        return runState.apply(s);
    }
    // 状態を渡して、状態を取り出す
    public S execState(S s) {
        return runState(s).state;
    }
    // 状態を渡して、値を取り出す
    public A evalState(S s) {
        return runState(s).value;
    }
    // 型省略用のファクトリメソッド
    public static <S, A> State<S, A> state(Function<S, StatePair<A, S>> runState) {
        return new State<S, A>(runState);
    }
    // return a        = State $ \s -> (a,s)
    public static <S, A> State<S, A> unit(final A a) {
        return state(new Function<S, StatePair<A, S>>() {
            public StatePair<A, S> apply(S s) {
                return StatePair.pair(a, s);
            }
        });
    }
    // (State x) >>= f = State $ \s -> let (v,s') = x s in runState (f v) s' 
    public <B> State<S, B> bind(final Function<A, State<S, B>> f) {
        return state(new Function<S, StatePair<B,S>>() {
            public StatePair<B, S> apply(S s) {
                StatePair<A, S> ret = runState(s);
                return f.apply(ret.value).runState(ret.state);
            }
        });
    }
    // get   = State $ \s -> (s,s) 
    public static <S>State<S, S> get() {
        return state(new Function<S, StatePair<S,S>>() {
            public StatePair<S, S> apply(S s) {
                return StatePair.pair(s, s);
            }
        });
    }
    // put s = State $ \_ -> ((),s)
    public static <S> State<S, Void> put(final S s) {
        return state(new Function<S, StatePair<Void, S>>() {
            public StatePair<Void, S> apply(S dummy) {
                return StatePair.pair(null, s);
            }
        });
    }
    /**
     *  Stateから中身を取り出すための、値と状態のペア
     *
     * @param <A> 値の型
     * @param <S> 状態の型
     */
    public static final class StatePair<A, S> {
        public final A value;
        public final S state;

        private StatePair(A a, S s) {
            this.value = a;
            this.state = s;
        }
        /** ペアを生成する */
        private static <A, S> StatePair<A, S> pair(A a, S s) {
            return new StatePair<A, S>(a, s);
        }
        public String toString() {
            return ToStringBuilder.reflectionToString(this);
        }
    }
}

関数は例によってGoogle GuavaのFunctionを借りている。
タプルが無いので、StatePairという値と状態の組をあらわすクラスを用意した。また、空のタプルもないので、Void(値としてはnull)で代用した。

解説

モナドと言うのは、何かを包んで、組み合わせて使用しやすいようにしたコンテナ、と考えればよいと思う。
状態モナドも、やはり何かを包んでいる。名前から想像するに……「状態」を保持?と考えるとハマるので止めましょう。
「状態を受け取って値と状態を返す“処理”」を包んだものと考えるのが分かりやすいと思う。


例えば、(上に挙げたサイトで出てきたサンプルにあるような、) スタックの実装をイメージすると分かりやすい。

  • push(n)という状態モナド → スタックを状態として与えると、それにnを積んだ状態を戻す「処理」
  • popという状態モナド → スタックを状態として与えると、値としてスタックのトップを、状態としてスタックの残りを、戻す「処理」

ちょっとReaderモナドに似ているけど、Readerモナドの中身は「環境を与えると値を戻す処理」なのに対して、Stateモナドの中身は「状態を与えると値と状態を戻す処理」という感じで高機能になっている。

使い方の例: スタック

Stateモナドとモナドトランスフォーマー - yunomuのブログ」にあるスタックの例(モナド変換子じゃない版)を写経してみる。
まずは、スタックを実現するデータ型として連結リストのクラスを定義。

/**
 * 連結リスト
 *
 * @param <A> 値の型
 */
public class List<A> {
    public final A head;
    public final List<A> tail;

    private List(A head, List<A> tail){
        this.head = head;
        this.tail = tail;
    }
    private static List EMPTY = new List(null, null) {
        @Override
        public String toString() {
            return "Nil";
        }
    };
    /** 空のリストを戻す。いわゆるNil。 */
    public static <A> List<A> empty() {
        return (List<A>) EMPTY;
    }
    /** リストが空かどうかを戻す */
    public boolean isEmpty() {
        return this == EMPTY;
    }
    /** リストの先頭に要素を追加したリストを戻す。 */
    public static <A> List<A> cons(A a, List<A> as) {
        return new List<A>(a, as);
    }
    @Override
    public String toString() {
        return "List(" + head.toString() + ", " + tail.toString() + ")";
    }
}

使い方は、empty()か既存のリストにcons()で要素を追加する

// 整数のリスト。配列風に書けば、[2, 1]
List<Integer> list = List.cons(2, List.cons(1, List.<Integer>empty()));
// -> List(2, List(1, Nil))

ではスタックの実装。push/popをstaticメソッドで実装する。

/**
 * 連結リストListで表現されたスタックを状態と見立てて、
 * それを操作するStateモナドを生成するユーティリティ。
 */
public class StackUtils {
    /**
     *  スタックを状態として渡すと、値aをそれに積んだスタックを戻すStateを生成する
     * @param <A> 値の型
     * @param a スタックに積む値
     */
    public static <A> State<List<A>, Void> push(final A a) {
        return State.<List<A>> get().bind(new Function<List<A>, State<List<A>, Void>>() {
            public State<List<A>, Void> apply(List<A> as) {
                return State.put(List.cons(a, as));
            }
        });
    }

    /**
     *  スタックを状態として渡すと、スタックのトップを値に、トップを取り除いた残りを状態として戻すStateを生成する
     * @param <A> 値の型
     */
    public static <A> State<List<A>, A> pop() {
        return State.<List<A>> get().bind(new Function<List<A>, State<List<A>, A>>() {
            public State<List<A>, A> apply(final List<A> as) {
                if (as.isEmpty()) {
                    throw new RuntimeException("stack empty");
                }
                return State.put(as.tail).bind(new Function<Void, State<List<A>, A>>() {
                    public State<List<A>, A> apply(Void dummy) {
                        return State.unit(as.head);
                    }
                });
            }
        });
    }
}

使ってみる。

        /* 空のスタックに、"aaa"を積むサンプル */
        // "aaa"を積むState
        State<List<String>, Void> push_aaa = StackUtils.push("aaa");

        // 空のスタック
        List<String> sampleStack1 = List.empty();

        // 空のスタックに、"aaa"を積んだスタックを取得
        List<String> result1 = push_aaa.execState(sampleStack1);
        System.out.println(result1);    // -> List(aaa, Nil)
        /* スタックから、値を取り出すサンプル */
        // スタックのトップの値を取り除いて取得するState
        State<List<Integer>, Integer> pop1 = StackUtils.pop();
 
        // 1, 2が積まれたスタック(トップは2)
        List<Integer> sampleStack2 = List.cons(2, List.cons(1, List.<Integer>empty()));
        System.out.println(sampleStack2);               // -> List(2, List(1, Nil))

        // 値を一つとり出す
        StatePair<Integer, List<Integer>> result2 = pop1.runState(sampleStack2);
        System.out.println("value = " + result2.value); // -> value = 2
        System.out.println("state = " + result2.state); // -> state = List(1, Nil)

pushとpopを組み合わせて使う例として、「スタックの先頭を複製する処理」を書いてみる。

    /** スタックの先頭を複製するStateを生成する */
    <A> State<List<A>, Void> dup() {
        return
            // popして値を取り出し、
            StackUtils.<A>pop()
            .bind(new Function<A, State<List<A>, Void>>() {
                public State<List<A>, Void> apply(final A value) {

                    return
                        // 取り出した値をpushし、
                        StackUtils.push(value)
                        .bind(new Function<Void, State<List<A>, Void>>() {
                            public State<List<A>, Void> apply(Void dummy) {

                                // さらに同じ値をpushする。
                                return StackUtils.push(value);
                            }
                        });
                }
            });
    }
        /* スタックのトップを複製するサンプル */
        // スタックのトップを複製するState
        State<List<Integer>, Void> dupState = dup();

        // 123が積まれたスタック
        List<Integer> sampleStack3 = List.cons(123, List.<Integer>empty());
        System.out.println(sampleStack3);   // -> List(123, Nil)

        // トップを複製する
        List<Integer> ret3 = dupState.execState(sampleStack3);
        System.out.println(ret3);           // -> List(123, List(123, Nil))

こんな感じで、状態を渡すと値と状態を戻す処理が書け、それらを組み合して使うこともできる。

「JavaScriptでの非同期関数合成」を継続モナドで

お題:

複数個の関数があって、関数を呼び出した結果を使って関数を呼び出して…っていうのを1個の関数にします。

JavaScriptでの非同期関数合成 - monjudoh’s diary

関数の形を見てみます。

// 1を足す
function add1(callback,arg){
  callback(arg+1);
}

コールバック関数を渡して、戻り値ではなくコールバック関数を呼び出してもらう事で結果を取得する……これって「CPS(継続渡し形式)」!CPSなら分かるわ!(本当か?)

参考にしたサイト:

ということで継続モナドを使って書いてみる。

継続モナド

例によって「モナドのすべて」を見ながら写経します。

// 継続モナド
function Cont(f) { // f: a -> r -> r
   this.run = f;
}
// a.k.a. '>>='
Cont.prototype.bind = function(f) { // f: a -> Cont r b
   var run = this.run;
   return new Cont(function(k) { return run(function(a) { return f(a).run(k) }) });
}
// a.k.a. 'return'
Cont.unit = function(a) {
   return new Cont(function(k) { return k(a) });
}
Cont.callcc = function(f){ // f: a -> Cont r b -> Cont r a
   return new Cont(function(k) { return f(function(a) { return new Cont(function(x) { return k(a) }) }).run(k) })
}

「return」は「unit」に、「>>=」は「bind」に名前を変えています。


使い方の前に、ちょっとおさらいをしてみる。
例として、「数を2乗して印字する」という処理を考える。
通常の書き方では、たとえば、「引数の値を2乗して戻す」関数を用意し、その戻り値を取得して印字する、というのが考えられる。

function square(x) { return x * x }
log.console(square(3)); // 9

CPSというのは、値を戻り値で返すのではなく、用意した別の関数を呼び出してもらうことで結果を受け取る方式のこと。

function squareCps(x, cont) { cont(x * x) }
squareCps(3, console.log); // 9

継続モナドというのは、このCPSの関数を複数組み合わせて使用しやすいようにしたもの、と考えればいいだろう。

// 上の方で定義したContを使うよ
var squareCont = function (x) { return Cont.unit(x * x) }
squareCont(3).run(console.log); // 9

では、実際に複数組み合わせてみる。「3に1を足して2乗して印字」という処理は次のように書ける。

var add1Cont = function (x) { return Cont.unit(x + 1) }
// 「3に」「1を足して」「2乗して」「印字」
Cont.unit(3).bind(add1Cont).bind(squareCont).run(console.log); // 16
// ↑と↓は同じ意味
// add1Cont(3).bind(squareCont).run(console.log);

bindという関数*1を使うことで、数を受け取ってContを返すような関数たちをつなげて使うことができる。

これの何が嬉しいの?というと……単体で使っても多分あんまり意味なそう。とりあえず、処理を中断したり、ショートカットしたりできます。
例えば、一連の処理の途中で「逆数を取る」という処理が挟まっていて、その時点で0だったらエラーにしたい場合は、次のように書ける。

var n = -1;
Cont.callcc(function(exit) {
   // 数に1を足して逆数を取って二乗する。但し逆数を取る際に0の場合はエラー
   return Cont.unit(n)
      .bind(add1Cont)
      .bind(function(x) { return (x != 0) ? Cont.unit(1.0 / x) : exit("*** error: Division by zero.") })
      .bind(squareCont);
}).run(console.log); // "*** error: Division by zero."

Cont.callccで渡す関数の引数として渡されたexitという関数を呼ぶことで、以降の処理をスキップして、console.logにエラーメッセージを渡している。

関数の合成

上で見たように、値を取ってContを返すような関数は、bindで連結することで連続して適用できることは分かった。でもこれって関数合成!(キリッと言い切るにはちょっと回りくどいような?
たとえば、add1ContとsquareContを連続して適用するような関数add1squareContを書こうとすると、

var add1squareCont = function(x) { return Cont.unit(x).bind(add1Cont).bind(squareCont) }

いちいちfunctionを書くのがまどろっこしい。

そこで、「値を取ってモナド(この場合はCont)を返すような関数」*2同士を直接合成できるようにする仕組みで、Kleisliというのがあるので、ちょっと真似してみる。
Kleisliについては、この辺参照

深遠な数学的背景があるそうですが、よくわかってないので、自分も「KleisliはA => M[B]の形の関数で、合成ができて、☆という印象。」です。


では実装。
https://github.com/scalaz/scalaz/blob/master/core/src/main/scala/scalaz/Kleisli.scalaから部分的にパクってくる。

// 「>=>」のみの手抜き実装
function Kleisli(f){
   this.apply = f;
}
Kleisli.prototype = {
   compose: function(k) {
      var apply = this.apply;
      return new Kleisli(function(a) { return apply(a).bind(function(b){ return k.apply(b) }) });
   }
}

「>=>」*3の名前をcomposeにしています。元々composeという関数があるので紛らわしいのですが、「>=>」の方です。

使ってみる。

// Kleisli化
var add1ContK = new Kleisli(add1Cont);
var squareContK = new Kleisli(squareCont);

// 合成する
var add1squareContK = add1ContK.compose(squareContK);

add1squareContK.apply(3).run(console.log);

functionを書かなくても処理同士を連結できるようになった。

お題のサンプルを実装してみる

「コールバックと数字を引数に取って何かの処理をしコールバックを呼ぶ関数」を合成可能し、合成した結果も同じ形になるようにしたい。

// 1を足す
function add1(callback,arg){
  callback(arg+1);
}
// 2をかける
function mul2(callback,arg){
  callback(arg*2);
}

function log(result){
  console.log(result);
}

関数の型や順番を合わせる為に、変換関数を用意する。

// make fun Cont-resultic
function inCont(f) {
   return function(a) { return new Cont(function(callback) { return f(callback, a) }) };
}
// make fun composable
function composable(f) { return new Kleisli(inCont(f)); };

これを使うと、「4をadd1してmul2してadd1した結果を印字する」処理はこう書ける。

var add1K = composable(add1);
var mul2K = composable(mul2);

add1K.compose(mul2K).compose(add1K).apply(4).run(log); // 11

「.apply(4).run(log)」の部分が……。元の関数と型が異なっているので、ここも変換して合わせる。

function kleisi2fun(kleisi) {
   var fun = function(k, a) { return kleisi.apply(a).run(k) };
   fun.apply = function(a) { return kleisi.apply(a) };
   fun.compose = function(k){ return kleisi2fun(kleisi.compose(k)) };
   return fun;
}
// make fun composable
function composable(f) { return kleisi2fun(new Kleisli(inCont(f))); };

合成もできるし、関数としても呼べるようになった。

var add1K = composable(add1);
var mul2K = composable(mul2);

add1K.compose(mul2K).compose(add1K)(log, 4); // 11

// 合成したものをさらに合成
var composed = composable(add1).compose(composable(mul2));
composed.compose(composed)(log, 4);  // 22

JavaScriptでObjectを関数に偽装する方法が分からなかったので関数の方をオブジェクトに偽装してみたけど、結構コスト高そう。

Scalazでも書いてみた

練習なので。

import scalaz._,Scalaz._

sealed trait Cont[R, A] extends NewType[(A => R) => R] {
   val value: (A => R) => R
   def run = value

   def flatMap[B](f: A => Cont[R, B]): Cont[R, B] = new Cont[R, B] {
      val value = (k: B => R) => Cont.this.value(a => {f(a).value(k)})
   }
}

object Cont {
   def cont[R, A](f: (A => R) => R) = new Cont[R, A] {
      val value = f
   }
   implicit def ContPure[R]: Pure[PartialApply1Of2[Cont, R]#Apply] =
      new Pure[PartialApply1Of2[Cont, R]#Apply] {
         def pure[A](a: => A) = new Cont[R, A] {
            val value = (k: A => R) => k(a)
         }
      }
   implicit def ContBind[R]: Bind[PartialApply1Of2[Cont, R]#Apply] =
      new Bind[PartialApply1Of2[Cont, R]#Apply] {
         def bind[A, B](r: Cont[R, A], f: A => Cont[R, B]) = r flatMap f
      }

/* 今回は使わないけど、↓こんな感じ?*/
/*
   implicit def ContFunctor[R]: Functor[PartialApply1Of2[Cont, R]#Apply] =
      new Functor[PartialApply1Of2[Cont, R]#Apply] {
         def fmap[A, B](r: Cont[R, A], f: A => B) = new Cont[R, B] {
            val value = (k: B => R) => r.value(k <<< f)
         }
      }
   import Monad._
   implicit def ContMonad[R]: Monad[PartialApply1Of2[Cont, R]#Apply] =
      monad[PartialApply1Of2[Cont, R]#Apply](ContBind, ContPure)
*/

   def callcc[R, A, B](f: (A => Cont[R,B]) => Cont[R, A]) = new Cont[R, A] {
      val value = (k: A => R) => f(a => new Cont[R, B] {
         val value = (x: B => R) => k(a)
      }).value(k)
   }
}

使う側:

import scalaz._,Scalaz._
import Cont._

      // 1を足す
      def add1(x: Int) = (x + 1).pure[PartialApply1Of2[Cont, Unit]#Apply]
      // 2をかける
      def mul2(x: Int) = (x * 2).pure[PartialApply1Of2[Cont, Unit]#Apply]
      // log
      val log = (arg: Int) => println(arg)

      val add1ContK = kleisli[PartialApply1Of2[Cont, Unit]#Apply, Int, Int](add1)
      val mul2ContK = kleisli[PartialApply1Of2[Cont, Unit]#Apply, Int, Int](mul2)
      (add1ContK >=> mul2ContK) apply(4) run(log) // 10

      val composed = add1ContK >=> mul2ContK
      (composed >=> composed) apply(4) run(log) // 22

ScalazにはContモナドは無いですが、Freeあるから要らない的な意見もあるようですが、どうやってFreeで書けば良いのか良くわからない。

*1:とりあえずFunction.prototype.bindとは関係ないよ

*2:M-resulticな関数というらしい。cf: http://d.hatena.ne.jp/m-hiyama/20060421/1145613700

*3:Kleisli composition operatorと言うらしい

Z会三年生中学受験コース5月のてんさく問題を Scala で解いてみた

Scalaの練習です。

お題:

4けたの数について、それぞれの位の数字を大きいじゅんにならべた数から小さいじゅんにならべた数をひくという計算を行います。
1974 について、この計算を 100 回行った答えを書きなさい。

Z会三年生中学受験コース5月のてんさく問題を Python で解いてみた - cooldaemonの備忘録
object Tensaku {
   def subAscFromDesc(n:Int) = {
       val s = n.toString
       s.sortWith(_>_).toInt - s.sortWith(_<_).toInt
   }
   def main(args:Array[String]) {
      println((1 to 100).foldLeft(1974)((n, _) => subAscFromDesc(n)))
   }
}
[info] Running Tensaku
6174

Scalaの文字列*1はStringOpsへの暗黙の変換を持っていて、StringOpsはSeqLike[Char, String]を継承しているので、Charの「>」「<」でsortWithできるのを使っています。

実際に入試問題として解くなら、問題文から処理が途中で周期性を持つことを推測し、周期性が出て来るまでべたに計算するのかな。

追記: Scalazでも書いてみた

練習なので。

   import scalaz._
   import Scalaz._

   val intToString: Int => String = _.toString
   val sorted: String => String = _.sorted
   val reverse: String => String = _.reverse
   val toInt: String => Int = _.toInt
   val sub: ((Int, Int)) => Int = _.fold(_ - _)

   val subAscFromDesc =
       intToString >>> sorted >>> ((reverse >>> toInt) &&& toInt) >>> sub

こちらの記事を参考にしました。

*1:というかjava.lang.String

ListとStreamの両方でheadとtailにmatchさせる

Listを扱うプログラムを書く時に、match式で::を使ってheadとtailを取り出すことが良くあると思う。
例えば、「二つのList a, bがあり、bがaに前方一致するならaからbを取り除いた残りを戻す」というような処理をmatchを使って書くと*1、こんな風になるかと思う。

   def dropWith[A](a: List[A], b: List[A]): Option[List[A]] = {
      (a, b) match {
         case (_, Nil) => Some(a)
         case (Nil, _) => None
         case (h1 :: _, h2 :: _) if h1 != h2 => None
         case (h1 :: t1, h2 :: t2) => dropWith(t1, t2)
      }
   }
scala> dropWith(List(1, 2, 3, 4, 5), List(1, 2))
res1: Option[List[Int]] = Some(List(3, 4, 5))

scala> dropWith(List(1, 2, 3, 4, 5), List(1, 3))
res2: Option[List[Int]] = None

これを拡張して、一つ目の引数が無限リスト(というかStream)でもOKにしたい。それで一つ目の引数の型をSeqにしてみたんだけど、List以外を指定すると「::」でマッチさせようとしてもうまく動かない。「::」に加えて「#::」も書いておくとStreamの場合にも対応できるけど、表現が冗長だし、結局ListとStream以外(例えばRange)が来た場合に動かない。*2

   // scalaVersion := "2.9.1"
   def dropWith[A](a: Seq[A], b: List[A]): Option[Seq[A]] = {
      (a, b) match {
         case (_, Nil) => Some(a)
         case (Nil, _) => None
         case (h1 :: _, h2 :: _) if h1 != h2 => None
         case (h1 :: t1, h2 :: t2) => dropWith(t1, t2)
         case (h1 #:: _, h2 :: _) if h1 != h2 => None
         case (h1 #:: t1, h2 :: t2) => dropWith(t1, t2)
      }
   }
scala> dropWith(Stream.from(1), List(1, 2))
res1: Option[Seq[Int]] = Some(Stream(3, ?))

scala> dropWith(1 to 5, List(1, 2))
scala.MatchError: (Range(1, 2, 3, 4, 5),List(1, 2)) (of class scala.Tuple2)
...

どうしたらいいかなーと思って検索してると、extractorを自分で定義している例を見つけた。

scala> val fff = new { def unapply[A, T](x : { def head : A; def tail : T;
def isEmpty : Boolean }) = if(x.isEmpty) None else Some(x.head, x.tail) }
fff: java.lang.Object{def unapply[A,T](AnyRef{def head: A; def tail: T;
def isEmpty: Boolean}): Option[(A, T)]} = $anon$1@32b90a0

scala> fff.unapply(List(1,2))
res112: Option[(Int, List[Int])] = Some((1,List(2)))

scala> List(1,2,3) match { case x fff xs => (x, xs

)}

res113: (Int, List[Int]) = (1,List(2, 3))

scala> Stream(1,2,3) match { case x fff xs => (x, xs)}
res114: (Int, Stream[Int]) = (1,Stream(2, ?))

Using the foldLeft operator /: | The Scala Programming Language

上の例では構造的部分型を使っているけど、ドキュメントを見るとtailは共通の親traitであるSeqLikeに定義されているので、SeqLikeに対してextractorを用意してやれば良いと思う。

   import collection.SeqLike
   def dropWith[A, S <% SeqLike[A, S]](a:S, b:Seq[A]): Option[S] = {
      object ::#:: {
         def unapply[T <% SeqLike[A, T]](x: T) = if (x.isEmpty) None else Some(x.head, x.tail) 
      }  
      (a, b) match {
         case (_, Nil) => Some(a)
         case (Nil, _) => None
         case (h1 ::#:: _, h2 ::#:: _) if h1 != h2 => None
         case (h1 ::#:: t1, h2 ::#:: t2) => dropWith(t1, t2)
      }  
   }
scala> dropWith(List(1, 2, 3, 4, 5), List(1, 2))
res0: Option[List[Int]] = Some(List(3, 4, 5))

scala> dropWith(Stream.from(1), List(1, 2))
res1: Option[scala.collection.immutable.Stream[Int]] = Some(Stream(3, ?))

scala> dropWith("pineapple", "pine")
res2: Option[java.lang.String] = Some(apple)

scala> dropWith(1 to 5, 1 to 3)
<console>:11: error: No implicit view available from scala.collection.immutable.Range.Inclusive => scala.collection.SeqLike[Int,scala.collection.immutable.Range.Inclusive].
              dropWith(1 to 5, 1 to 3)
                      ^

うーん、Rangeでは結局上手くいかない。RnageはSeqLike[Int, Range]ではなくSeqLike[Int, IndexedSeq[Int] ]を継承しているせいみたい。

ちなみにStreamが::をサポートしていない理由は「Scala by Example」に書いてあって、::がtailをListとして事前に評価してしまうから(遅延評価を旨とするStreamとは合わない)らしい。

追記: やっぱり構造的部分型で受けてみた

   object :#: {
      def unapply[A, T <% {def head: A; def tail: T; def isEmpty: Boolean}](x: T): Option[(A, T)] =
          if (x.isEmpty) None else Some(x.head, x.tail)
   }  
   @tailrec
   def dropWith[A, T <% {def head: A; def tail: T; def isEmpty: Boolean}](a: T, b: Seq[A]): Option[T] = {
      (a, b) match {
         case (_, Nil) => Some(a)
         case (Nil, _) => None
         case (h1 :#: _, h2 :#: _) if h1 != h2 => None
         case (h1 :#: t1, h2 :#: t2) => dropWith(t1, t2)
      }
   }
scala> dropWith(Stream.from(1), List(1, 2))
res0: Option[scala.collection.immutable.Stream[Int]] = Some(Stream(3, ?))

scala> dropWith("pineapple", "pine")
res1: Option[java.lang.String] = Some(apple)

scala> dropWith(1 to 5, 1 to 3)
<console>:11: error: No implicit view available from scala.collection.immutable.Range.Inclusive => AnyRef{def head: Int; def tail: scala.collection.immutable.Range.Inclusive; def isEmpty: Boolean}.
              dropWith(1 to 5, 1 to 3)
                      ^

scala> dropWith[Int,Range](1 to 5, 1 to 3)
res3: Option[Range] = Some(Range(4, 5))

*1:お話の都合上matchを使っているので、if (a startsWith b) Some(a drop b.length) else Noneで良いじゃんというのは無しで……

*2:まああと戻り値が共変じゃないので使いにくい。