2012年12月21日金曜日

Konst

この記事はScalaz Advent Calendarの20日目の記事です。

ScalaにはFunction.constという常に一定の値を返す関数があります。

val one = Function.const(1)_
one(2) assert_=== 1
one("hoge") assert_=== 1
view raw const.scala hosted with ❤ by GitHub

この関数を型レベルで表したものがScalazにKonstとして定義されています。

Konst


NaturalTransformationを例にKonstを使ってみます。

new (List ~> Option) {
def apply[A](list: List[A]) = list.headOption
}
new (List ~> Konst[Int]#Apply) {
def apply[A](list: List[A]) = list.size
}
new (Konst[String]#Apply ~> Konst[Int]#Apply) {
def apply[A](string: String) = string.size
}
view raw konst.scala hosted with ❤ by GitHub

あまり使わなさそうですね!
私としてはまだScalaz6にあったPartialApplyの方が便利な気がするのですが。
まあ、型推論がうまくいかなかったり、Unapplyが新しく入ったりしたことが原因にあるのでしょうね。

2012年12月18日火曜日

Scalazのかきかた


というわけで、Scalaz Advent Calendarの18日目の記事です。

Scalazには多くの型クラスとそのインスタンスが定義されており、それを扱うために、多くの記法が存在します。
この記事では簡単で冗長な記法から、複雑で簡素な記法まで紹介していきます。

インスタンスの取得


implicit valueとして定義されている型クラスのインスタンスは、implicit parameterにより取得が可能です。

そのインスタンスの取得にも様々方法があります。

implicitly

Scala標準ライブラリに定義されている、implicit valueを取得する関数です。

import std.option._
val map = Map('foo -> "bar", 'hoge -> "fuga")
implicitly[Apply[Option]].apply2(map get 'foo, map get 'hoge)(_ + _)

TypeClass

また、Scalazの型クラスにはインスタンスの取得のための関数、TypeClass.applyを使用することができます。

Apply[Option].apply2(map get 'foo, map get 'hoge)(_ + _)
view raw apply.scala hosted with ❤ by GitHub

関数呼び出し


ある型クラスの関数を使用するとき、先ほどのようにインスタンスから直接呼び出すことができます。

import std.list._
Bind[List].join(List(List(1, 2), List(3, 4)))
view raw join.scala hosted with ❤ by GitHub

Ops

しかし、明示的に型クラスのインスタンスを取得するのは冗長です。
Scalazではimplicit conversionを用いて、型クラスのインスタンスをもつオブジェクトに対して暗黙の型変換を提供します。

import scalaz.syntax.bind._
List(List(1, 2), List(3, 4)).join
view raw ops.scala hosted with ❤ by GitHub

暗黙型変換の他にも、単一のオブジェクトを対象としない型クラスの関数がインポートされます。

import scalaz.std.anyVal._
Monoid[Int].zero
import scalaz.syntax.monoid._
mzero[Int]
view raw zero.scala hosted with ❤ by GitHub

関数定義


関数を定義するとき、implicit parameterを指定する方法が2つあります。

implicit

1つはimplicitを使う方法です。

def double[A](a: A)(implicit A: Semigroup[A]) =
A.append(a, a)
view raw double.scala hosted with ❤ by GitHub

Context Bounds

もう1つ、Context Boundsというものが存在します。

def doubleCB[A: Semigroup](a: A) =
Semigroup[A].append(a, a)

インスタンスを明示的に扱わない場合はContex Boundsで定義した方が良いでしょう。

def doubleCBS[A: Semigroup](a: A) = a |+| a

Syntax


大抵の場合の場合はOpsとContex Boundsで短いコードが得られますが、これらを使っても冗長になる場合があります。

def zeroTrio[A: Monoid] =
(mzero[A], mzero[A], mzero[A])
import scalaz.syntax.pointed._
def nestPoint[M[_]: Pointed, A](a: A) =
a.point[M].point[M]
view raw morph.scala hosted with ❤ by GitHub

Scalaでは返り値の型を指定してもimplicit valueを決定することは出来ないので明示的に型を書く必要があります。
このような場合、Syntaxを使うことで明示的に型を指定する必要がなくなります。

def zeroTrioS[A](implicit A: Monoid[A]) = {
import A.monoidSyntax._
(mzero, mzero, mzero)
}
def nestPointS[M[_], A](a: A)(implicit M: Pointed[M]) = {
import M.pointedSyntax._
point(point(a))
}
view raw syntax.scala hosted with ❤ by GitHub

まとめ


短く簡素なコードになるほど、複雑な仕組みが使われていきます。
大抵のものはContex BoundsとOpsで短いコードになるので、積極的に使っていきましょう。
型クラスのインスタンスから直接関数を呼ぶのも良いですが、Syntaxをimportした方が簡素になる場合があることも頭に入れておくと良いでしょう。

2012年12月17日月曜日

JavaFX & Web Start with Clojure

This article is the 5th of JavaFX Advent Calendar and a sequel of Tic-tac-toe with Clojure.

Tic-tac-toe

Since the logic of the game have been made, we only need to implement at the drawing.

Application


As the basis for JavaFX, inherit javafx.application.Application to the main class.

In order to compile the class files of Java, we use the gen-class.

(ns tic-tac-toe.main
(:refer-clojure :exclude [==])
(:use [clojure.core.logic])
(:require [tic-tac-toe.game :as game])
(:gen-class
:extends javafx.application.Application)
(:import
[javafx.application Application]
[javafx.scene Scene Group]
[javafx.event EventHandler]
[javafx.scene.canvas Canvas]
[javafx.scene.shape Rectangle Circle Line]
[javafx.scene.text Text Font]
[javafx.scene.paint Color]))
view raw ns.clj hosted with ❤ by GitHub
In the main method calls Application.launch.

(defn -main [& args]
(Application/launch tic_tac_toe.main args))
view raw main.clj hosted with ❤ by GitHub
In the start method implements tic-tac-toe.game.Canvas and register the handler in each panel.

(defn -start [this stage]
(let [root (Group.)
scene (Scene. root)
children (.getChildren root)
canvas (reify game/Canvas
(draw-circle [this panel]
(.add children
(Circle. (+ (:x panel) game/radius)
(+ (:y panel) game/radius)
game/radius)))
(draw-cross [this panel]
(let [{:keys [x y]} panel
x' (+ x game/panel-size)
y' (+ y game/panel-size)]
(doto children
(.add (Line. x y
x' y'))
(.add (Line. x' y
x y')))))
(draw-text [this s]
(.add children
(doto (Text. 0 game/font-size s)
(.setFont font)
(.setFill Color/GRAY)))))]
(doseq [panel game/panels]
(.add children
(doto (Rectangle. (:x panel)
(:y panel)
game/panel-size
game/panel-size)
(.setFill Color/WHITE)
(.setStroke Color/BLACK)
(.setOnMouseClicked (reify EventHandler
(handle [this event]
(game/play canvas panel)))))))
(doto stage
(.setTitle "Tic Tac Toe")
(.setScene scene)
(.setMinHeight game/window-size)
(.setMinWidth game/window-size)
(.setResizable false)
(.show))))
view raw start.clj hosted with ❤ by GitHub
doto is useful when using the GUI library in Clojure.
We can run continuously some methods under the instance.
It is like the instance_eval in Ruby.

Such an interface as EventHandler is obtained an instance by using reify.

Web Start


First, make a standalone jar file with lein2 uberjar.

Then create a jnlp file.

<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec="1.0+" xmlns:jfx="http://javafx.com" codebase="http://halcat0x15a.github.com/tic-tac-toe/" href="tic_tac_toe.jnlp">
<information>
<title>Tic Tac Toe</title>
<vendor>baskingcat</vendor>
<homepage href="http://halcat0x15a.github.com/tic-tac-toe/"/>
<description>tic tac toe</description>
<offline-allowed />
</information>
<security>
<all-permissions />
</security>
<resources>
<j2se version="1.7+" />
<jar href="target/tic-tac-toe-0.1.0-SNAPSHOT-standalone.jar" main="true" />
</resources>
<application-desc main-class="tic_tac_toe.main"></application-desc>
</jnlp>
view raw jnlp.xml hosted with ❤ by GitHub
The all-permissions are needed to run Clojure.

We must make a signature, because it requires all-permissions.

You can make the keystore by keytool, and sign the jar using jarsigner.

keytool -genkey -keystore foo -alias bar
jarsigner -keystore foo target/tic-tac-toe-0.1.0-SNAPSHOT-standalone.jar bar

You can start with Web Start.

If you try on local environment, rewrite the codebase of jnlp file.

Miscellaneous Thoughts


I think that JavaFX is simpler than Swing and we would be able to write the GUI application easily.

But I felt that the Web Start is not suitable for other required language runtime, such as the Scala and Clojure.
Because file size becomes very large.

2012年12月15日土曜日

UndoT

この記事はScalaz Advent Calendarの15日目の記事です。

scalazのcoreのjarに含まれ、独自のパッケージが存在しながらも、全く話題に上がらないundoについて書きます。

scalaz.undo以下にはUndoTとHistoryの2つのデータ型があります。

UndoはHistoryを状態とするStateモナドで、Historyはundo, redoの為のListと、currentのデータを持ちます。

例を作ってみます。

import scalaz._, Scalaz._
import scalaz.effect._, IO._
object Undo extends SafeApp {
import scalaz.undo._, UndoT._
override def runc = for {
(s1, s2) <- (for {
_ <- hput[IO, String]("hello")
_ <- hput[IO, String]("world")
current = UndoT(get[History[String]].lift[IO])
h1 <- undo[IO, String] >> current
h2 <- redo[IO, String] >> current
} yield h1.current -> h2.current).eval("initialize")
_ <- putStrLn(s1) >> putStrLn(s2)
} yield ()
}
view raw undo.scala hosted with ❤ by GitHub
この時、Historyはこのように変遷しています。

History("initialize", Nil, Nil) // eval("initialize")
History("hello", List("initialize"), Nil) // hput("hello")
History("world", List("hello", "initialize"), Nil) // hput("world")
History("hello", List("initialize"), List("world")) // undo
History("world", List("hello", "initialize"), Nil) // redo


これを使っているユーザーがどれだけいるのか気になるところです。

これだけでは物足りないと思ったので、MonadStateについて書こうと思いましたが、UndoTのMonadStateのインスタンスの定義がおかしい気がする。

なので今日はpull requestを投げて終わります。

2012年12月13日木曜日

Codensity

この記事はScalaz Advent Calendarの13日目の記事です。

Codensityについてググると、

The Mother of all Monads

という記事が見つかる。
Codensityは継続モナドとほぼ同じものみたい。

この記事にある例をScalaで書いてみる。

def i[F[+_]: Monad, A](a: F[A]) = new Codensity[F, A] {
def apply[B](f: A => F[B]) = a >>= f
}
view raw i.scala hosted with ❤ by GitHub
Option

val map = Map('foo -> "bar", 'hoge -> "fuga")
(for {
x <- i(map get 'foo)
y <- i(map get 'hoge)
} yield x + y).improve assert_=== Some("barfuga")
view raw option.scala hosted with ❤ by GitHub
Disjunction

type StrDisj[+A] = String \/ A
(for {
x <- i[StrDisj, String]((map get 'foo) \/> "foo")
y <- i[StrDisj, String]((map get 'baz) \/> "baz")
} yield x + y).improve assert_=== "baz".left
view raw disj.scala hosted with ❤ by GitHub
なるほど。
CodensityがOptionやDisjunctionとして動く。

でもこれってIdTじゃ(ry

Codensityは継続モナドのようなもの、ということで継続らしい例を書いてみる。

iprintで、計算の経過を表示する。

def iprint[F[+_]: Monad, A](a: F[A]) = new Codensity[F, A] {
def apply[B](f: A => F[B]) = {
println(a)
a >>= f
}
}
(for {
x <- iprint(map get 'foo)
y <- iprint(map get 'baz)
} yield x + y).improve assert_=== None
view raw iprint.scala hosted with ❤ by GitHub
Some(bar)とNoneが表示される。

継続を破棄するbreakを定義し、for式で使ってみる。

def break[F[+_]: PlusEmpty, A] = new Codensity[F, A] {
def apply[B](f: A => F[B]) = PlusEmpty[F].empty[B]
}
(for {
x <- iprint(map get 'foo)
_ <- break[Option, String]
y <- iprint(map get 'hoge)
} yield x + y).improve assert_=== None
view raw break.scala hosted with ❤ by GitHub
Some(bar)だけが表示される。

無事、計算が破棄された。

Codensityはあるモナドにおいて計算量を減らせることがわかった。
他にも何か出来そうだが、わたしが思いついたのはこれくらい。

2012年12月9日日曜日

Injectiveを考える

この記事はScalaz Advent Calendarの9日目の記事です。

この記事に書いてあることは役にたたないと思うので、あまり気合を入れて読まないでください。

Injective


Scalazには、Injectively, Injective1 ~ Injective5が定義されている。
よくわからない。

Injectiveをググってみると、どうやら単射のことっぽい。

Injectiveは型パラメータをとる型を型引数にとる。
つまり、種(カインド)が* -> *や、* -> * -> *である型が単射であるいう制約をつけるものではないかと考えた。

とりあえずInjectiveの例を列挙してみる。

List

def f[M[_]: Injective, A](m: M[A]) = m
object ListInjective extends Injective[List]
f(List(0))(ListInjective)
view raw list.scala hosted with ❤ by GitHub
ふつうに定義できる。

type member

Hoge#Fは未定義なので、インスタンスの供給は出来ない。

trait Hoge {
type F[A]
def f[A](a: A): F[A]
}
val optionHoge = new Hoge {
type F[A] = Option[A]
def f[A](a: A) = Option(a)
}
object HogeInjective extends Injective[Hoge#F]
/* f(optionHoge.f(0))(HogeInjective) */ // Compile Error
view raw member.scala hosted with ❤ by GitHub
dependent method types

def hogeInjective(hoge: Hoge) = new Injective[hoge.F] {}
f(optionHoge.f(0))(hogeInjective(optionHoge))
view raw depend.scala hosted with ❤ by GitHub
明示的にインスタンスを渡す必要はあるが、コンパイルすることは可能。


ふむ、よくわからない。

単射にならない型がもしあるのだとしたら、F[_]に対して、ある型Aを渡した時にコンパイルが通らないということだろうか。
ということは、F[_]がとりうる型に対して制約をかければよいということだろう。

次のような例を書いた。

trait Foo
trait Bar[F <: Foo]
/* object BarInjective extends Injective[Bar] */ // Compile Error
view raw injective.scala hosted with ❤ by GitHub
なるほど。
確かに、単射ではないからInjectiveのインスタンスが定義出来ない。

Injectiveの意味がようやく理解できた。

InjectiveはLiskovのコードで使われているが、まあ、よくわからない。
わからなくても、この先困るということもないと思う。

2012年12月5日水曜日

ClojureでJavaFX & Web Start

JavaFX Advent Calendar 2012
5日目の記事です。

三目並べ

Cojureで三目並べの続き。

ここでは三目並べのJavaFX実装について書きます。

ゲームのロジックは作ってあるのであとは描画のところを実装するだけ。

Application


JavaFXの基本として、メインクラスにjavafx.application.Applicationを継承します。

ここではgen-classを使って、Javaのclassファイルにコンパイルします。

(ns tic-tac-toe.main
(:refer-clojure :exclude [==])
(:use [clojure.core.logic])
(:require [tic-tac-toe.game :as game])
(:gen-class
:extends javafx.application.Application)
(:import
[javafx.application Application]
[javafx.scene Scene Group]
[javafx.event EventHandler]
[javafx.scene.canvas Canvas]
[javafx.scene.shape Rectangle Circle Line]
[javafx.scene.text Text Font]
[javafx.scene.paint Color]))
view raw ns.clj hosted with ❤ by GitHub
mainメソッドではApplication.launchを呼び出します。

(defn -main [& args]
(Application/launch tic_tac_toe.main args))
view raw main.clj hosted with ❤ by GitHub
あとはstartメソッドでtic-tac-toe.game.Canvasを実装し、各パネルにhandlerを登録します。

(defn -start [this stage]
(let [root (Group.)
scene (Scene. root)
children (.getChildren root)
canvas (reify game/Canvas
(draw-circle [this panel]
(.add children
(Circle. (+ (:x panel) game/radius)
(+ (:y panel) game/radius)
game/radius)))
(draw-cross [this panel]
(let [{:keys [x y]} panel
x' (+ x game/panel-size)
y' (+ y game/panel-size)]
(doto children
(.add (Line. x y
x' y'))
(.add (Line. x' y
x y')))))
(draw-text [this s]
(.add children
(doto (Text. 0 game/font-size s)
(.setFont font)
(.setFill Color/GRAY)))))]
(doseq [panel game/panels]
(.add children
(doto (Rectangle. (:x panel)
(:y panel)
game/panel-size
game/panel-size)
(.setFill Color/WHITE)
(.setStroke Color/BLACK)
(.setOnMouseClicked (reify EventHandler
(handle [this event]
(game/play canvas panel)))))))
(doto stage
(.setTitle "Tic Tac Toe")
(.setScene scene)
(.setMinHeight game/window-size)
(.setMinWidth game/window-size)
(.setResizable false)
(.show))))
view raw start.clj hosted with ❤ by GitHub
ClojureでGUIライブラリを使うときに便利なのがdoto。
あるインスタンスのもとで、メソッドを連続して実行することが出来ます。
Rubyのinstance_evalのようなものですね。

EventHandlerなどのインターフェースはreifyを使うことで実体を得られます。

Web Start


JavaFX Script時代にはいくつかWeb Startのアプリケーションを作ったことが
ありましたが、JavaFX 2になってからは初のWeb Startです。

まずは、lein2 uberjarでstandaloneなjarを作ります。
20MBもあるのはClojure+JavaFXのclassファイルが入ってる所為です。

次にjnlpですが、こんな感じになりました。

<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec="1.0+" xmlns:jfx="http://javafx.com" codebase="http://halcat0x15a.github.com/tic-tac-toe/" href="tic_tac_toe.jnlp">
<information>
<title>Tic Tac Toe</title>
<vendor>baskingcat</vendor>
<homepage href="http://halcat0x15a.github.com/tic-tac-toe/"/>
<description>tic tac toe</description>
<offline-allowed />
</information>
<security>
<all-permissions />
</security>
<resources>
<j2se version="1.7+" />
<jar href="target/tic-tac-toe-0.1.0-SNAPSHOT-standalone.jar" main="true" />
</resources>
<application-desc main-class="tic_tac_toe.main"></application-desc>
</jnlp>
view raw jnlp.xml hosted with ❤ by GitHub
all-permissionsになっているのはClojureを実行するためです。
多分JRubyやGroovyでもall-permissionsが必要になるはず。

all-permissionsを要求するので、署名をしなければなりません。
keytoolで適当なkeystoreを作ります。

keytool -genkey -keystore foo -alias bar

fooというファイルが作られるので、jarsignerを使ってjarに署名します。

jarsigner -keystore foo target/tic-tac-toe-0.1.0-SNAPSHOT-standalone.jar bar

これでWeb Startで起動できます。
実際に試す場合はjnlpファイルのcodebaseを

codebase="file:/home/halcat0x15a/tic-tac-toe/"

のように書き替え、

javaws tic_tac_toe.jnlp

で実行可能です。

雑感


このプログラムではたいしたことをしていませんが、JavaFXのおかげで、Swingよりもシンプルで簡単にGUIを書けるようになったと思います。
他のGUIライブラリと比べても、ライブラリの設計は格段に良くなったと感じます。

Web Startは、ScalaやClojureなどのランタイムが他に必要な言語にはあまりむかないのかなと感じました。
プログラム+ライブラリ+ランタイムとなると、かなりファイルサイズが大きくなってしまいます。

ClojureScriptでgoog.graphics

altjs Advent Calendar 2012
5日目の記事です。

三目並べ

Cojureで三目並べの続き。

ここでは三目並べのClojureScriptによる実装について書きます。

ゲームのロジックは作ってあるのであとは描画のところを実装するだけ。

goog.graphics


描画にはgoog.graphicsを使うことにします。

ClojureScriptはGoogle Closure Libraryで実装されており、nsでJavaScriptのライブラリをrequireすることが出来ます。

(ns tic-tac-toe.main
(:require [cljs.core.logic :as logic]
[tic-tac-toe.game :as game]
[goog.dom :as dom]
[goog.graphics :as graphics]
[goog.events.EventType :as event-type]))
view raw ns.clj hosted with ❤ by GitHub
各種定数。

(def white-fill (graphics/SolidFill. "white"))
(def gray-fill (graphics/SolidFill. "gray"))
(def black-fill (graphics/SolidFill. "black"))
(def black-stroke (graphics/Stroke. 1 "black"))
(def font (graphics/Font. game/font-size "monospace"))
view raw const.clj hosted with ❤ by GitHub
tic-tac-toe.game.Canvasを実装します。

(def g (graphics/createGraphics 300 300))
(def canvas
(reify game/Canvas
(draw-circle [this panel]
(.drawCircle g
(+ (:x panel) game/radius)
(+ (:y panel) game/radius)
game/radius
nil
black-fill))
(draw-cross [this panel]
(let [{:keys [x y]} panel
x' (+ x game/panel-size)
y' (+ y game/panel-size)]
(doto g
(.drawPath (doto (graphics/Path.)
(.moveTo x y)
(.lineTo x' y'))
black-stroke
nil)
(.drawPath (doto (graphics/Path.)
(.moveTo x' y)
(.lineTo x y'))
black-stroke
nil))))
(draw-text [this s]
(.drawText g s 0 0 game/panel-size game/font-size "left" "top" font nil gray-fill))))
view raw canvas.clj hosted with ❤ by GitHub
main関数を定義します。

(defn main []
(doseq [panel game/panels]
(doto (.drawRect g (:x panel) (:y panel) game/panel-size game/panel-size black-stroke white-fill)
(.addEventListener event-type/CLICK
(fn [event]
(game/play canvas panel)))))
(.render g (dom/getElement "canvas")))
view raw main.clj hosted with ❤ by GitHub
HTMLはコンパイルされたJavaScriptを読み込み、main関数を呼び出します。

<!DOCTYPE html>
<html>
<head>
<title>tic tac toe</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<script type="application/javascript" src="main.js"></script>
</head>
<body>
<div id="canvas"></div>
<script type="application/javascript">
tic_tac_toe.main.main();
</script>
</body>
</html>
view raw index.html hosted with ❤ by GitHub
これで動いてほしいところですが、cljs.core.logicがバグってるので、コンパイルされた.jsの修正が必要です。
cljs.core.logic.macros._take_STAR_のところをcljs.core.logic._take_STAR_に変更します。

masterでは直ってますが、修正版がpublishされていないのが悲しいですね。

雑感


いままでもClojureScriptについていろいろ書いてますが、やはりClojureは書いてて楽しいです。                                                            
最近はprotocolとrecordとmacroが好みです。                                                                                                           

論理プログラミングで書いたコードがWeb上で動いているのはなかなかおもしろいと思うのですが、コンパイルされたJavaScriptのコードをみるとものすごくカオスです。

Google Closure Compilerによるアシストがあるとはいえ、ファイルサイズは比較的大きくなるので、注意です。

cljsbuildのおかげで、今回のようなJVMとWebの両方で動くようなコードが書けるので、これからClojureScriptを使う人には是非知ってもらいたいものです。

Clojureで三目並べ

Lisp Advent Calendar 2012
6日目の記事です。

三目並べ

ここでは三目並べソルバについて書きます。

tic tac toe


三目並べ、すなわち◯×ゲームです。
小学生の頃とかやってました。
このゲームは両者が最善を尽せば必ず引分けになるゲームで、その最善手もわかりやすいので他人とやってもだいたいドローになります。

この三目並べで盤面から次の手を返すプログラムを、ClojureとClojureScriptの両方で動くように作成します。

core.logic


今回は論理プログラミングでこのパズルを解きます。

最初にnamespaceですが、ClojureとClojureScriptの両方で動かす為に、多少の黒魔術があります。

(ns tic-tac-toe.core
(:refer-clojure :exclude [==])
;*CLJSBUILD-REMOVE*;(:use-macros [cljs.core.logic.macros :only [defna matcha conda conde fresh project ==]])
)
;*CLJSBUILD-REMOVE*;(comment
(use '[clojure.core.logic :only [defna matcha conda conde fresh project ==]])
;*CLJSBUILD-REMOVE*;)
view raw ns.clj hosted with ❤ by GitHub
;*CLJSBUILD-REMOVE*;はcljsbuildがビルド時に削除してくれるコメントです。
core.logicがclj版とcljs版で違うnamespaceを使っているのでこうなっています。

盤面で、空いているところはnilとします。
not-nil?という述語を作るために、!=を使いたいところですがcljs版に存在しないのでprojectを使います。

(defn not-nil? [m]
(project [m] (== (nil? m) false)))
view raw not-nil.clj hosted with ❤ by GitHub
projectはcore.logicで使われている変数にあたるものから値を取り出します。

盤面が空いていたらコマを置く述語write。

(defna write [s m q]
([s nil q] (== q s)))
view raw write.clj hosted with ❤ by GitHub
コマが2連続で続いていたら自分であろうと相手であろうと決着します。
そのため、コマを優先して置く述語checkを定義します。

(defn check [s
m1 m2 m3
q1 q2 q3]
(matcha [m1 m2 m3]
([m nil m] (conda [(not-nil? m) (== q2 s)]))
([nil m m] (conda [(not-nil? m) (== q1 s)]))
([m m nil] (conda [(not-nil? m) (== q3 s)]))))
view raw check.clj hosted with ❤ by GitHub
これで補助の述語の定義は終りです。
あとは愚直に盤面を検査します。

(defn play [q s
m11 m12 m13
m21 m22 m23
m31 m32 m33]
(fresh [q11 q12 q13
q21 q22 q23
q31 q32 q33]
(== q [q11 q12 q13
q21 q22 q23
q31 q32 q33])
(conda [(check s m21 m22 m23 q21 q22 q23)]
[(check s m12 m22 m32 q12 q22 q32)]
[(check s m11 m12 m13 q11 q12 q13)]
[(check s m31 m32 m33 q31 q32 q33)]
[(check s m11 m21 m31 q11 q21 q31)]
[(check s m13 m23 m33 q13 q23 q33)]
[(check s m11 m22 m33 q11 q22 q33)]
[(check s m13 m22 m31 q13 q22 q31)]
[(write s m22 q22)]
[(write s m12 q12)]
[(write s m21 q21)]
[(write s m23 q23)]
[(write s m32 q32)]
[(write s m11 q11)]
[(write s m13 q13)]
[(write s m31 q31)]
[(write s m33 q33)])))
view raw play.clj hosted with ❤ by GitHub
checkやwriteの順番を並べ変えると強くなったり弱くなったりします。
ここではなるべく中心にコマを置くように書いています。

盤面から次の手を返す述語が定義できました。
もう1つ、ゲームが終了したか調べる述語も定義します。

(defna complete [b m1 m2 m3]
([b s s s] (== b s)))
(defn end [q
m11 m12 m13
m21 m22 m23
m31 m32 m33]
(conde [(complete q m11 m12 m13)]
[(complete q m21 m22 m23)]
[(complete q m31 m32 m33)]
[(complete q m11 m21 m31)]
[(complete q m12 m22 m32)]
[(complete q m13 m23 m33)]
[(complete q m11 m22 m33)]
[(complete q m13 m22 m31)]))
view raw end.clj hosted with ❤ by GitHub
実際の使い方はこんな感じ。

(run* [q] (play q :x
:o nil :x
:x :o nil
:o :o :x))
; ([_.0 :x _.1
; _.2 _.3 _.4
; _.5 _.6 _.7])
(run* [q] (end q
:o :x :x
:o :x :o
:o :o :x))
; (:o)
view raw result.clj hosted with ❤ by GitHub
敵味方の区別をつけていないので思った通りの手が返ってきていませんが、勝てないというのもつまらないのでこのままにします(手抜き)。

これらの関数を使い、ゲームのロジックを書きます。

(ns tic-tac-toe.game
(:require [tic-tac-toe.core :as core])
;*CLJSBUILD-REMOVE*;(:use-macros [cljs.core.logic.macros :only [run*]])
)
;*CLJSBUILD-REMOVE*;(comment
(use '[clojure.core.logic :only [run*]])
;*CLJSBUILD-REMOVE*;)
(def number 3)
(def window-size (* number 100))
(def panel-size (/ window-size number))
(def font-size (/ window-size 5))
(def radius (/ panel-size 2))
(defrecord Panel [x y sym])
(defprotocol Canvas
(draw-circle [this panel])
(draw-cross [this panel])
(draw-text [this text]))
(defprotocol Symbol
(draw [this canvas panel]))
(def o
(reify Symbol
(draw [this canvas panel]
(draw-circle canvas panel))))
(def x
(reify Symbol
(draw [this canvas panel]
(draw-cross canvas panel))))
(def finish? (atom false))
(def panels
(for [x (range number) y (range number)]
(Panel. (* x panel-size)
(* y panel-size)
(atom nil))))
(defn syms []
(map (comp deref :sym) panels))
(defn finish [canvas s]
(draw-text canvas s)
(reset! finish? true))
(defn write [canvas panel sym]
(when-not @finish?
(reset! (:sym panel) sym)
(draw sym canvas panel)
(if-let [result (first (run* [q] (apply core/end q o (syms))))]
(if (= result o)
(finish canvas "Win")
(finish canvas "Lose"))
(when (every? (comp not nil?) (syms))
(finish canvas "Draw")))))
(defn play [canvas panel]
(when-not @(:sym panel)
(doto canvas
(write panel o)
(write (-> (run* [q] (apply core/play q x (syms)))
first
(zipmap panels)
(get x))
x))))
view raw game.clj hosted with ❤ by GitHub
Canvasプロトコルを実装し、play関数を各パネルをクリックした時のhandlerとして登録することで動きます。

JavaFXによる実装goog.graphicsによる実装を書きました。

雑感


core.logicによる論理プログラミングは慣れていないせいか、とても頭をつかいました。
完成したコードは愚直だけれど分かり易いものになりました。
こういった書き方が出来ることが論理プログラミングの1つの利点でもあると思います。

ゲームのロジックに関してはあまり綺麗に書けなかったのですが、各ライブラリと協調できるうまい書き方を知りたいものです。

2012年12月1日土曜日

Isomorphism

Scalaz Advent Calendar!

Isomorphism


あるcase classに対してMonoidを定義したいとき、大体のものはTupleのMonoidのインスタンスが使えると思います。
こんなときにIsomorphismMonoidが使えます。

case class Person(name: String, age: Int)
object Person {
type T = (String, Int)
object PersonTuple extends (Person <=> T) {
def from = Function.tupled(Person.apply)
def to = Person.unapply _ >>> (_.get)
}
implicit object PersonMonoid extends IsomorphismMonoid[Person, T] {
def G = Monoid[T]
def iso = PersonTuple
}
}
view raw person.scala hosted with ❤ by GitHub
scala> import scalaz._, Scalaz._
import scalaz._
import Scalaz._
scala> mzero[Person]
res0: Person = Person(,0)
scala> Person("hoge", 2) multiply 3
res1: Person = Person(hogehogehoge,6)
view raw repl.scala hosted with ❤ by GitHub
Monoid以外の型クラスを定義したい時もこんな感じで使える。

implicit object PersonShow extends IsomorphismShow[Person, T] {
def G = Show[T]
def iso = PersonTuple
}
implicit object PersonOrder extends IsomorphismOrder[Person, T] {
def G = Order[T]
def iso = PersonTuple
}
view raw iso.scala hosted with ❤ by GitHub
このように、データ構造が同じもののインスタンスを流用する場合はIsomorphismが使えます。

<=>[A, B]はIso[Function1, A, B]のtype aliasで、to: A => Bとfrom: B => Aを定義します。
IsomorphismMonoidなどの型クラスは、toを使って実装されていることに注意しましょう。

2012年10月31日水曜日

ClojureでLisp評価器

SICP読書会、毎週月曜19時からmixiでやってます。
現在第4章の超循環評価器を作っています。

4章の一番最初に出てくるevalの定義は以下の通り

(define (eval exp env)
(cond ((self-evaluating? exp) exp)
((variable? exp) (lookup-variable-value exp env))
((quoted? exp) (text-of-quotation exp))
((assignment? exp) (eval-assignment exp env))
((definition? exp) (eval-definition exp env))
((if? exp) (eval-if exp env))
((lambda? exp)
(make-procedure (lambda-parameters exp)
(lambda-body exp)
env))
((begin? exp)
(eval-sequence (begin-actions exp) env))
((cond? exp) (eval (cond->if exp) env))
((application? exp)
(apply (eval (operator exp) env)
(list-of-values (operands exp) env)))
(else
(error "Unknown expression type -- EVAL" exp))))
view raw eval.scm hosted with ❤ by GitHub
とてもわかりやすいけど、データ主導にしたいよね、そうしよう、というのが問題4.3にあります。

これをClojureっぽく書くことを考えてみる。

Protocol


ClojureといったらやっぱProtocolだよね!ということでプロトコルベースで考える。
とりあえず評価器プロトコル、Eval(uator)の定義

(defprotocol Eval
(eval [this env exp]))
view raw eval.clj hosted with ❤ by GitHub
このプロトコルを実装したリテラルの評価器を書いてみる。

(def nothing (Object.))
(defn literal? [exp]
(or (true? exp)
(false? exp)
(nil? exp)
(number? exp)
(string? exp)))
(def literal
(reify Eval
(eval [this env exp]
(if (literal? exp)
exp
nothing))))
view raw literal.clj hosted with ❤ by GitHub
部分関数っぽくしたいのだけど、例外を投げるか迷った。
今回はとりあえずこれで。

このように、小さな評価器をいっぱい作って合成していくという考え方。

special form


ifやdefineやbeginなんかのspecial formをチェックする関数tagged?の定義

(defn tagged? [tag exp]
(and (seq? exp)
(-> exp empty? not)
(-> exp first (= tag))))
view raw tagged.clj hosted with ❤ by GitHub
先頭要素を比較するだけ。

これを使って、special formを以下のように表してみる

(deftype Special [tag f]
Eval
(eval [this env exp]
(if (tagged? tag exp)
(f env (rest exp))
nothing)))
view raw special.clj hosted with ❤ by GitHub
Specialを使ったquote

(def quotation
(Special. 'quote
(fn [env exp]
(first exp))))
view raw quotation.clj hosted with ❤ by GitHub

if


ifは式を評価しなければいけない。
しかし、どの評価器を使えばいいのかわからない。

(def divergence
(Special. 'if
(fn [env exp]
(if (->> exp first (eval ? env))
(eval ? env (nth exp 1))
(eval ? env (nth exp 2))))))
view raw divergence0.clj hosted with ❤ by GitHub
評価器を引数にとるという選択肢もあるけど、不都合がある。

(def lisp (compose literal
quotation
(divergence lisp)))
view raw lisp0.clj hosted with ❤ by GitHub
この式を評価するとよくわからないエラーが投げられると思う。
ここでは評価機が部分適用されたeval関数をとることにする。

(defn divergence [f]
(Special. 'if
(fn [env exp]
(if (->> exp first (f env))
(f env (nth exp 1))
(f env (nth exp 2))))))
view raw divergence.clj hosted with ❤ by GitHub

compose


一番大事な評価器を合成する関数

(defn compose [e & es]
(reduce (fn [e e']
(reify Eval
(eval [this env exp]
(let [result (eval e env exp)]
(if (= result nothing)
(eval e' env exp)
result)))))
e
es))
view raw compose.clj hosted with ❤ by GitHub
やっていることは簡単、引数の先頭の評価器から順に、式を評価器で評価して失敗したら次の評価器で評価するという評価器を作っているだけ。

これを使ってifとliteralだけが使えるlispを定義すると以下のようになる。

(def lisp
(compose literal
(divergence (fn [env exp]
(eval lisp env exp)))))
view raw lisp.clj hosted with ❤ by GitHub
実行

user=> (eval lisp {} '(if true 1 2))
1
user=> (eval lisp {} '(if false 1 2))
2
view raw result.clj hosted with ❤ by GitHub
ヤッター

まとめ


こんな感じで小さな評価器を書いていって大きな評価器を作っています。
なかなか楽しい。

今回のプログラムはScalaBaseで@maeda_さんが発表されていた、Javascript as an Embedded DSLに触発されて書いてみました。
おもしろいので読んでみるとよいです。

次はぱーさーこんびねーたについて書く、書きたい、書けたらいいな。

2012年10月27日土曜日

Clojureのエラー処理

Clojureでエラー値を返すかもしれない関数ってどう書くのだろうとちょっと考えてみる。

Scalaだと例外はあまり使わず、EitherやValidationを使う派。
やはり、コンパイラの恩恵を受けることが出来る書き方が良い。

しかし、Clojureは動的型付けの言語。
コンパイラの恩恵は特にない。

なので、 どのような値が返ると扱いやすいかを考える。

Clojureは基本的に
  • nil返す
  • 例外投げる
の2通りだと思う。

Clojure標準のライブラリはnilを渡してもそれなりに動作するし、if-let,when-let,fnilなどの関数も用意されている。
しかし、nilだとどのようなエラーだったのか判別がつかない。
このような時に例外を使って、エラーの種類によって投げる例外を変えれば良いのだと思う。

Clojureの例外を投げるような関数は以下のように書ける。

(defn nthv [n vec]
(if (integer? n)
(if (and (pos? n) (< n (count vec)))
(get vec n)
(throw (IndexOutOfBoundsException.)))
(throw (IllegalArgumentException.))))
view raw ex.clj hosted with ❤ by GitHub
この場合は、Java APIのIllegalArgumentExceptionとIndexOutOfBoundsExceptionを使った。
しかし、何時でもJava APIに自分が欲しいExceptionが定義されているわけではない。
このような時は、独自の例外を作るだろう。

駄菓子菓子!

Clojureでclassを定義するのはとても面倒。
nsgen-classを見てもらえればわかると思う。
Throwableがinterfaceならば、deftypeやdefrecordが使えたのだけれど・・・

例外を定義する部分だけJavaで書いたほうが楽かも。

class MyException extends Exception {}
view raw ex.java hosted with ❤ by GitHub
leiningenはJavaもサポートしているので、共存させるのは苦ではない。

結論


基本的にnilを返す。
独自の例外を定義したかったらJavaを使うと楽

2012年10月13日土曜日

Clojureでモナド内包表記とStateモナド

cljsbuildのcrossoversという機能を知らない所為で今まで無駄に苦労してきましたが、これでマクロが使えないという制約から解き放たれました。
万歳!cljsbuild!

マクロが使えるようになってまず一番最初にやることは何か?
もちろん、モナド内包表記の実現ですね!

HaskellのdoよりはScalaのforの方が慣れているのでこちらを採用しました。

動的型付けの言語でのモナドの実現はいくつか見てきましたが、明示的にコンテキストを指定するタイプはあまり好かないので、ClojureらしくProtocolで表現することにしました。

Monadic Protocol


通常モナドはunitとbindが実装され、モナド則
  • bind f (unit x) == f x
  • bind unit m == m
  • あともう一個なんかあった気がする
を満たしますが、動的型付けの言語でunitを実現するのは面倒なので、モナドっぽいインターフェースを定義します。

(defprotocol Monadic
(fmap [m f])
(bind [m f]))
view raw monadic.clj hosted with ❤ by GitHub
このインターフェースが実装されていれば、モナド内包表記が使えるようになります。
とてもScala的。

モナド内包表記


Lispは神の言語と皆ネタにしていますが、Lispのマクロは本当に強力です。
既存の言語にある構文の全てはLispのマクロで表現できるのではと思えるくらいです。

Clojureの標準ライブラリではパターンマッチが出来ないので、準標準ライブラリであるcontribのcore.matchを使います。

(defmacro do-m [bindings expression]
(match [bindings]
[[name value]]
`(fmap ~value (fn [~name] ~expression))
[[name value & bindings']]
`(bind ~value (fn [~name] (do-m ~bindings' ~expression)))))
view raw do_m.clj hosted with ❤ by GitHub
しんぷるいずべすと、MonadPlusなんて知らないのでガードは実装しません。めんどい。

このdo-mマクロがどのように展開してくれるかというと

(do-m
[a m
b (f a)]
(g a b))
view raw expand.clj hosted with ❤ by GitHub


(bind m (fn [a] (fmap (f a) (fn [b] (g a b)))))
view raw expanded.clj hosted with ❤ by GitHub
こんな感じになります。

実際に動かしてみます。

user=> (defn bind [m f] (mapcat f m))
#'user/bind
user=> (defn fmap [m f] (map f m))
#'user/fmap
user=> (do-m [a [1] b [(inc a)]] (+ a b))
(3)
view raw example.clj hosted with ❤ by GitHub
動いたー!
素敵です。

Stateモナド


私が現在製作中のアプリケーションでは、レコードを更新する関数が多数定義されています。
これは、状態をとって、新しい状態を返す関数ですが、少し問題があります。

一つ例を示します。

(defrecord Cursor [x y])
(defn right [n cursor]
(assoc cursor :x (+ (:x cursor) n)))
(defn down [n cursor]
(assoc cursor :y (+ (:y cursor) n)))
view raw cursor.clj hosted with ❤ by GitHub
このような関数が定義されていた時、右下へ移動する関数を定義するのに->>マクロを使い、手続き的に書くことができます。

(defn right-down [n cursor]
(->> cursor
(right n)
(down n)))
view raw right_down.clj hosted with ❤ by GitHub
しかし、このrightでの計算結果、つまりxを使う関数を定義する時、コードはこのようになります。

(defn square [n cursor]
(let [cursor' (right n cursor)]
(down (:x cursor') cursor')))
view raw square.clj hosted with ❤ by GitHub
このパターンが関数内で頻出する場合、一時的なcursorの状態を保存する為の変数が増えてしまいます。
さらに、この場合はxを取得するコストは微々たるものですが、フィールドの取得に複雑なロジックを用いたり、計算値が状態の更新に間接的に使われるだけで、レコードからは取得できない場合があります。

新しい状態だけを返すのではなく、計算値とのペアで返せばこのパターンを解決できます。

(defn right' [n cursor]
(let [x (+ (:x cursor) n)]
[(assoc cursor :x x) x]))
view raw right.clj hosted with ❤ by GitHub
しかし、->>マクロのようにネストせずに書けるわけではありません。

そこでStateモナドの出番です!(導入長い!

Stateモナドは状態を取り、新しい状態と計算値のペアで返す関数に対してモナドを定義したものです。

先ほど定義したMonadicをStateに実装してみます。

(defrecord State [f]
Monadic
(fmap [s g]
(State. (fn [s]
(let [[s' a] (f s)]
[s' (g a)]))))
(bind [s g]
(State. (fn [s]
(let [[s' a] (f s)]
((:f (g a)) s'))))))
view raw state.clj hosted with ❤ by GitHub
right, down関数をStateで定義してみます。

(defn right [n]
(State. (fn [cursor]
(let [x (+ (:x cursor) n)]
[(assoc cursor :x x) x]))))
(defn down [n]
(State. (fn [cursor]
(let [y (+ (:y cursor) n)]
[(assoc cursor :y y) y]))))
この時、square関数はdo-mを使って以下のように定義できます。

(defn square [n]
(do-m [x (right n)
s (down x)]
s))
ヤッター!

関数の合成が手続き的に書けますね。

まとめ


マクロ強力。Stateモナド美しい。

本当はFreeモナド使おうと思ったけどStateモナドの素晴らしさを伝えたかったので控えました。

これから70個近い関数をStaeモナドに書き換える作業が待ってます。
Freeモナドの資料も作らなきゃですね。
私の代わりに大学のC言語の課題をやってくれるといいと思います。

2012年10月3日水曜日

Clojureへの不満とか

最近はClojureばかり書いているので、良い所ばかりでなく、気になるところも見えてきました。

なにか意見をもらえるかもしれないので書き並べておきます。

セルフホスティングして欲しい


Clojureのソースコードを読むときに、高確率でJavaのコードを読まなければいけないので悲しくなります。
やはり、gen-classとかでは物足りないのかなあ?
それとも、セルフホスティングにはあまりヤル気がないのか・・・・

clojure.langのJavaDoc欲しい


ソースコード追うのが面倒なので。

Protocolをもっと使って欲しい


自分で新しいデータ型を作った時に、既存の関数を使うためにclojure.langの得体の知れないinterfaceを実装するわけですが、Protocolとして存在すればClojureの中で完結するのでうれしいのだけど、これはデータ型がJavaに依存しすぎてて簡単にはいかないんだろうなあと。

関数が返す値は適用する値と同じデータ型で返して欲しい


特にStringとか。
charのシーケンスで返ってくるのが悲しい。
これはなぜだろう、効率のためかな?
どこかで読んだ気がしなくもない。

いっそのことClojure独自に文字列作るというのは・・・・まあ、それはそれで面倒臭そう。

Scalazは独自にCordというデータ型を定義しています。
さすが、尖ってる。

insertしたい


ArrayListLinkedListを使えばいいのですが、functionalでpurelyなdata structureが欲しいわけです。

Arrowが欲しい


Haskellのfirst,secondに当たるものが欲しい。
分配束縛したくない。
もっとポイントフリーなプログラミングがしたいのです。

しかし、comppartialjuxtでやりすぎたコードを書くのには注意です。

if-let, when-letが1つの変数しか束縛できない


なぜ複数束縛できないのか・・・・・

要素を1つだけ持つlistとmapmapcatまたはforを使って、Maybeモナドっぽいことは出来ます。
あとは(fn [a] (if (nil? a) (list) (list a)))のようなものがあるといいのだけど。

do記法、for内包、コンピュテーション式にあたるものが欲しい


for内包はあることにはあります。
しかし、これはSequableでSequentialなデータ型(JavaのIterableとIteratorのようなもの?)の為のもので、モナドの為のマクロではないです。

Maybeモナドやdo記法はalgo.monadsで提供されていますが、あまり設計に納得がいかない。
私はあるデータ型に対してモナドを定義したいので、Protocolをベースに設計したものが欲しかった。

限定継続が欲しい


do記法がないなら限定継続を使えばいいじゃないと昔の貴族は言ったものです。
限定継続があれば括弧を減らせるような構文がいろいろと簡単に定義できそうだなあと思い挙げてみた。 

delimicといものがあるらしい。

まとめ


私はいままではScalaばかり書いていましたが、いまはClojureばかり書いているのでこのような記事を書いています。
実際には2年前くらいから少しずつ書いていましたが、これほどClojureを書いてる期間はありませんでした。

Scalaに触れ、さらにScalazに触れたことで完全に関数型プログラミング信者になってしまいましたが、実際に有用な技法を多く知ることが出来ました。
それをClojureで実現するために、このような贅沢な不満が出てきました。

Clojureはまだまだ若い言語だと思うので、あれもこれもと求め過ぎていると思います。
なので、今後私が期待しているものに発展してくれるようClojureに貢献していきたいです。

2012年9月13日木曜日

ClojureScriptのテスト

皆さんテスト書いてますか?
私はあまり書いてません。
テストは大事です、書きましょう。

私は現在ClojureScriptを使っているのですが、JavaScriptでのテスト方法とかよくわからないし、Clojureでテスト書きたいので、ClojureからClojureScriptを読み込もうという考えに至りました。

が、しかし。
requireなどを使って、ClojureScriptのコードを読み込むことはできませんでした。
なぜなら拡張子がcljsだから。
load-fileなども使ってみましたが、無理でした。
ClojureScriptのコードの中でrequireを使っているからです。

どうにかして拡張子がclj,classのファイル以外を読み込むことはできないのかと調べてみたところ・・・・

https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/RT.java#L403

ベタ書き・・・!
差し替えることなど到底無理っぽい。

ならば作るしかありません、cljファイルを。

(ns onedit.test
(:require [clojure.java.io :as io])
(:use clojure.test))
(def names ["foo" "bar"])
(defn cljfile [name]
(io/file (str "test/geso/" name ".clj")))
(defn cljsfile [name]
(io/file (str "src-cljs/geso/" name ".cljs")))
(defn setup []
(doseq [name names]
(io/copy (cljsfile name) (cljfile name)))
(doseq [name names]
(use (symbol (str "geso." name)))))
(defn cleanup []
(doseq [name names]
(io/delete-file (cljfile name))))
(setup)
(defn fixture [f]
(f)
(cleanup))
(use-fixtures :once fixture)
(deftest geso
(testing "Foo Functions"
(testing "geso geso"
(is (geso?)))))
view raw gistfile1.clj hosted with ❤ by GitHub

copyしてuseしてdeleteする、悲しみあふれるコード。
cleanupのところはtry-finallyした方が良いかも。

clojure.testはfailした時にマクロを利用して述語に渡した値を展開してくれたりでなかなか便利なので割と好きです。
しかし、ここまでして使うのもどうかなと。

素直にJavaScriptのテストフレームワークを使うとか、assertで済ませる(ClojureScript本体のテスト方法)などでいいかもしれません。

2012年9月10日月曜日

Scalaでdo記法

函数プログラミングの集いで限定継続の話が面白かったので書いておきます。

ScalaでもHaskellのdo記法っぽく書ける。

調度良くScalaでHaskellしてる記事があったので、これを参考にさせていただきます。

import scala.language._
import scala.util.continuations._
import scalaz._, Scalaz._
import effect._, IO._
import scalaz.syntax.Ops
object Do {
sealed trait DoBindOps[F[_], A] extends Ops[F[A]] {
implicit val F: Bind[F]
val self: F[A]
def bind[B] = shift((k: A => F[B]) => self >>= k)
}
implicit def ToDoBindOps[F[_]: Bind, A](fa: F[A]) = new DoBindOps[F, A] {
val F = implicitly[Bind[F]]
val self = fa
}
def apply[F[_]: Bind, A](fa: => F[A] @cps[F[A]]) = reset(fa)
}
object Main extends SafeApp {
import Do._
override def runc = Do {
ch07.basicio.bind[Unit]
ch07.callingpure
}
object ch07 {
def basicio = Do {
putStrLn("Greetings! What is your name?").bind[Unit]
val inpStr = readLn.bind[Unit]
putStrLn(s"Welcome to Haskell, $inpStr!")
}
def callingpure = {
def name2reply(name: String) = {
val charcount = name.length
s"""Pleased to meet you, $name.
Your name contains $charcount characters."""
}
Do {
putStrLn("Greetings once again. What is your name?").bind[Unit]
val inpStr = readLn.bind[Unit]
val outStr = name2reply(inpStr)
putStrLn(outStr)
}
}
}
}
view raw gistfile1.scala hosted with ❤ by GitHub
\めんどい/

2012年9月5日水曜日

Clojureのinsert

Clojureでlistやvectorのn番目に値をinsertしたい時はありませんか?
Clojureには標準でlistやvectorに対するinsertが定義されていません。
でもzipperにはinsertがあるみたい。
そこでvectorとzipper、どちらのinsertが速いか測ってみました。

コード


(ns bench-insert
(:require [clojure.zip :as zip]))
(defn insert-zipper [n a z]
(if-let [l (zip/down z)]
(zip/up
(loop [n n l l]
(cond (zero? n) (zip/insert-left l a)
:else (if-let [r (zip/right l)]
(recur (dec n) r)
(zip/insert-right l a)))))
(zip/insert-child z a)))
(defn insert-vector [n a v]
(let [[x y] (split-at n v)]
(vec (concat x (list a) y))))
(defn bench [f z n]
(time
(reduce #(f (rand-int %2) %2 %1) z (range n))))
view raw insert.clj hosted with ❤ by GitHub

結果


ClojureScript

ClojureScript:bench-insert> (bench insert-zipper (zip/vector-zip []) 200)
"Elapsed time: 2488 msecs"
[[199 193 5 4 99 133 64 37 176 87 169 106 117 154 9 42 60 130 181 44 94 122 13 11 192 182 153 47 3 197 156 149 172 155 113 147 19 68 134 165 148 101 171 123 151 107 162 1 168 27 7 120 17 163 21 95 170 112 198 79 53 98 194 61 166 70 84 118 26 25 100 56 62 59 97 160 71 66 51 144 145 143 77 164 29 23 78 49 109 31 129 116 43 32 114 80 36 55 74 135 92 167 83 124 188 173 91 75 22 81 6 175 86 30 177 93 137 180 104 45 150 189 140 146 18 20 108 38 58 105 65 126 184 195 110 142 90 85 41 24 191 121 190 186 119 63 139 179 138 40 50 178 127 128 131 125 115 15 35 39 69 8 185 48 73 183 12 33 54 174 161 111 187 157 57 72 158 132 152 96 136 34 14 16 2 28 103 196 102 46 141 76 89 67 159 88 10 82 52 0] {:changed? true}]
ClojureScript:bench-insert> (bench insert-vector [] 200)
"Elapsed time: 1470 msecs"
[121 106 120 198 2 14 132 52 182 175 160 162 42 125 148 98 28 90 193 66 80 170 23 171 126 67 74 17 53 83 190 57 150 40 76 10 145 5 166 140 186 114 167 124 179 157 87 16 41 123 70 20 156 119 27 110 176 164 49 102 15 187 88 86 24 96 142 89 196 129 34 55 144 111 101 118 85 58 115 73 91 154 71 138 136 78 165 122 72 194 158 195 130 197 185 63 18 26 82 30 84 192 32 152 37 141 143 127 11 92 183 146 184 199 139 69 109 62 103 169 79 77 65 31 60 12 13 35 75 188 172 25 178 54 149 8 99 116 93 64 189 137 4 173 21 6 39 47 97 95 168 36 7 1 9 180 29 174 163 128 112 131 48 133 45 134 147 50 100 161 117 43 22 51 33 68 94 191 108 19 56 153 3 177 181 61 38 81 44 113 155 135 105 151 159 107 59 104 46 0]
ClojureScript:bench-insert>
view raw cljs.cljs hosted with ❤ by GitHub
Clojure

bench-insert=> (bench insert-zipper (zip/vector-zip []) 200)
"Elapsed time: 17.650291 msecs"
[[69 166 146 84 59 77 130 52 175 4 125 126 179 172 188 181 193 76 74 34 64 177 66 30 173 184 121 51 60 178 11 156 134 194 17 102 62 185 5 43 71 83 87 138 196 167 38 169 187 137 106 115 1 162 101 180 79 131 109 105 42 136 65 95 75 57 55 108 8 160 191 45 157 133 114 53 155 153 103 54 111 41 171 176 2 40 15 118 31 96 28 145 22 189 37 48 151 143 129 78 128 14 82 63 140 26 81 89 91 86 161 10 124 92 117 127 58 186 190 32 88 12 33 144 132 36 163 198 119 16 158 85 195 116 98 90 148 23 47 3 123 170 113 174 80 46 183 72 122 9 7 147 56 100 24 73 25 150 142 152 99 120 21 67 97 139 68 149 110 159 197 165 192 94 164 61 93 20 6 141 135 44 13 199 29 50 112 35 182 107 27 168 19 49 104 39 18 154 70 0] {:changed? true}]
bench-insert=> (bench insert-vector [] 200)
"Elapsed time: 10.157477 msecs"
[90 154 123 110 174 83 3 63 104 94 60 50 105 191 19 40 86 84 46 57 162 28 176 146 65 153 199 67 30 10 61 102 8 196 20 38 138 21 165 155 23 168 136 43 99 96 116 18 69 39 68 109 58 173 198 139 26 62 14 52 29 106 134 97 1 98 135 131 147 78 42 113 9 144 151 111 16 148 17 150 36 72 184 169 142 130 167 107 170 117 152 95 76 179 89 49 81 112 54 15 163 73 75 25 37 100 194 32 85 120 13 180 166 181 114 115 119 143 164 183 137 161 74 182 108 93 64 55 185 190 7 48 31 145 175 195 33 12 88 92 118 188 22 158 87 187 45 129 2 186 197 71 177 122 56 140 156 66 172 34 59 159 160 24 189 157 41 133 178 47 44 82 126 171 77 124 35 4 91 51 193 132 53 149 5 79 192 128 11 70 6 125 101 141 103 80 121 27 127 0]
bench-insert=>
view raw clj.clj hosted with ❤ by GitHub
bench-insert=> (bench insert-zipper (zip/vector-zip []) 1000)
"Elapsed time: 306.185331 msecs"
[[181 558 125 742 420 462 193 689 845 8 335 906 73 702 329 554 37 436 633 140 846 941 496 739 281 737 735 175 397 153 732 372 656 84 19 297 810 220 484 274 495
313 884 797 567 593 366 659 13 400 691 653 415 245 690 18 497 454 75 728 852 564 468 70 615 612 965 987 980 486 671 266 654 20 250 413 544 503 777 270 50 202
977 323 406 606 80 635 955 385 663 90 392 433 783 641 30 214 947 156 470 718 467 439 628 135 197 917 540 312 278 238 499 465 509 758 717 111 913 207 374 780 60
2 186 145 803 273 956 218 827 441 657 621 114 267 215 376 666 548 17 650 440 464 932 799 525 311 46 35 569 430 696 322 651 713 421 898 60 449 263 553 217 353 6
8 142 922 450 173 741 859 704 792 935 820 970 27 480 861 879 871 176 242 831 578 187 318 757 96 190 707 15 672 950 209 150 53 679 240 620 87 74 508 269 336 310
727 110 547 936 228 85 219 964 573 399 882 103 616 256 396 9 841 91 314 502 909 54 350 308 755 610 171 501 221 21 2 6 716 348 878 386 337 798 557 327 738 62 3
19 178 309 864 301 129 953 105 11 519 836 411 885 367 23 937 443 394 768 594 288 167 133 870 64 258 537 873 412 401 687 639 623 725 705 143 51 849 762 7 693 338 448 342 563 940 159 907 212 425 86 49 409 543 597 877 98 782 185 596 875 726 745 223 435 584 999 795 695 155 368 586 862 57 960 887 756 994 315 78 581 746 667 681 839 900 198 44 170 161 99 601 227 152 990 821 92 222 276 183 866 260 840 951 77 813 475 67 943 976 466 640 770 285 763 802 453 968 166 88 320 764 948 905 814 247 858 588 851 562 682 444 834 379 473 570 734 863 614 339 658 384 115 505 244 894 147 972 121 649 642 754 847 536 749 500 791 56 307 438 974 785 134 662 184 24 886 132 304 660 824 637 434 535 494 867 211 290 131 835 902 895 117 729 261 817 280 761 160 427 194 402 673 545 664 417 559 962 528 404 375 723 457 476 925 36 524 52 493 856 306 506 670 58 118 971 526 600 796 3 740 709 162 382 619 585 874 128 589 45 724 284 865 208 511 378 474 592 571 928 38 41 701 632 617 611 931 753 179 805 259 299 201 876 22 25 916 71 451 196 426 822 429 373 767 12 488 804 294 683 733 773 541 344 163 395 627 452 293 151 324 122 232 736 455 869 929 772 919 995 407 844 357 880 924 157 295 102 461 993 510 954 542 29 979 648 978 393 698 561 515 287 289 843 246 168 361 277 857 934 101 431 498 747 483 829 365 490 418 826 625 491 975 912 262 292 838 938 731 914 326 391 231 927 973 398 646 714 833 355 112 94 576 422 779 680 560 986 340 697 471 998 966 530 139 715 206 952 685 807 579 241 192 371 781 769 712 61 603 477 360 575 959 711 130 119 359 203 539 272 66 692 286 189 626 205 369 684 387 778 216 790 298 933 164 126 992 63 234 283 522 456 165 239 609 719 848 330 489 233 106 580 629 818 983 577 136 832 69 364 180 568 124 334 675 529 100 534 765 538 1 381 981 235 918 255 43 837 775 751 28 478 358 76 154 686 177 243 899 347 888 389 257 317 55 72 703 146 574 595 957 447 305 14 631 423 787 552 204 720 599 251 872 424 296 174 282 144 328 200 459 254 479 788 127 141 939 910 923 97 721 678 172 636 881 527 752 604 806 195 383 104 630 34 963 550 997 442 414 331 988 512 638 487 249 608 123 546 815 408 303 891 26 996 750 668 661 760 116 377 921 343 507 253 624 333 710 158 669 784 897 437 854 137 107 903 516 655 59 169 236 39 95 237 482 16 771 652 776 42 445 613 766 4 325 463 967 40 868 598 896 79 643 674 582 961 744 345 229 10 853 109 618 801 828 985 676 5 182 566 362 492 688 605 432 302 908 645 265 825 549 346 816 949 416 901 352 700 850 370 31 481 224 520 587 191 926 555 812 982 363 647 81 419 349 748 583 532 113 390 969 883 743 89 248 930 460 565 860 199 380 722 356 291 108 410 904 944 351 889 148 47 644 268 794 809 226 823 82 138 607 830 264 300 786 677 514 405 513 83 551 893 332 789 759 911 469 556 213 699 892 504 523 188 591 149 533 946 984 774 945 518 819 665 33 855 225 48 316 354 341 403 271 915 706 800 989 531 572 388 458 321 590 708 93 485 808 942 842 230 279 210 521 890 920 958 120 446 65 472 991 622 811 793 32 252 428 634 730 517 275 694 0] {:changed? true}]
bench-insert=> (bench insert-vector [] 1000)
"Elapsed time: 129.433545 msecs"
[373 401 356 943 386 720 51 217 641 42 457 564 806 859 560 251 276 303 34 959 308 206 405 207 665 779 508 793 957 350 309 365 782 87 137 89 181 188 514 676 526 167 545 170 353 250 647 601 214 800 821 668 124 711 535 241 795 870 76 339 906 369 299 306 598 632 102 81 580 594 171 393 389 247 975 468 826 94 140 145 816 114 275 942 302 106 47 95 540 88 125 667 423 100 873 455 237 228 127 952 65 6 572 429 243 285 451 316 212 301 355 987 544 17 765 582 842 416 974 254 803 516 639 454 968 133 404 876 74 144 933 325 893 698 634 622 804 849 643 650 467 36 867 490 377 501 218 565 259 231 902 687 363 575 586 360 364 637 570 341 524 43 547 104 256 747 851 387 22 976 877 786 552 731 935 929 166 538 701 205 157 938 261 288 345 813 899 739 315 805 742 99 219 294 499 471 351 539 409 527 67 945 479 318 406 221 618 966 39 970 77 614 5 312 944 190 86 25 450 600 465 636 761 950 672 529 131 438 201 333 832 23 321 146 287 759 28 44 459 707 887 520 242 713 239 825 253 861 48 998 775 385 700 101 754 434 985 245 587 445 332 554 794 477 948 983 908 802 843 994 543 984 90 523 381 204 612 912 208 562 378 431 512 745 92 98 770 503 382 783 840 845 461 888 623 421 785 722 946 780 810 165 186 903 644 808 868 80 932 149 550 949 680 992 691 651 193 635 238 168 941 936 669 71 234 607 430 109 192 446 730 581 189 472 162 83 331 926 727 828 732 714 724 335 161 120 820 60 148 620 602 784 497 712 266 989 915 403 75 330 621 982 138 605 579 862 122 224 45 513 916 772 264 240 521 322 338 70 273 252 558 799 72 573 534 249 183 988 223 978 738 542 61 126 209 510 964 420 112 642 458 729 569 139 371 857 448 537 750 999 807 53 972 662 466 56 583 422 895 624 507 504 818 886 692 901 856 858 317 82 963 8 652 833 684 723 548 939 835 96 505 142 640 649 484 710 271 184 408 726 773 546 346 596 682 200 447 506 384 613 153 134 342 762 905 481 568 881 909 414 195 954 604 337 310 324 531 666 326 681 519 734 278 411 119 511 980 11 269 13 313 147 626 156 824 616 764 962 49 267 279 182 334 440 555 663 418 64 84 29 836 236 766 556 848 817 113 449 229 199 68 419 388 20 628 14 844 476 756 777 955 216 135 898 235 62 314 295 740 85 424 694 518 415 655 875 907 837 361 811 442 675 659 879 860 177 117 585 743 164 904 653 274 758 855 488 460 752 320 852 391 995 169 654 853 277 154 549 319 606 110 118 357 487 897 914 260 719 757 559 397 172 311 883 4 54 359 130 595 24 530 150 781 592 38 796 32 603 55 819 27 197 290 19 703 928 10 797 475 717 178 515 693 921 646 986 115 103 2 633 831 15 889 736 413 863 704 715 160 456 225 815 660 690 865 474 699 270 725 121 402 981 292 509
610 396 917 370 50 194 491 584 305 33 685 191 383 464 494 227 159 884 52 426 615 735 891 885 993 196 977 677 143 3 551 176 263 37 563 425 925 272 872 410 284 7
06 814 593 123 30 281 536 599 185 958 328 894 262 453 46 927 492 59 953 910 26 390 741 755 73 532 180 163 934 432 443 721 392 670 358 211 716 398 293 749 179 6
83 268 937 175 380 463 827 93 158 961 58 258 697 688 778 329 619 920 291 525 588 533 483 705 823 571 323 997 173 578 343 790 232 561 589 748 965 679 822 661 69
6 869 590 969 41 394 591 220 792 215 991 116 774 947 298 854 478 202 631 709 541 132 787 376 973 673 210 283 79 763 69 489 428 753 1 923 435 629 427 890 971 25
7 375 880 656 433 645 349 911 230 485 439 486 967 718 744 372 347 812 66 9 286 798 996 12 296 412 922 617 608 930 918 648 174 480 7 695 671 470 771 63 136 960
436 576 557 566 658 367 789 437 105 990 896 233 57 379 630 517 979 788 336 498 866 469 151 767 297 850 625 496 18 495 21 108 280 444 152 473 809 768 452 246 87
1 362 502 882 878 864 847 627 838 248 769 226 674 348 609 834 340 830 97 141 244 493 289 567 374 462 213 399 956 657 35 874 129 187 801 327 441 352 395 846 522
638 839 366 16 737 40 746 597 611 574 31 728 776 400 931 304 482 892 577 111 940 678 222 198 128 951 265 500 203 686 407 900 689 300 417 553 751 791 924 760 1
07 528 155 368 282 919 78 664 913 344 702 841 91 255 708 354 307 733 829 0]
bench-insert=>
view raw big.clj hosted with ❤ by GitHub
思っていたよりは差がつかなかった。

vectorやlistはいつもこんな感じでinsertしてるのですが、もっと綺麗で効率のよい方法があったら教えて下さい。

効率のよいinsertが出来るシーケンスを標準で用意してくれてもいいのではと思わなくもないです。

2012年9月1日土曜日

Machines

先日rpscalaに行った時、@xuwei_kさんにscala-machinesというものを教えてもらったので調べてみました。

Example.scalaを参考に、コードを読んでいきます。

import java.io._
import scalaz.{ Reader => _, _ }, Scalaz._
import scalaz.effect._, IO._
import com.clarifi.machines._
object Main extends SafeApp {
import Plan._
def getFileLines[A](m: Process[String, A])(f: File): Procedure[IO, A] =
new Procedure[IO, A] {
type K = String => Any
val machine: Machine[K, A] = m
def withDriver[R](k: Driver[IO, K] => IO[R]): IO[R] = {
bufferFile(f).bracket(closeReader)(r => {
val d = new Driver[IO, String => Any] {
def apply(k: String => Any): IO[Option[Any]] = rReadLn(r) map (_ map k)
}
k(d)
})
}
}
def bufferFile(f: File): IO[BufferedReader] =
IO { new BufferedReader(new FileReader(f)) }
def rReadLn(r: BufferedReader): IO[Option[String]] = IO { Option(r.readLine) }
def closeReader(r: Reader): IO[Unit] = IO { r.close }
def countLn: Process[String, Int] = Process(_ => 1)
def exec[A: Monoid](f: File => Procedure[IO, A]): IO[Unit] =
(f(new File("test.csv")).execute >>= putOut) >> putStrLn("")
override def runc = exec(getFileLines(countLn))
}
view raw machines.scala hosted with ❤ by GitHub
少々書き換えました。

ぱっと見ても全くわからないですね。

IO


副作用(アクション)を表します。
IO.applyは値をcall by nameでとります。
unsafePerformIOを呼ぶことで、アクションを実行します。

bufferFile


def bufferFile(f: File): IO[BufferedReader] =
IO { new BufferedReader(new FileReader(f)) }
ファイルをとってBufferedReaderを返す関数です。
IOでラップしています。

rReadLn


def rReadLn(r: BufferedReader): IO[Option[String]] = IO { Option(r.readLine) }
view raw rReadLn.scala hosted with ❤ by GitHub
BufferedReaderから一行読み取ります。
readLineは文字列かnullを返すのでOption.applyを使用しています。
これもIOでラップしています。

closeReader


def closeReader(r: Reader): IO[Unit] = IO { r.close }
Readerを閉じます。
当然副作用なのでIOでラップされます。

Process


Process[I, O]はI => Oをラップしたものと考えていい・・・・はず。
Process[I, O]はMachine[I => Any, O]のシノニムで、Machine[K, O]はPlan[K, O, Nothing]のシノニムなので、実体はPlanなわけですが、まだ私は理解できていません。
IterateeのIteratee,Enumeratee,Enumeratorの内のIterateeにあたるようなものだと思われます。
なのでストリームの要素はこいつを使って処理していきます。

countLn


def countLn: Process[String, Int] = Process(_ => 1)
view raw countLn.scala hosted with ❤ by GitHub
文字列をとって1を返すProcessです。

getFileLines


def getFileLines[A](m: Process[String, A])(f: File): Procedure[IO, A] =
new Procedure[IO, A] {
type K = String => Any
val machine: Machine[K, A] = m
def withDriver[R](k: Driver[IO, K] => IO[R]): IO[R] = {
bufferFile(f).bracket(closeReader)(r => {
val d = new Driver[IO, String => Any] {
def apply(k: String => Any): IO[Option[Any]] = rReadLn(r) map (_ map k)
}
k(d)
})
}
}
型パラメータAとProcess[String, A]とFileをとり、Procedure[IO, A]を返します。
モナド変換子と同じく、Procedureから値Aを取り出す時にIOにラップされるようになります。
ProcedureはEnumeratorのようなもので、ストリームとそれを処理するMachineを持ちます。
ProcedureはMachineの第一型パラメータとMachine、抽象メソッドのwithDriverを定義します。
Process[I, O]は先述の通りMachine[I => Any, O]と定義されているので、抽象タイプメンバにはString => Any、Machineは引数としてとったProcessで定義します。
Driverでは入力を定義するのですが、当然、入力にはリソースが必要になります。
そこで、このwithDriverではリソースの作成、開放、Driver(入力)の作成、Driverの適用を定義します。
リソースの作成はbufferedReaderを使います。
IO#bracket(f)(g)は処理gの後に必ず処理fが処理されます。
事後処理としてcloseReaderを渡し、Driverの作成と適用をします。
DriverにはrReadLnを使い、入力を適用するapplyを定義します。
最後にwithDriverがとる関数kにDriverを適用します。

exec


def exec[A: Monoid](f: File => Procedure[IO, A]): IO[Unit] =
(f(new File("test.csv")).execute >>= putOut) >> putStrLn("")
view raw exec.scala hosted with ❤ by GitHub
ファイルを関数に適用し、実行、出力する関数です。
executeはProcedureの結果の型Aがモノイドである必要があります。
ストリームを処理した結果をaccumulatorに加える関数で合成していきます。

runc


override def runc = exec(getFileLines(countLn))
view raw runc.scala hosted with ❤ by GitHub
main関数で呼ばれる関数で、裏でunsafePerformIOを呼びます。

CSVファイルの読み込みと要素のカウントを書いてみた。

import java.io._
import scalaz.{ Reader => _, _ }, Scalaz._
import scalaz.effect._, IO._
import com.clarifi.machines._
object CSV extends SafeApp {
import Plan._
def getCSVFile[A](m: Process[List[String], A])(f: File): Procedure[IO, A] =
new Procedure[IO, A] {
type K = List[String] => Any
val machine = m
def withDriver[R](k: Driver[IO, K] => IO[R]): IO[R] = {
bufferFile(f).bracket(closeReader)(r => {
val d = new Driver[IO, List[String] => Any] {
def apply(k: List[String] => Any): IO[Option[Any]] = rReadLn(r) map (_ map (s => k(s.split(",").toList)))
}
k(d)
})
}
}
def bufferFile(f: File): IO[BufferedReader] =
IO { new BufferedReader(new FileReader(f)) }
def rReadLn(r: BufferedReader): IO[Option[String]] = IO { Option(r.readLine) }
def closeReader(r: Reader): IO[Unit] = IO { r.close }
def countLn: Process[List[String], Int] = Process(_.size)
def exec[A: Monoid](f: File => Procedure[IO, A]): IO[Unit] =
(f(new File("test.csv")).execute >>= putOut) >> putStrLn("")
override def runc = exec(getCSVFile(countLn))
}
view raw CSV.scala hosted with ❤ by GitHub

まとめ


IterateeよりはIOに特化していると思われる。
リソースの処理をProcedureでやっているところとか。
ProcessやProcedureを自分で書くのは、IterateeやEnumeratorを自分で書くよりは楽・・・・な気がしなくもない。

今回は本当にExampleをさわっただけで、詳細はわかっていない。
作者のドキュメントに期待です。

2012年8月26日日曜日

ClojureScript

最近はClojureScriptばかり書いてます。
Clojureを書いてるとJavaを意識しなければいけないことがありますが、ClojureScriptも同じでJavaScriptを意識しなければなりません。
とはいってもJavaScriptを書くよりも遥かに快適なので是非広まって欲しいものです。

今日までにClojureScriptを書いてきて、わかったことを少しまとめようと思います。

clojure.browser


Google Closure Libraryのラッパーで、domやevent、netなどのライブラリがあります。
domとeventを触りましたが、ものすごく中途半端です。
結局goog.domやgoog.eventの関数が必要になるので、 まだまだ発展途上と言えるでしょう。

Closure Libraryの関数はJavaScriptのArrayやObjectを扱うものが多く、ClojureScriptのデータ型を渡すときに少々不便だったりします。
clojure.browserはそういった不満を解消するものとなってほしいです。


ちょっと便利な関数
  • clojure.browser.dom/ensure-element
    • keywordを渡すとgetElementとして、 stringを渡すとhtmlToDocumentFragmentとして動きます
  • clojure.browser.dom/log
    • Console#logです
    • *print-fn*にset!するのが一般的?

ClojureとJavaScript


JavaScriptの世界で生きていくためにはClojureの標準ライブラリの関数だけでは生きていけません。
なので、ClojureScriptにはClojureとJavaScriptの間を取り持つ関数が存在します。
しかし、ドキュメントが少なすぎて存在があまり知られていないように感じます。かなしい。

js-objとarrayは特に重要です。
それぞれObjectとArrayを生成する関数です。
主にClosure Libraryの関数を呼び出す為に使います。
js-keysやjs-deleteなんかも覚えておきましょう。

JavaScriptを直接実行するjs*なんかもありますが、最終手段とするべきでしょう。

array-seqも便利です。
JavaScriptのArray的データ型(NodeList, FileListなど)をClojureScriptのデータ型に変換するものです。
(extend-type Hoge ISeqable (-seq [hoge] (array-seq hoge)))
としておくとClojureScriptのコレクション関連の関数が使えて便利になります。

外部ライブラリ


ちゃんとClojureScriptのライブラリだって存在します。
  • core.match
    • パターンマッチを提供してくれるライブラリ、便利。
  • core.logic
    • JavaScriptで論理プログラミングが出来るよ!やったね!
    • しかしバグあるしあまり期待しないほうがいい。

読まなければいけないもの



ClojureScriptやりましょうず!

2012年8月8日水曜日

手続きと抽象化とScalaz

夏休みです。
コード書きましょう。

現在、UnfilteredとJavaFX使ってなんか創ってます。

Unfiltered


コード例

import unfiltered.request._
import unfiltered.response._
object Server extends App {
val hello = unfiltered.netty.cycle.Planify {
case _ => ResponseString("hello world")
}
unfiltered.netty.Http(8080).plan(hello).run()
}
view raw app1.scala hosted with ❤ by GitHub
だいたいこんな感じ。

私はAsynchronousにHttpクライアント使ってほげほげしたいので、async版を使います。

import dispatch._
object Server extends App {
def hello(client: nio.Http) = unfiltered.netty.async.Planify {
case req => req.respond(ResponseString("hello world"))
}
val client = new nio.Http
unfiltered.netty.Http(8080).plan(hello(client)).run()
client.shutdown()
}
view raw app2.scala hosted with ❤ by GitHub
※このコードではHttpクライアント使ってないけど例なのでキニシナイ。

私のプロジェクトではこのサーバーを
  • アプリケーションとして起動
  • JavaFXの裏で起動
という2通りの方法が欲しいのです。

JavaFX


SwingはオワコンなのでJavaFX使いましょう。

コード例

import javafx.application.Application
import javafx.stage.Stage
class Client extends Application {
def start(stage: Stage) {
stage.show
}
}
object Client extends App {
Application launch classOf[Client]
}
view raw app3.scala hosted with ❤ by GitHub
クライアントを起動する前にサーバーを起動したいのですが、いろいろと問題があり、通常のrunメソッドによる起動は出来ません。

そこで、runメソッドで行われているstart, stop, destroyを直接呼ぶことにします。

object Client extends App {
val client = new nio.Http
val server = unfiltered.netty.Http(8000).plan(Server.hello(client))
server.start()
Application launch classOf[Client]
server.stop()
server.destroy()
client.shutdown()
}
view raw app4.scala hosted with ❤ by GitHub

抽象化


重複したところを考えます。

まずはサーバーを組み立てるところ。
ポートも自由に設定したい。

object Server extends App {
def hello(client: nio.Http) = unfiltered.netty.async.Planify {
case req => req.respond(ResponseString("hello world"))
}
def server(port: Int)(client: nio.Http) =
unfiltered.netty.Http(port).plan(hello(client))
val client = new nio.Http
server(8080)(client).run()
client.shutdown()
}
object Client extends App {
val client = new nio.Http
val server = Server.server(8000)(client)
server.start()
Application launch classOf[Client]
server.stop()
server.destroy()
client.shutdown()
}
view raw app5.scala hosted with ❤ by GitHub

次にnio.Httpのところ。
shutdownは自動でやって欲しい。
幸い、unfiltered.netty.HttpとApplication.launchはThreadをwaitしてくれるのでローンパターン的にします。
util.control.Exceptionを使うと、try - catch - finallyを生で書く必要がないことがわかりますね!
ナチュラルにScalaz使っていますが、>>>はandThenです。

import scalaz._, Scalaz._
import scala.util.control.Exception
object Server extends App {
def client[A](f: nio.Http => A) = {
val client = new nio.Http
(Exception ultimately client.shutdown())(f(client))
}
def hello(client: nio.Http) = unfiltered.netty.async.Planify {
case req => req.respond(ResponseString("hello world"))
}
def server(port: Int)(client: nio.Http) =
unfiltered.netty.Http(port).plan(hello(client))
client(server(8080) _ >>> (_.run()))
}
object Client extends App {
Server.client { c =>
val server = Server.server(8000)(c)
server.start()
Application launch classOf[Client]
server.stop()
server.destroy()
}
}
view raw app6.scala hosted with ❤ by GitHub

サーバーの方は十分綺麗になりました。
クライアントのサーバーの起動、終了の部分もローンパターンで書けます。

object Client extends App {
def run[A](a: => A)(server: unfiltered.netty.Http) = {
Exception ultimately {
server.stop()
server.destroy()
} apply {
server.start()
a
}
}
Server.client(Server.server(8000) _ >>> run(Application launch classOf[Client]))
}
view raw app7.scala hosted with ❤ by GitHub

綺麗になりました。

Scalaz


さて、ここまでが私的Scalaプログラミングですが、まあ、普通過ぎて面白くないですね。
そこで、IOモナドを用いて実装してみます。


まずはサーバーのコード。

import effect._
object Server extends SafeApp {
def hello(client: nio.Http) = unfiltered.netty.async.Planify {
case req => req.respond(ResponseString("hello world"))
}
def client[A](f: nio.Http => IO[A]) =
IO(new nio.Http).bracket(_.shutdown.point[IO])(f)
def server(port: Int)(client: nio.Http) =
unfiltered.netty.Http(port).plan(hello(client))
override def runc = client(server(8080) _ >>> (_.run.point[IO]))
}
view raw app8.scala hosted with ❤ by GitHub

SafeAppはIOモナドを暗黙的に実行するためのもので、runcをオーバーライドする事でアプリケーションを構築します。

IO#bracketはtry - finallyのようなもので、リソースを扱うときに使います。

次にクライアント。

object Client extends SafeApp {
def run[A](a: IO[A])(server: unfiltered.netty.Http) =
IO(server.start).bracket_(IO(server.stop) >> IO(server.destroy))(a)
override def runc =
Server.client(Server.server(8000) _ >>> run((Application launch classOf[Client]).point[IO]))
}
view raw app9.scala hosted with ❤ by GitHub

アンダーバーが付いた関数は、前の結果を捨てるという意味があります。
Haskellの習慣ですね。



はいおしまい。

IOモナドは便利なのですが、UnfilteredやJavaFXがIOモナド用いた設計をしているわけではないので、少しばかり面倒なところがあります。

告知


第二回スタートScalazやります、多分。

2012年7月2日月曜日

core.logic

clojureのcontribをながめてるとcore.logicなるものが。

なにやらClojureで論理プログラミングをするためのものらしいのでいろいろと試してみた。

よく例に出るappendの例。

(use 'clojure.core.logic)
(defne append [x y z]
([() a a])
([[a . b] _ [a . c]]
(append b y c)))
view raw append1.clj hosted with ❤ by GitHub

xとyが連結するリスト、zが連結後のリストです。
xが空ならyとzは同じ。
xの先頭とzの先頭が同じなら、xのテールとyを連結したzのテールになる。

普通の関数で書いたらこんな感じ?

(use '[clojure.core.match :only [match]])
(defn append [x y]
(match [x]
[[]] y
[[h & t]] (recur t (cons h y))))
view raw append2.clj hosted with ❤ by GitHub


しかし、普通の関数と違ってかなり高機能です。

実行してみます。

(assert (= (run* [q] (append [1 2] [3 4] q))
'((1 2 3 4))))
(assert (= (run* [q] (append [1 2] q [1 2 3 4]))
'((3 4))))
(assert (= (run* [q] (fresh [a b]
(== q [a b])
(append a b [1 2 3 4])))
'([() (1 2 3 4)] [(1) (2 3 4)] [(1 2) (3 4)] [(1 2 3) (4)] [(1 2 3 4) ()])))
view raw append3.clj hosted with ❤ by GitHub

なんと、append一つ定義するだけで連結・差分・組み合わせの三つの使い方が出来るのです!

おー、論理プログラミングすげー

さらに、ClojureScript版も用意されています。

上記のappendのコードは動きますが、clojure.core.logicと比べると定義されている関数やマクロは少ないです。
バグってたりします。

数独も解いてみた。

(use 'clojure.core.logic)
(def _1 [()])
(def _2 [() ()])
(def _3 [() () ()])
(def _4 [() () () ()])
(defne different [list]
([()])
([[x . ()]])
([[x . [h . t]]]
(fresh [l]
(!= x h)
(conso x t l)
(different l))))
(defne lte [m n]
([() _])
([[_ . x] [_ . y]]
(lte x y)))
(defne gte [m n]
([_ ()])
([[_ . x] [_ . y]]
(gte x y)))
(defne bounds [l m n]
([() _ _])
([[h . t] m n]
(lte m h)
(gte n h)
(bounds t m n)))
(defne sudoku [puzzle]
([[s11 s12 s13 s14
s21 s22 s23 s24
s31 s32 s33 s34
s41 s42 s43 s44]]
(bounds [s11 s12 s13 s14
s21 s22 s23 s24
s31 s32 s33 s34
s41 s42 s43 s44] _1 _4)
(different [s11 s12 s13 s14])
(different [s21 s22 s23 s24])
(different [s31 s32 s33 s34])
(different [s41 s42 s43 s44])
(different [s11 s21 s31 s41])
(different [s12 s22 s32 s42])
(different [s13 s23 s33 s43])
(different [s14 s24 s34 s44])
(different [s11 s12 s21 s22])
(different [s13 s14 s23 s24])
(different [s31 s32 s41 s42])
(different [s33 s34 s43 s44])))
(run* [q] (sudoku [_3 _1 _2 _4 _4 _2 _3 _1 _1 _3 _4 _2 _2 _4 _1 _3]))
view raw sudoku.clj hosted with ❤ by GitHub

数字の扱いが微妙なのでリストで数字を表してみました。

しかしこのコード、正しいパズルかどうかはテストしてくれますが、パズルを解決してくれません。

!=を使っているから・・・・?

まあ、まだまだ開発途中なので今後に期待です。

2012年6月27日水曜日

Stateもなど

Stateモナドの有用性を考えるために書いてみます。

case class Range(node: Node, offset: Int)
def getRange = IO(Range(<p>{ Random.nextString(5) }</p>, Random.nextInt(5)))
def nextNode(node: Node) = IO(<p>{ Random.nextString(5) }</p>)
def prevNode(node: Node) = IO(<p>{ Random.nextString(5) }</p>)
def move(range: Range) = IO(())
view raw dom.scala hosted with ❤ by GitHub

Rangeオブジェクトはカーソルが指すNodeとoffsetを持っています。
getRangeは現在のRangeを返すものとします。
moveはそのRangeにカーソル移動するものとします。
nextNodeとprevNodeはそれぞれ次のNodeと前のNodeを返します。

getRange,nextNode,prevNode,moveは擬似的なものなので、定義は気にしないで下さい。
この4つの関数は参照透過とかそんなわけないのでIOを返します。

これらに対して、4方向にカーソルを動かす関数を定義します。

def moveLeft = for {
r <- getRange
} yield move(r.copy(offset = r.offset.pred))
def moveRight = for {
r <- getRange
} yield move(r.copy(offset = r.offset.succ))
def moveUp = for {
r <- getRange
n <- prevNode(r.node)
} yield move(r.copy(node = n))
def moveDown = for {
r <- getRange
n <- nextNode(r.node)
} yield move(r.copy(node = n))
view raw move1.scala hosted with ❤ by GitHub

IOはモナドなのでforが使えます。
predとsuccはscalaz.Enumのシンタックスでデクリメントとインクリメントのようなものです。

実際には移動するときに、offsetなどを調べるものですが、OptionT[IO, Unit]などと複雑になるので省略します。
これらの関数を組み合わせてみます。

def movemovemove = moveLeft >> moveRight >> moveUp >> moveDown
view raw comp1.scala hosted with ❤ by GitHub

さて、これらの関数、ちょっと冗長ですね。
いちいちmoveを実行しているところもいただけません。

これらをStateを使って書きなおしてみます。

def moveLeft = State[Range, Range](r => r.copy(offset = r.offset.pred).squared)
def moveRight = State[Range, Range](r => r.copy(offset = r.offset.succ).squared)
def moveUp = StateT[IO, Range, Range](r => prevNode(r.node) map (n => r.copy(node = n).squared))
def moveDown = StateT[IO, Range, Range](r => nextNode(r.node) map (n => r.copy(node = n).squared))
view raw move2.scala hosted with ❤ by GitHub

squaredは値をペアにして返します。

同じように、合成したStateとそれを使って移動する関数を定義します。

def movemovemove = for {
_ <- moveLeft.lift[IO]
_ <- moveRight.lift[IO]
_ <- moveUp
r <- moveDown
} yield r
def run = getRange >>= movemovemove.eval >>= move
view raw comp2.scala hosted with ❤ by GitHub

getRangeとmoveの呼び出しは1回で済むようになりました。
movemovemoveというStateを使い回すことも出来ます。

さらに、Stateモナドであることを活かして、次のようなStateを定義することが出来ます。

def moveDownStart = for {
r <- moveDown
rr <- List.fill(r.offset)(moveLeft).foldRight(State.init[Range])(_ >> _).lift[IO]
} yield rr
view raw state.scala hosted with ❤ by GitHub

移動した直後の状態を扱えるのがStateモナドの利点ですね。



最近は大学生がなかなか忙しいです。
俺、定期試験が終わったらScalaz勉強会開くんだ・・・・・

2012年6月23日土曜日

Pythonの復習

最近Pythonさわってなかったので。

FizzBuzz


def fizzbuzz(n):
if n % 15 == 0:
return "FizzBuzz"
elif n % 3 == 0:
return "Fizz"
elif n % 5 == 0:
return "Buzz"
else:
return n
view raw fizzbuzz1.py hosted with ❤ by GitHub

なんのへんてつもないコード。

yield使って、generatorにしてみる。

def fizzbuzz_iter():
n = 1
while True:
yield fizzbuzz(n)
n += 1
view raw fizzbuzz2.py hosted with ❤ by GitHub

このケースならitertoolsを使ったほうがカッコイイ。

import itertools
def fizzbuzz_iter():
return map(fizzbuzz, itertools.count(1))
view raw fizzbuzz3.py hosted with ❤ by GitHub

countはnからstep(デフォルトは1)づつ増えていくgeneratorを作る関数です。
mapはいつの間にかイテレータに対応してくれてました。

素数


判定を書く

import functools
import operator
def isprime(n):
return all(map(functools.partial(operator.mod, n), range(2, n - 1)))
view raw prime1.py hosted with ❤ by GitHub

functoolsとoperatorで関数型っぽく書けて素敵ですね。
functools.partialは部分適用、operator.modは剰余です。

素数列を作る

def primes():
return filter(isprime, itertools.count(2))
view raw prime2.py hosted with ❤ by GitHub

なかなかいい感じ。

フィボナッチ数列


n番目のフィボナッチ数を返す関数

def fibonacci(n):
if n == 0:
return 1
elif n == 1:
return 1
else:
return fibonacci(n - 2) + fibonacci(n - 1)
view raw fibonacci1.py hosted with ❤ by GitHub

dict使って分岐する

def fibonacci(n):
return {0: lambda: 1, 1: lambda: 1}.get(n, lambda: fibonacci(n - 2) + fibonacci(n - 1))()
view raw fibonacci2.py hosted with ❤ by GitHub

switch的なものはdictで代用できるのでもーまんたいですね。
getの第二引数はキーが存在しない場合返ります。

イテレータ版

def fibonacci_iter():
return map(fibonacci, itertools.count(1))
view raw fibonacci3.py hosted with ❤ by GitHub

Pythonはなかなか関数型言語ちっくに書けて素敵ですね。
Rから始まる4文字のプログラミング言語より関数型っぽいです。

2012年6月10日日曜日

Proof with Scala

※今回はScala2.10.0-M3を使います。

ある型クラスのインスタンスを定義するとき、何らかの規則を満たす必要があるときがあります。

例えばMonadだとモナド則。
bind,returnを定義するときに、以下のものが満たされないといけません。

bind (return a) f = f a
bind m return = m
bind (bind m f) g = bind m (fun x => bind (f x) g)

しかし、Scalazの型クラスなどはモナド則を満たしているかどうかを検査してくれるわけではありません。

そこで、コンパイル時に規則を満たしているかどうかを検査するように型クラスを定義してみます。

Monoidを例に考えます。

trait Monoid[M] {
type Zero <: M
type Append[A <: M, B <: M] <: M
def zero: Zero
def append[A <: M, B <: M](a1: A, a2: B): Append[A, B]
}
view raw monoid1.scala hosted with ❤ by GitHub

ScalazのMonoidの定義と違うところは、ある型のサブクラスも許容しているところです。

Monoidは以下のものを満たす必要があります。

append zero a = a
append a zero = a
append a (append b c) = append (append a b) c

これをScalaで記述すると

trait Monoid[M] extends MonoidLow[M] {
type Zero <: M
type Append[A <: M, B <: M] <: M
def zero: Zero
def append[A <: M, B <: M](a1: A, a2: B): Append[A, B]
}
trait MonoidLow[M] { self: Monoid[M] =>
def leftIdentity[A <: M](a: Append[Zero, A]): A
def rightIdentity[A <: M](a: Append[A, Zero]): A
def associativity[A <: M, B <: M, C <: M](a: Append[A, Append[B, C]]): Append[Append[A, B], C]
}
view raw monoid2.scala hosted with ❤ by GitHub

となります。

型が命題でプログラムが証明になります。
つまり、leftIdentity,rightIdentity,associativityを定義出来れば証明できたことになります。
カリー=ハワード同型対応!

自然数を定義します。

sealed trait Nat {
type +[N <: Nat] <: Nat
def +[N <: Nat](n: N): +[N]
}
sealed trait _0 extends Nat {
type +[N <: Nat] = N
def +[N <: Nat](n: N) = n
}
case object _0 extends _0
case class S[N <: Nat](n: N) extends Nat {
type +[M <: Nat] = S[N# + [M]]
def +[M <: Nat](m: M) = S(n + m)
}
view raw monoid3.scala hosted with ❤ by GitHub

自然数をMonoidのインスタンスにします。

object NatInstance extends Monoid[Nat] {
type Zero = _0
type Append[A <: Nat, B <: Nat] = A# + [B]
def zero = _0
def append[A <: Nat, B <: Nat](a: A, b: B) = a + b
def leftIdentity[A <: Nat](a: Append[Zero, A]) = a
implicit def rightIdentity[A <: Nat](a: Append[A, Zero]) = a
implicit def associativity[A <: Nat, B <: Nat, C <: Nat](a: Append[A, Append[B, C]]) = a
}
view raw monoid4.scala hosted with ❤ by GitHub

コンパイル通ったー!

通らない例を作ってみましょう。

正の整数を定義して、Monoidのインスタンスを考えてみます。

sealed trait Pos {
type +[P <: Pos] <: Pos
def +[P <: Pos](p: P): +[P]
}
sealed trait _1 extends Pos {
type +[P <: Pos] = S[P]
def +[P <: Pos](p: P) = S(p)
}
case object _1 extends _1
case class S[P <: Pos](p: P) extends Pos {
type +[Q <: Pos] = S[P# + [Q]]
def +[Q <: Pos](q: Q) = S(p + q)
}
object PosInstance extends Monoid[Pos] {
type Zero = _1
type Append[A <: Pos, B <: Pos] = A# + [B]
def zero = _1
def append[A <: Pos, B <: Pos](a: A, b: B) = a + b
def leftIdentity[A <: Pos](a: Append[Zero, A]) = a
implicit def rightIdentity[A <: Pos](a: Append[A, Zero]) = a
implicit def associativity[A <: Pos, B <: Pos, C <: Pos](a: Append[A, Append[B, C]]) = a
}
view raw monoid5.scala hosted with ❤ by GitHub

この定義では当然コンパイルは通りませんね。

コンパイルが通るように定義することも多分できないと思われます。(asInstanceOfは使っちゃダメですよ。)

これらのコードを書いてる中で疑問が現れました。
NatInstanceのrightIdentityとassociativityです。
このコード、コンパイルは通らないと思っていたのですが、implicitを付けると通りました。
・・・・・なぜ?

2012年6月5日火曜日

わたしのScalazプログラミングその2

型クラスについて。

例によって有理数で。

case class Rational(n: Int, d: Int)
view raw rational1.scala hosted with ❤ by GitHub

Groupのインスタンスを定義します。
Groupとは、結合演算子、単位元、逆元を持つもののことです。

trait RationalInstances {
implicit object rationalInstance extends Group[Rational] {
def zero = Rational(0, 1)
def inverse(r: Rational) = Rational(-r.n, r.d)
def append(r1: Rational, r2: => Rational) =
Rational(r1.n * r2.d + r2.n * r1.d, r1.d * r2.d)
}
}
object Rational extends RationalInstances
view raw rational2.scala hosted with ❤ by GitHub

動かしてみる。

scala> import scalaz._, Scalaz._
import scalaz._
import Scalaz._
scala> Rational(1, 2) |+| Rational(2, 3)
res0: Rational = Rational(7,6)
scala> Rational(1, 2) |-| Rational(2, 3)
res1: Rational = Rational(-1,6)
scala> Rational(1, 2) multiply 2
res2: Rational = Rational(4,4)
scala> -Rational(1, 2)
res3: Rational = Rational(-1,2)
view raw repl1.scala hosted with ❤ by GitHub

Groupのインスタンスを定義するだけでこれだけのことができます。

自分でデータ型を定義するときは、インスタンス自体に関数を定義より、データ型にメソッドとして定義したほうがいいかもしれません。
また、Groupのインスタンスからメソッドが定義できます。

case class Rational(n: Int, d: Int) {
def +(r: Rational) =
Rational(n * r.d + r.n * d, d * r.d)
def -(r: Rational) =
Rational.rationalInstance.minus(this, r)
def unary_- = Rational(-n, d)
}
trait RationalInstances { self: Rational.type =>
implicit object rationalInstance extends Group[Rational] {
def zero = Rational(0, 1)
def inverse(r: Rational) = -r
def append(r1: Rational, r2: => Rational) = r1 + r2
}
}
object Rational extends RationalInstances
view raw rational4.scala hosted with ❤ by GitHub

scala> Rational(1, 2) + Rational(2, 3)
res0: Rational = Rational(7,6)
scala> Rational(1, 2) - Rational(2, 3)
res1: Rational = Rational(-1,6)
view raw repl2.scala hosted with ❤ by GitHub

乗算、除算も定義してみます。

import scalaz._, Scalaz._, Tags._
case class Rational(n: Int, d: Int) {
def +(r: Rational) =
Rational(n * r.d + r.n * d, d * r.d)
def -(r: Rational) =
Rational.rationalInstance.minus(this, r)
def *(r: Rational) =
Rational(n * r.n, r.d * d)
def /(r: Rational): Rational =
Rational.rationalMultiplicationInstance.minus(Tag(this), Tag(r))
def unary_- = Rational(-n, d)
def unary_~ = Rational(d, n)
}
trait RationalInstances { self: Rational.type =>
implicit object rationalInstance extends Group[Rational] {
def zero = Rational(0, 1)
def inverse(r: Rational) = -r
def append(r1: Rational, r2: => Rational) = r1 + r2
}
implicit object rationalMultiplicationInstance extends Group[Rational @@ Multiplication] {
def zero = Tag(Rational(1, 1))
def inverse(r: Rational @@ Multiplication) = Tag(~r)
def append(r1: Rational @@ Multiplication, r2: => Rational @@ Multiplication) =
Tag(r1 * r2)
}
}
object Rational extends RationalInstances
view raw rational5.scala hosted with ❤ by GitHub

scala> import scalaz._, Scalaz._, Tags._
import scalaz._
import Scalaz._
scala> Rational(1, 2) * Rational(2, 3)
res0: Rational = Rational(2,6)
scala> Rational(1, 2) / Rational(2, 3)
res1: Rational = Rational(3,4)
scala> Foldable[List].fold(List(Rational(1, 2), Rational(2, 3)))
res2: Rational = Rational(7,6)
scala> Foldable[List].fold(List(Tag[Rational, Multiplication](Rational(1, 2)), Tag[Rational, Multiplication](Rational(2, 3))))
res3: scalaz.package.@@[Rational,scalaz.Tags.Multiplication] = Rational(2, 6)
view raw repl3.scala hosted with ❤ by GitHub

べんり!

2012年6月2日土曜日

わたしのScalazプログラミング

Scalaはオブジェクト指向言語なのですよ。
知ってました?

重要:これから書くコードはとてもメニアックなので参考にしないようにして下さい。

今日は有理数を定義してみます。


case class Rational(n: Int, d: Int)
view raw rational1.scala hosted with ❤ by GitHub

加算と減算を追加してみる。

case class Rational(n: Int, d: Int) {
def +(r: Rational) = Rational(n * r.d + r.n * d, d * r.d)
def -(r: Rational) = Rational(n * r.d - r.n * d, d * r.d)
}
view raw rational2.scala hosted with ❤ by GitHub

さて、この2つのメソッド、とても似ていますよね。
抽象化してしまいます。

case class Rational(n: Int, d: Int) {
def op(f: (Int, Int) => Int)(r: Rational) =
Rational(f(n * r.d, r.n * d), d * r.d)
lazy val + = op(_ + _)_
lazy val - = op(_ - _)_
}
view raw rational3.scala hosted with ❤ by GitHub

さて、今日の本題はここからです。
この加算と減算を他の数値型に対応させたいとします。
普通はこのようにオーバーロードを使いますよね。

case class Rational(n: Int, d: Int) {
def +(r: Rational) = Rational(n * r.d + r.n * d, d * r.d)
def +(i: Int) = Rational(n + i * d, i * d)
}
view raw rational4.scala hosted with ❤ by GitHub

しかし、+や-はメソッド型ではなく、値型なのでオーバーロードは使用できません。
そこで、オーバーロードを使わず、UnionTypesを使って定義します。

case class Rational(n: Int, d: Int) {
def +[A](a: A)(implicit ev: A ∈ t[Rational]#t[Int]) =
a match {
case Rational(nn: Int, dd: Int) => Rational(n * dd + nn * d, d * dd)
case i: Int => Rational(n + i * d, i * d)
}
}
view raw rational5.scala hosted with ❤ by GitHub

これでもまだ問題があります。
Scalaでは、型パラメータをとる関数を値として定義できません。
そこで、関数のようにふるまう型を自分で定義します。

case class Rational(n: Int, d: Int) {
case class Function(f: (Int, Int) => Int) {
def apply[A](a: A)(implicit ev: A ∈ t[Rational]#t[Int]) = a match {
case Rational(nn: Int, dd: Int) => Rational(f(n * dd, nn * d), d * dd)
case i: Int => Rational(f(n, i * d), i * d)
}
}
lazy val + = Function(_ + _)
lazy val - = Function(_ - _)
}
view raw rational6.scala hosted with ❤ by GitHub

これで+と-の演算を抽象化し、さらに多相に対応しましたね。
めでたしめでたし。

2012年6月1日金曜日

Scalaz7でIterateeとIO

いつものようにTwitterでScalazを検索すると


この様なつぶやきが。

コードはこんな感じ。

import java.io.{ BufferedReader, FileReader }
import scalaz._, Scalaz._
import scalaz.effects._
import IterV._
object App {
def main(args: Array[String]) {
val br = new BufferedReader(new FileReader("test2.txt"))
val io = byEnumerator(br)
io.unsafePerformIO
}
def byEnumerator(br: BufferedReader): IO[Unit] = {
val lineIter: IterV[String, Int] = head[String] map {
lineOpt => (lineOpt map (_.length)) | 0
}
val iter: IterV[String, Stream[Int]] = repeat[String, Int, Stream](lineIter)
val enumerator: EnumeratorM[IO, String] = getReaderLines(br)
val read: IO[Int] = enumerator(iter) map { iter =>
iter.run reduce (_ + _)
}
read map (_.toString) flatMap (putStrLn _)
}
}
view raw App.scala hosted with ❤ by GitHub

fmfm.
先頭行をとって長さをとって行数分ぬりつぶして合計をとっているようですね。

このコードはScalaz6なのですが、Scalaz7でも書けるかなーと思い、ちょっと書いてみました。

import java.io.{ Reader, FileReader }
import scalaz._, Scalaz._
import effect._, IO._
import iteratee._, Iteratee._
object App extends SafeApp{
val separator = sys.props("line.separator").head
override def run(args: ImmutableArray[String]): IO[Unit] = {
val r = new FileReader("test2.txt")
byEnumerator(r)
}
type IEOC = IoExceptionOr[Char]
def byEnumerator(r: Reader): IO[Unit] = {
val lineIter = takeWhile[IEOC, Stream](_ map (_ =/= separator) valueOr false) map (_.length)
val iter = repeatBuild[IEOC, Int, Stream](lineIter).up[IO] map (_.sum)
val enum = enumReader(r)
val read = iter &= enum
(read.run >>= putOut) >> putStrLn("")
}
}
view raw App.scala hosted with ❤ by GitHub

違いを1つ1つみていきます。

まず、Scalaz7ではgetReaderLinesがないので、BufferedReaderではなくReaderを使うようにしました。
なので、1行ずつではなく1文字ずつしかみれなくなります。
そこで、改行コードをみるようにします。
改行コードはシステムプロパティーのline.separatorで取得できるとのことなので、sys.propsから取得します。

もとのコードでは、先頭行を取得する為にheadを使っていますが、このコードではtakeWhileを使います。
ここでは、述語として改行コードかどうかの判定をとります。

さて、肝心のIterateeの要素ですが、IoExceptionOr[Char]となっています。
これはその名の通り、IoExceptionとの和で、どちらかが内包されています。
ここではvalueOrを使い、IoExceptionだった場合の値を決め、値を取得しています。

repeatBuildはScalaz6のscalaz.IterV.repeatと同じです。
on[IO]は、Iteratee[E, A]から、IterateeT[E, IO, A]に変換しています。

enumReaderはReaderからEnumeratorを作り、&=はEnumeratorをIterateeに流し込みます。

最後に実行の部分ですが、mainを定義しているわけではありません。
SafeAppはunsafePerformIOを明示的に呼び出さない仕組みで、runをオーバーライドすることで実行できます。

・・・・・とまあこんな感じなのですが、Scalaz6と比べて、かなり違うことが分かりますね。
とくにIteratee。
IterVとIterateeで別々のものになっていましたが、いまはIterateeTのみで大分使いやすくなった気がします。(Iteratee[E, A]はIteratee[E, Id, A]の別名)
ただ、enumReaderLinesのようなものは欲しかった。

IOはMonadIOとかMonadControlIOとかいろいろ高機能になっているのですが、基本的な機能が少ないので、相変わらず残念です。
せめてSourceとReaderとWriterのインスタンスくらい作ってくれればいいのに。
あと入出力系。

超便利とまでは言えないけれど、将来性はありそうなiterateeとeffect。
一度さわってみてはどうでしょう?

2012年5月23日水曜日

Scalaz7

最近はgistでScalazワンライナー職人やってます。
ワンライナーになるのはScalazにアルゴリズムが揃ってるからですよ!と主張してみる。

Scalaz7


現在の最新リリース版はScalaz6系です。
githubのmasterも6系ですね。更新が1ヶ月前で止まってますが。

開発が活発に行われているブランチはscalaz-sevenです。
これは次期Scalazで、大幅な設計の見直しがされています。

主な違いは、
  • scalazのconcurrent,effect,iterateeパッケージがサブプロジェクトに分離されました。
    • httpとgeoはお亡くなりになりました・・・
  • 型クラスの階層構造が見直されました。
    • ZeroやEmptyがなくなったり、TraverseがFoldableを継承したり。
  • 型クラスのコンパニオンオブジェクトにインスタンスが定義されなくなりました。
    • インスタンスの定義は、scalaz.std以下や、各データ構造のコンパニオンオブジェクトに定義されるようになりました。
  • scalaz.syntax以下にimplicit conversionを使ったシンタックスが定義されるようになりました。
    • Identity,MA,MABなどない!
  • 新しいデータ構造が追加されました。
    • モナド変換子がいっぱいだよ! 
とまあこんな感じ。

Scalaz6と7では、パッケージ、データ構造、型クラス、メソッド名、かなり違います。
なので、6から7への移行は少し面倒かもしれません。

上記の違いはREADME参考にしながら書きましたが私的に重要だと思った変更を列挙していこうと思います。

Tag, Union


これだけでもにScalaz6から7へ移行する理由に成り得ます。
これらを使うことで適度に制約を加えられるようになり、設計がしやすくなります。
いままでNewTypeを使って実装していたものは全てTagで書き直されています。
あまり詳しく書いてないけれど、過去にちょっと書いた

scalaz.std


いままでは、*WにScalaの基本的なデータ構造の拡張が定義されていましたが、Scalaz7ではscalaz.std以下に型クラスのインスタンスと共に定義されています。
シンタックスも定義されていますが、高階関数に関数を渡すときなどに、直接関数を指定できるようになりました。

Sugarがない


Scalazのアイデンティティとも言える変態記号メソッドがなくなりました!
これを使ってScalazは変態だなんてもう言えなくなってしまうと思うと少し残念です。
いままで☆とか使ってた人はどうなるのでしょう・・・・・

まとめ


いまからScalazを使い始める人は、Scalaz7を使って欲しいかなあと思います。
変更が大きいということもありますが、Scalaz7の方が洗礼されてますし、より高機能になっているので、Scalazの凄さがよくわかるのではないかと思います。

Scalazを勉強したい方へ


自分の趣味のプロジェクトのコードにこの2行を加えましょう

import scalaz._
import Scalaz._




Scalaz勉強会とか需要があるのなら開こうと思うのだけれど、実際どうだろう?

2012年4月21日土曜日

ClojureScriptの導入

ハードル高いと言われてしまったので。

Leiningenの導入


まずはbuild toolの導入です。
https://github.com/technomancy/leiningen
からスクリプトをダウンロードしましょう。
  • Linux
    • Installationの"Download the script"
  • Windows
    •  Installationの"the batch file"
 ダウンロードしたスクリプトはleinと名前を付けて、PATHの通ったところまたはダウンロードした場所にPATHを通しましょう。

ターミナルを立ち上げて

lein self-install

が実行出来ればleiningenの導入は完了です。

Projectの作成


ターミナルで

lein new cljstest

と打ちましょう。
cljstestというプロジェクトが作られます。

作られたプロジェクトのディレクトリには、project.cljが存在します。
これを以下のように書き換えて下さい。

(defproject cljstest "1.0.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.4.0"]]
:plugins [[lein-cljsbuild "0.1.8"]]
:cljsbuild {
:builds [{
:source-path "src"
:compiler {
:output-to "main.js"
:optimizations :whitespace
:pretty-print true}}]})
view raw project.clj hosted with ❤ by GitHub

詳しい設定の仕方は
https://github.com/emezeske/lein-cljsbuild

編集ができたら

lein deps
lein cljsbuild auto

を実行して下さい。

core.cljsの編集


cljstest/src/cljstestに存在するcore.cljをcore.cljsにリネームし、以下のように編集します。

(ns cljstest.core)
(.alert js/window "Hello")
view raw core.clj hosted with ❤ by GitHub

ファイルを保存すると自動でコンパイルされ、 cljstestにmain.jsが作成されます。
以下のようなhtmlファイルをcljstestに作り、実行してみましょう。

<html>
<head>
<title>cljstest</title>
</head>
<body>
<script type="text/javascript" src="main.js"></script>
</body>
</html>
view raw test.html hosted with ❤ by GitHub

アラートが出れば完成です。

2012年4月16日月曜日

ClojureScript

最近うぇっぶの勉強が足りないと思い、なんか書いてます。
せっかくだから新しいものに手を出していこうと思い、いろいろと挑戦しています。
使っている言語、ライブラリは
  • Scala
    • Unfiltered
    • Scalate
      • Jade
      • CoffeeScript
      • SASS
    • JavaFX
  • ClojureScript
    • Closure Library
  • Jython
    • Pygments
などなど。
便利なライブラリに驚きつつも、覚えなければいけないことが多くてなかなか大変です。
この中でも特に嵌ったものがClojureScript(Closure Library)。
情報が少ないので調べるのにも結構苦労します。
ClojureScript自体はとてもいいものなので、普及のため/覚え書きに基本的なことを書いていきます。

ClojureScript

ClojureScriptはGoogle Closure Libraryに依存しています!(重要)
なので、ClojureとClosure Libraryの両方を使えるようにならなければなりません!
学習コスト高いですね。しかし、言語的に強力なClojureと強大なライブラリ群のClosure Libraryが合わさっているので最強の環境だと思うのですよ!
なので皆さん是非使って私に教えて下さい。

開発環境


ClojureのデファクトなビルドツールとしてLeiningenがあります。
そして、ClojureScriptをビルドするpluginとしてlein-cljsbuildが存在します。
これなしには開発できないレベルで便利です。
lein cljsbuild auto
とやっておくだけで信じられない速度でcompileしてくれます。
是非使いましょう。

emacsを使っている人は、clojurescript-modeというものもあるので入れておくと便利だと思います。

cljs


では、実際のコードをみてポイントを書いていきたいと思います。

(ns onedit
(:require [goog.dom :as dom]
[goog.events :as events]
[goog.debug.Logger :as logger]
[goog.debug.Console :as console]))
(def logger (logger/getLogger "onedit"))
(console/autoInstall)
(def reader
(let [reader (goog.global.FileReader.)]
(set! reader.onload (fn [e]
(.info logger e.target.result)
(dom/setTextContent (dom/getElement "buffer") e.target.result)))
reader))
(defn load [file]
(.readAsText reader file))
(events/listen (dom/getElement "open") goog.events.EventType.CLICK #(.click (dom/getElement "file")))
(events/listen (dom/getElement "file") goog.events.EventType.CHANGE (fn [e] (load (aget e.target.files 0))))
view raw gistfile1.clj hosted with ❤ by GitHub

ns


nsはClosure Libraryのprovideの役割を果たします。
(ns hoge)で定義された(def geso)は外部からhoge.gesoでアクセス出来ます。

:require


嵌ります。
:asが必須です。
あとUpperCamelだからといってクラス名と勘違いしないで下さい。
あくまでこれはClojureScriptなのです。
このrequireはClosure Libraryのrequireです。
注意して下さい。
:asで付けたaliasはstatic methodチックに呼び出せます。

参照


Clojureでは書き換え可能な参照を作るのにrefやatomを使いますが、これはJavaScriptなのでそんなことをせずにset!で直接varを書き換えることができます。
あとrefを使ってもSTMが使えるわけではないです。

goog.global


windowやdocumentなどのオブジェクトにアクセスしたい時はgoog.globalを使いましょう。
JavaScript Objectのインスタンスを作りたい時もこれを使うことになると思います。

※追記
goog.globalを使わなくてもjs/Stringのようにglobal objectを参照できました。

総括


とりあえずこんなところかな?
もっとたくさん苦労したところがあった気がする・・・・・
まあ、随時記事を書いていこうと思います。

私はClojure自体は慣れていたのですが、JavaScriptは初心者レベルだしClosure Libraryは触ったことがないしでとても大変でした。
ClojureScriptを書くとしてもJavaScriptがゆるふわなところには振り回されると思うので覚悟しておいて下さい。

2012年3月31日土曜日

play.api.libs.iteratee

明日はPlay Framework 2.0 ソースコードリーディングの会でIterateeのところを任されたのでちょっと使ってみたり。

Iteratee

適当な説明

  • Enumerator -> ストリーム
  • Iteratee -> ストリームに対する処理
  • Enumeratee -> ストリームの各要素に対する処理
以上!



Enumerator(1, 2, 3) &> Enumeratee.map(2 *) |>> Iteratee.fold(0)(_ + _)
List(1, 2, 3).map(2 *).fold(0)(_ + _)
view raw enumlist.scala hosted with ❤ by GitHub

scalaz.iterateeとの比較

Scalazのと比較すると簡単に思える不思議。

Scalaz
  • Scalazではモナド変換子版がベース。
  • IterateeにEnumeratee,Enumeratorを適用する。
  • 型クラスを利用した実装で提供される関数は多い。
  • だが型にうるさい。
  • 明示的に型を指定しなければいけないこともしばしば。

Play
  • Playでは結果がPromiseで返る。
  • EnumeratorにIteratee,Enumerateeを適用する。
  • 内部で使うことが目的なので提供される関数は少ない。
  • まあfoldあれば事足りるよね。
  • 推論が効く!Scalazのより楽だ!

結論:Scalazの方が高機能、Playの方はシンプル

ページ内文字列カウントスクリプト

ワンライナー!

import java.net.URL
import play.api.mvc.Codec
import play.api.libs.iteratee._
object Main extends App {
Enumerator.fromStream(new URL(args(0)).openStream) &>
Parsing.search(implicitly[Codec].encode(args(1))) &>
Enumeratee.collect { case Parsing.Matched(_) => 1 } |>>
Iteratee.fold(0)(_ + _) map (_ mapDone println) flatMap (_.run) await
}

処理を適用していく感じが関数型言語チックでとても書きやすいです。

2012年2月27日月曜日

Trampoline

トランポリンをご存知でしょうか。
相互再帰を最適化するものです。
私はプログラミングClojureを読んではじめて知りました。

相互再帰を最適化すると言っても、既存のものを最適化することは出来ません。
最適化のためには関数をトランポリンを使ったものにしなくてはなりません。

さて、そのトランポリンですが、実はScalazには用意されているのです!

多分6.0.4から入った・・・と思う。
私がStack Overflowで質問したからですね!フフン!

まぁ、たぶん質問しなくても入ってたと思いますが・・・・

なぜTrampolineが入ったのか

以下推測

それは、IOモナドの最適化のためだと思われます。

以前のIOモナドは、>>=で連結していくと評価時にIOの呼び出しが深くなってしまい、StackOverflowErrorを起こしていました。

そこで、トランポリンを導入することで最適化がされました。

命名規則をみると、Scalaz7から輸入されたようです。

なので、IOが問題になっていなかったら、Scalaz6では入っていなかったかも・・・?

使い方

プログラミングClojureの例を参考に偶数、奇数を調べる関数を書いてみる。

まずはトランポリンを使わずに

lazy val odd: Int => Boolean = {
case 0 => false
case n => even(n - 1)
}
lazy val even: Int => Boolean = {
case 0 => true
case n => odd(n - 1)
}

どれくらいでスタックがあふれるか調べる。

assert(odd(1))
assert(odd(11))
assert(odd(111))
assert(odd(1111))
assert(odd(11111))
// assert(odd(111111)) StackOverflow

6桁で落ちるとは・・・・

トランポリン版

lazy val odd: Int => Free.Trampoline[Boolean] = {
case 0 => Free.return_(false)
case n => Free.suspend(even(n - 1))
}
lazy val even: Int => Free.Trampoline[Boolean] = {
case 0 => Free.return_(true)
case n => Free.suspend(odd(n-1))
}

実行するにはrunメソッドを呼びます。

assert(odd(11111).run)
assert(odd(111111).run)
assert(odd(1111111).run)
assert(odd(11111111).run)
assert(odd(111111111).run)

これは全く落ちない。
あと多分速い。

※追記
遅かった・・・・

結論

プログラミングClojureにあるとおり、なるべく最適なアルゴリズム、自己再帰で書きましょう。
IOモナドのような特殊なケースはあまりないと思います。

さて、このTrampoline、じつはtype aliasです。
type Trampoline [+A] = Free[Function0, A]
と定義されており、Freeがもっと一般化されたなにかであることがわかります。

Freeについてはいつか調べるor誰か調べてくれる・・・はず・・・・・

2012年2月17日金曜日

type Self


に対し、りりかるろじかるさんが反応をくれたので書いてみます。

最初に、これが何の役に立つかですが、

trait Foo {
type Self <: Foo
val i: Int
def f(i: Int): Self
def g(i: Int) = Option(f(i))
}
case class Bar(i: Int) extends Foo {
type Self = Bar
def f(i: Int) = Bar(i)
def h = "hoge"
}
view raw self1.scala hosted with ❤ by GitHub

と定義したとき、

val bar: Bar = Bar(100)
assert(bar == Bar(100))
val foo: Bar = bar.f(10)
assert(foo == Bar(10))
val foobar: Option[Bar] = foo.g(1)
assert(foobar == Option(Bar(1)))
val baz: String = foo.h
assert(baz == "hoge")
view raw self2.scala hosted with ❤ by GitHub

となります。

ポイントは、
  • Barからfを呼び出すとBarが返る。
  • Barからgを呼び出すとOption[Bar]が返る。
  • fの返り値からhを呼び出せる。
といったところでしょうか。

つまり、スーパークラスでサブクラスの値を返すことができるということです。

しかし、これにはサブクラスがいちいちSelfを定義しなければいけないので楽したいですよね。
そこで、りりろじさんに考えて頂いた方法を書いていきます。

this.type

this.typeを使って定義すると、

trait Foo {
val i: Int
def f(i: Int): this.type
def g(i: Int) = Option(f(i))
}
case class Bar(i: Int) extends Foo {
type Self = Bar
def f(i: Int) = Bar(i).asInstanceOf[this.type]
def h = "hoge"
}
view raw thistype1.scala hosted with ❤ by GitHub

となります。

Bar.this.typeが要求されているところでBarを渡すとコンパイルエラーがでます。
なので、asInstanceOfを使って定義します。

val bar: Bar = Bar(100)
assert(bar == Bar(100))
val foo: Bar = bar.f(10)
assert(foo == Bar(10))
/*
* scala> val foobar: Option[Bar] = foo.g(1)
* <console>:12: error: type mismatch;
* found : Option[Foo]
* required: Option[Bar]
* val foobar: Option[Bar] = foo.g(1)
* ^
*/
val baz: String = foo.h
assert(baz == "hoge")
view raw thistype2.scala hosted with ❤ by GitHub

なんかエラー出てる・・・・
うーん、これはどういうことでしょう?
gの返り値をOption[this.type]と明示的に書いても変わりません。
むむむむむ・・・・・えらいひと、教えて下さい。

型パラメータ

Genericsまんせー
ということで書いてみる。

trait Foo[A <: Foo[_]] {
val i: Int
def f(i: Int): A
def g(i: Int) = Option(f(i))
}
case class Bar(i: Int) extends Foo[Bar] {
def f(i: Int) = Bar(i)
def h = "hoge"
}
val bar: Bar = Bar(100)
assert(bar == Bar(100))
val foo: Bar = bar.f(10)
assert(foo == Bar(10))
val foobar: Option[Bar] = foo.g(1)
assert(foobar == Option(Bar(1)))
val baz: String = foo.h
assert(baz == "hoge")
view raw generics1.scala hosted with ❤ by GitHub

おお、ちゃんと動きますね。

この型パラメータ版とtype Self版で比べてみると、
  • 型パラメータ有無
  • どちらも、型の情報がなくなると意味がない
  • 継承元がtype Selfを書くか、型パラメータに型を書くか
などあります。

型パラメータを使ったほうが楽に書けそうですが、type Selfを使うと楽になることもあります。

trait Foo {
type Self <: Foo
val i: Int
def f(i: Int): Self
}
trait Baz { self: Foo =>
def g(i: Int) = Option(f(i))
}
case class Bar(i: Int) extends Foo with Baz {
type Self = Bar
def f(i: Int) = Bar(i)
}
view raw self3.scala hosted with ❤ by GitHub

このように、複数のtraitでSelfを使う場合はこの書き方が有利なようです。


他にもっといい方法があったら是非教えてくださいねー!

2012年2月11日土曜日

Scalaでポイントフリースタイル

関数合成の話がTLであったので書いてみる。

最近だとゆろよろさんの記事が面白い。
http://d.hatena.ne.jp/yuroyoro/20120203/1328248662
http://d.hatena.ne.jp/yuroyoro/20120206/1328513534

Category

とりあえずCategoryから。

Categoryとか知らない人は
https://github.com/quassia88/introduction-to-category-theory-in-scala-jp/wiki
を見て、なんとなく分かってみてください。

この圏論入門の記事だとCategoryのインスタンスはFunctionしか書いてなくて、抽象化をしていることがわかりにくいですが、ScalazではCategoryのインスタンスにFunction, PartialFunction, Kleisliがあります。

※コード例はScalaz7

import scalaz.std.function._
function1Instance.id(5) assert_=== 5
lazy val show: Int => String = _.shows
lazy val len: String => Int = _.length
lazy val f: Int => Int = function1Instance.compose(len, show)
f(100) assert_=== 3
view raw category1.scala hosted with ❤ by GitHub

idが恒等関数、composeが合成関数です。

>>>, <<<というシンタックスもあります。

(len <<< show)(20) assert_=== 2
(len >>> show)("100") assert_=== "3"
view raw category2.scala hosted with ❤ by GitHub

Arrow

究極的には関数合成だけでプログラムを書くこともできますが、>>>だけではなかなか難しいところがあります。

そこで登場するものが***と&&&。
この2つは関数を並列に合成します。

***はA => BとC => Dから(A, C) => (B, D)を返します。
&&&はA => BとA => CからA => (B, C)を返します。

(show *** len)(5, "hello") assert_=== ("5", 5)
(show &&& (show >>> len))(100) assert_=== ("100", 3)
view raw arr.scala hosted with ❤ by GitHub


関数合成を意識して、n番目のフィボナッチ数を得る関数を書いてみます。

lazy val fst: ((Int, Int)) => Int = _._1
lazy val snd: ((Int, Int)) => Int = _._2
lazy val add: ((Int, Int)) => Int = _.fold(_ |+| _)
lazy val pair: ((Int, Int)) => (Int, Int) = snd &&& add
lazy val recur: Int => ((Int, Int)) => (Int, Int) = {
case 0 => identity
case n => (n - 1 |> recur) >>> pair
}
lazy val init: (((Int, Int)) => (Int, Int)) => (Int, Int) = (0, 1) |> _
lazy val fib: Int => Int = recur >>> init >>> fst
(0 to 9).map(fib).toList assert_=== List(0, 1, 1, 2, 3, 5, 8, 13, 21, 34)
view raw fib.scala hosted with ❤ by GitHub

|>はパイプ演算子と言われるもので、値を関数に適用するものです。

並列合成の使いどころは、ある値を複数回使うときです。

ScalazではTupleでfoldが使えるので別々に計算したものを簡単にまとめることが出来ます。

Scalaでも、Let's point free!

2012年2月5日日曜日

ClojureとFXMLとRuby

この前Clojureで試したのですが、fx:idが使えず微妙な感じでしたが、解決方法を見つけました!

JavaFXではfx:scriptタグというのがありますよね?

このタグにJavaScriptのコードを書いているサンプルコードをよく見ますが、実はこれ、
JavaScript以外でも動くのですよ!

いや、まずJavaScriptが動いているという時点でなにか疑問を持たなければいけなかったのですが。

fx:script

この機能はJSR-223で実現されています。
恐らく。

この機能標準ライブラリにあることを最近知りました。
わざわざダウンロードしていたのは何だったのか・・・・・

fx:scriptを使うためには、言語を指定します。

この時に、FXMLLoaderはgetEngineByNameを使ってスクリプトを実行するためのScriptEngineを取得します。

なので、JSR-223に対応している言語ならば動くのですよ!

とりあえず、安定して動きそうなJRubyを試してみました。

<?xml version="1.0" encoding="UTF-8"?>
<?language ruby?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.*?>
<BorderPane xmlns:fx="http://javafx.com/fxml">
<fx:script>
def f(e)
e.source.text = 'Action!'
end
</fx:script>
<center>
<Button text="Test" onAction="f($event)" />
</center>
</BorderPane>
view raw root1.xml hosted with ❤ by GitHub

実行



なぜか重いけど動く・・・!

eventはグローバルな変数で、アクションを起こしたい時はこいつを渡しておけばいいようです。

JRubyではgetは書かなくていいし、setも=を使って書けるしなかなか楽ですね。

fx:id

さて、これでfx:idの値を参照してみます。

<?xml version="1.0" encoding="UTF-8"?>
<?language ruby?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.*?>
<BorderPane xmlns:fx="http://javafx.com/fxml">
<fx:script>
def f
$label.text = 'Action!'
end
</fx:script>
<top>
<Label fx:id="label" text="Test" />
</top>
<center>
<Button text="Test" onAction="f" />
</center>
</BorderPane>
view raw root2.xml hosted with ❤ by GitHub

実行



動いたー!

Clojureで試す

なぜ最初からClojureで動かさなかったのか、それは動かなかったからです。

で動かしてみましょう、ぬるぽが出るはずです。

そこで、"clojure jsr 223"でぐぐってみるとこんなプロジェクトが。
http://code.google.com/p/clojure-jsr223/

そう、clojure標準ではJSR-223に対応していなかったのです。

なのでこのライブラリをdependenciesに追加すれば動くようになります。

<?xml version="1.0" encoding="UTF-8"?>
<?language Clojure?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.*?>
<BorderPane xmlns:fx="http://javafx.com/fxml">
<top>
<Label fx:id="label" text="Test" />
</top>
<center>
<Button text="Test" onAction="(f)" />
</center>
<fx:script>
(defn f [] (.setText label "Action!"))
</fx:script>
</BorderPane>
view raw root3.xml hosted with ❤ by GitHub

fx:scriptを後ろに置いたのは、labelが見えないと怒られるからです。


これにてこの問題は解決です。

メジャーなScript言語はJavaFXで動くので、是非試してみて下さい。

2012年2月3日金曜日

Tagged, Union

Scalaz7のSNAPSHOTがリポジトリにあったので、いろいろ試しています。

設計が大幅に変わったので6系にべったりだと移行はかなり大変な気がします。

Tagged

Scalaz7ではNewTypeがなくなり、代わりにTagged Typesというものが入りました。

これは既存の型にタグを付けることで、コンパイラに別の型のように見せることができます。
さらに、この型は元の型が必要な場合にはunboxされるという素敵なものです。

trait Foo
type Bar = Int @@ Foo
view raw tagged1.scala hosted with ❤ by GitHub

@@がタグを付けるためのものです。
ここではIntにFooタグを付けたものをBarとしました。

val bar: Bar = Tag[Int, Foo](100)
lazy val f: Bar => Int = 100 |+| _
f(bar) assert_=== 200
// f(100) Error
view raw tagged2.scala hosted with ❤ by GitHub

この型を取る関数にIntを渡すとコンパイルエラーになります。

val foobar: Long @@ Foo = Tag(10)
lazy val g: Long @@ Foo => Long = 100L |-| _
g(foobar) assert_=== 90L
type Baz[T] = T @@ Foo
val baz: Baz[String] = Tag("Scalaz")
lazy val h: Baz[String] => String = (_: String).multiply(2)
h(baz) assert_=== "ScalazScalaz"
view raw tagged3.scala hosted with ❤ by GitHub

エイリアス付けるとTagを使ったときに推論が効かないので、@@を直接書くか、型パラメータをとるようにするといいです。

Scalaz7ではNewTypeを使っていた殆どはTagged Typesに置き換えられました。
あまりなにも考えずNewTypeばかり継承していると移行が大変ですのでこのことも考えておきましょう。 (体験者談)

Tagged Typesについて詳しく知りたい場合はこちらを。

Union

最近入って、SNAPSHOTにもまだ入ってないもの。
型の和が簡単に作れてしまう優れもの。
これもunboxされる。

type Hoge = UnionTypes.t[Int]#t[String]
type Huga = UnionTypes.t[Long]#t[Double]
view raw union1.scala hosted with ❤ by GitHub

t[A]#t[B]でAとBの和を作ります。
この型を直接引数に取るのではなく、Containsという型を使います。

def f[T](t: T)(implicit ev: UnionTypes.Contains[T, Hoge]): Int = t match {
case i: Int => i
case s: String => s.length
}
f(100) assert_=== 100
f("Scalaz7") assert_=== 7
// f(10.05) Error
val huga: UnionTypes.Union[Huga] = Converter(0.5).union
huga.value.asInstanceOf[Double] assert_=== 0.5
view raw union2.scala hosted with ❤ by GitHub

Converterで値を持てるけど、Anyになってしまうので微妙。

Tagged Typeにも言えますが、Unionはsealed traitとcase classで新しいデータ構造を作るより手軽に使えるところがいいところですね。

Scalaz7は全体的に効率化がされていて、Unboxedなこれらの型もそのためにあるのだと思います。

便利な上に効率もいいのでガンガン使ってしまいましょう。

早くScalaz7が出て欲しいなぁ。

2012年2月2日木曜日

ClojureとFXML

JavaFX for Linux キタ━(゚∀゚)━!
ということでさわってみました。

ただ動かすのも面白くない、というかJava面白くない、ということでClojureから呼び出してみました。

JavaFXで私が一番注目していたものはFXML。
XMLは好きじゃないけど、ちゃんとロジックが分離できそうだし、GWTのUiBinderを気に入っていたので楽しみにしていました。

というわけでClojure+FXMLで書いてみました。

プロジェクトの設定(Linux)

Leiningenを使います。

多分Winの場合はそのまま動く。
Linuxの場合は http://www.oracle.com/technetwork/java/javafx/downloads/devpreview-1429449.html から Linux 32-bit をダウンロードして、 lib に jfxrt.jar を、 bin に *.so を置いて下さい。

main.clj

まずはApplicationを継承した起動ポイントを書く。

(ns fx.main
[:gen-class
:extends javafx.application.Application]
[:import
[javafx.application Application]
[javafx.scene Scene]]
[:use
[fx.pane :only [root]]])
(defn -start [this stage]
(let [scene (Scene. (root))]
(doto stage
(.setTitle "test")
(.setScene scene)
(.show))))
(defn -main [& args]
(Application/launch fx.main args))
view raw main.clj hosted with ❤ by GitHub

main/launchだとエラーが出る謎。

pane.clj

xmlをFXMLLoaderでロードするだけ。

(ns fx.pane
[:import
[javafx.fxml FXMLLoader]])
(defn root []
(let [fxml (.getResource (class root) "root.xml")]
(FXMLLoader/load fxml)))
view raw pane.clj hosted with ❤ by GitHub

root.xml

fx:controllerを指定してButtonを置いてonActionを設定する。

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.*?>
<BorderPane xmlns:fx="http://javafx.com/fxml"
fx:controller="fx.controller">
<center>
<Button text="Test" onAction="#action" />
</center>
</BorderPane>
view raw root.xml hosted with ❤ by GitHub

controller.clj

Buttonから呼び出される関数を定義する。

(ns fx.controller
[:gen-class
:methods [[action [javafx.event.ActionEvent] void]]]
[:import
[javafx.fxml FXML]])
(defn -action [this e]
(let [button (.getSource e)]
(.setText button "Action!")))
view raw controller.clj hosted with ❤ by GitHub

gen-classに:methodsをわざわざ書くのがめんどう。

実行



なんかあっさり動いてしまった。

これでバリバリClojureでGUIが書けるぞー










と思ったらそんなことなかった。

fx:id

Buttonを押したらLabelのテキストが変わるようにしてみたい。
そんな場合はfx:idでControlに名前をつけるとコントローラで扱えるようになる。
その時にコントローラはmutableFXMLアノテーションが付いたフィールドを持っていなければならない。

gen-classでフィールドは指定できない。
Clojureでは:stateを使うことで状態を実現している。

一番最初に思いついたものはdeftypeを使う方法。

deftype

deftypeではmutableなフィールドを持てます。
なのでdeftypeでコントローラを作ってみます。

(defprotocol Action
(action [this e]))
(deftype Controller [^{:unsynchronized-mutable true} label]
Action
(action [this e]
(.setText label "Action!")))
view raw Controller.clj hosted with ❤ by GitHub

でもこれだと、コンストラクタが引数をとるのでFXMLLoaderがインスタンスを作れない。
なのでこれを更に継承・・・・・出来なかった。
なぜならdeftypeで作ったクラスはfinalが修飾されている。
メソッドの型も曖昧なままなので継承できたとしても動くかどうかはわからない。

こうなると別の方法を取るしかないですね。

別の方法といってもClojureでclassを作るにはdeftype, defrecord, gen-classしかない。
deftypeは先程の通りコンストラクタが引数をとってしまう。
defrecordではそもそもmutableなフィールドを持てない。
gen-classはフィールドを指定できない。

Javaを使うことを・・・強いられてるんだ!

ここで登場Java!
嫌いじゃないけど使いたくないJava!

Java側でmutableでFXMLアノテーションが付いた変数を宣言しておこうという戦略。

package fx;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
abstract class Controller {
@FXML private Label label;
}
view raw Controller.java hosted with ❤ by GitHub

これを継承してさあ実行。


javafx.fxml.LoadException: Value is already set.


\(^o^)/

ヤンナルネ・・・・・

結果

onActionで関数を指定して呼び出すことはできたがfx:idが使えなかった。

誰か解決方法があったら教えて下さい!

2012年1月22日日曜日

coq2scalaをさわってみた

http://proofcafe.org/wiki/Coq2Scala を試してみました。

インストールはhgでソースコードとってきて、
./configure
make world
make install
でおk。

Extraction Language Scala. が使えちゃうCoqがインストールされます。

使ってみた

とりあえず自然数と足し算を定義してみます。

Extraction Language Scala.
Module ScalaTest.
Inductive Nat := Zero | Succ : Nat -> Nat.
Fixpoint nplus n m :=
match n with
| Zero => m
| Succ n' => Succ (nplus n' m)
end.
Notation "n + m" := (nplus n m).
End ScalaTest.
Extraction "coq.scala" ScalaTest.
view raw scala.v hosted with ❤ by GitHub

吐き出されたScalaのコード。

object CoqMain {
object CoqModule {
sealed abstract class Nat
case class Zero() extends Nat
case class Succ(x1: Nat) extends Nat
def nat_rect[A] : (A => ((Nat => (A => A)) => (Nat => A))) = {
(f:A) => (f0:(Nat => (A => A))) => (n:Nat) =>
n match {
case Zero() => f
case Succ(n0) => f0(n0)(nat_rect(f)(f0)(n0))
}
}
def nat_rec[A] : (A => ((Nat => (A => A)) => (Nat => A))) = {
(f:A) => (f0:(Nat => (A => A))) => (n:Nat) =>
n match {
case Zero() => f
case Succ(n0) => f0(n0)(nat_rec(f)(f0)(n0))
}
}
def nplus : (Nat => (Nat => Nat)) = {
(n:Nat) => (m:Nat) =>
n match {
case Zero() => m
case Succ(n$prime) => Succ(nplus(n$prime)(m))
}
}
}
}
view raw coq.scala hosted with ❤ by GitHub

Notationは無視される?
パラメータとらない場合はcase object使って欲しいかも・・・・
あとインデントも付くとうれしい。

ClassとInstanceを使ってみる

Monoidを定義してみた。

私はtraitとimplicit parameterでかっこ良く変換されるのかな!?と期待。

Class Monoid (M : Type) := {
plus : M -> M -> M;
zero : M
}.
Instance NMonid : Monoid Nat := {
plus := nplus;
zero := Zero
}.
view raw monoid.v hosted with ❤ by GitHub

結果は・・・・・

sealed abstract class Monoid[M]
case class Build_Monoid[M](x1: (M => (M => M)), x2: M) extends Monoid[M]
def monoid_rect[A, B] : (((A => (A => A)) => (A => B)) => (Monoid[A] => B)) = {
(f:((A => (A => A)) => (A => B))) => (m:Monoid[A]) =>
m match {
case Build_Monoid(x, x0) => f(x)(x0)
}
}
def monoid_rec[A, B] : (((A => (A => A)) => (A => B)) => (Monoid[A] => B)) = {
(f:((A => (A => A)) => (A => B))) => (m:Monoid[A]) =>
m match {
case Build_Monoid(x, x0) => f(x)(x0)
}
}
def plus[A] : (Monoid[A] => (A => (A => A))) = {
(monoid:Monoid[A]) =>
monoid match {
case Build_Monoid(plus0, zero0) => plus0
}
}
def zero[A] : (Monoid[A] => A) = {
(monoid:Monoid[A]) =>
monoid match {
case Build_Monoid(plus0, zero0) => zero0
}
}
def nMonid : Monoid[Nat] = Build_Monoid(nplus, Zero())
view raw monoid.scala hosted with ❤ by GitHub

(´・ω・`)ショボーン
明示的にインスタンスを渡して関数を得る感じですね。
まぁ、まだまだ開発途中なのでこれからどうなるかはわかりません。

最後に証明付きコード

Extraction Language Scala.
Module ScalaTest.
Inductive Nat := Zero | Succ : Nat -> Nat.
Fixpoint nplus n m :=
match n with
| Zero => m
| Succ n' => Succ (nplus n' m)
end.
Notation "n + m" := (nplus n m).
Lemma nplus_assoc : forall a b c, a + (b + c) = (a + b) + c.
intros.
induction a.
reflexivity.
simpl.
rewrite IHa.
reflexivity.
Qed.
Lemma nplus_zero : forall n, n = n + Zero.
intros.
induction n.
reflexivity.
simpl.
rewrite <- IHn.
reflexivity.
Qed.
Lemma nplus_succ : forall n m, Succ (n + m) = n + (Succ m).
intros.
induction n.
reflexivity.
simpl.
rewrite IHn.
reflexivity.
Qed.
Lemma nplus_comm : forall n m, n + m = m + n.
intros.
induction n.
apply nplus_zero.
simpl.
rewrite IHn.
apply nplus_succ.
Qed.
Fixpoint nmult n m :=
match n with
| Zero => Zero
| Succ n' => nplus m (nmult n' m)
end.
Notation "n * m" := (nmult n m).
Lemma distr : forall a b c, a * c + b * c = (a + b) * c.
intros.
induction a.
reflexivity.
simpl.
rewrite <- IHa.
rewrite nplus_assoc.
reflexivity.
Qed.
Lemma nmult_assoc : forall a b c, a * (b * c) = (a * b) * c.
intros.
induction a.
reflexivity.
simpl.
rewrite IHa.
apply distr.
Qed.
Lemma nmult_zero : forall n, Zero = n * Zero.
intros.
induction n.
reflexivity.
simpl.
apply IHn.
Qed.
Lemma nmult_succ : forall n m, n + n * m = n * Succ m.
intros.
induction n.
reflexivity.
simpl.
rewrite <- IHn.
rewrite nplus_assoc.
rewrite nplus_assoc.
replace (n + m) with (m + n).
reflexivity.
apply nplus_comm.
Qed.
Lemma nmult_comm : forall n m, n * m = m * n.
intros.
induction n.
apply nmult_zero.
simpl.
rewrite IHn.
apply nmult_succ.
Qed.
Class Monoid (M : Type) := {
plus : M -> M -> M;
zero : M;
id : forall m, plus m zero = m
}.
Instance NMonid : Monoid Nat := {
plus := nplus;
zero := Zero
}.
intros.
induction m.
reflexivity.
simpl.
rewrite <- nplus_zero.
reflexivity.
Qed.
Eval compute in zero.
End ScalaTest.
Extraction "test.scala" ScalaTest.
view raw coq2scala.v hosted with ❤ by GitHub
object CoqMain {
object CoqModule {
sealed abstract class Nat
case class Zero() extends Nat
case class Succ(x1: Nat) extends Nat
def nat_rect[A] : (A => ((Nat => (A => A)) => (Nat => A))) = {
(f:A) => (f0:(Nat => (A => A))) => (n:Nat) =>
n match {
case Zero() => f
case Succ(n0) => f0(n0)(nat_rect(f)(f0)(n0))
}
}
def nat_rec[A] : (A => ((Nat => (A => A)) => (Nat => A))) = {
(f:A) => (f0:(Nat => (A => A))) => (n:Nat) =>
n match {
case Zero() => f
case Succ(n0) => f0(n0)(nat_rec(f)(f0)(n0))
}
}
def nplus : (Nat => (Nat => Nat)) = {
(n:Nat) => (m:Nat) =>
n match {
case Zero() => m
case Succ(n$prime) => Succ(nplus(n$prime)(m))
}
}
def nmult : (Nat => (Nat => Nat)) = {
(n:Nat) => (m:Nat) =>
n match {
case Zero() => Zero()
case Succ(n$prime) => nplus(m)(nmult(n$prime)(m))
}
}
sealed abstract class Monoid[M]
case class Build_Monoid[M](x1: (M => (M => M)), x2: M) extends Monoid[M]
def monoid_rect[A, B] : (((A => (A => A)) => (A => (Unit => B))) => (Monoid[A] => B)) = {
(f:((A => (A => A)) => (A => (Unit => B)))) => (m:Monoid[A]) =>
m match {
case Build_Monoid(x, x0) => f(x)(x0)(())
}
}
def monoid_rec[A, B] : (((A => (A => A)) => (A => (Unit => B))) => (Monoid[A] => B)) = {
(f:((A => (A => A)) => (A => (Unit => B)))) => (m:Monoid[A]) =>
m match {
case Build_Monoid(x, x0) => f(x)(x0)(())
}
}
def plus[A] : (Monoid[A] => (A => (A => A))) = {
(monoid:Monoid[A]) =>
monoid match {
case Build_Monoid(plus0, zero0) => plus0
}
}
def zero[A] : (Monoid[A] => A) = {
(monoid:Monoid[A]) =>
monoid match {
case Build_Monoid(plus0, zero0) => zero0
}
}
def nMonid : Monoid[Nat] = Build_Monoid(nplus, Zero())
}
}
view raw coq2scala.scala hosted with ❤ by GitHub

proof-editing mode楽しいです。
ScalaでPDDの時代が来るかもですね。

2012年1月10日火曜日

Coqはじめました。

あけましておめでとうございます。

何か新しいことをはじめようと思い、Coqに手を出しました。

まだ慣れていないけど、なかなか楽しいです。

証明楽しいとかどんな変態(褒め言葉)なんだ!

と思っていましたが、私も無事変態の仲間に入ることが出来ました。

Coqをはじめる

Coqを使い始めたきっかけは冬休みの宿題で読書感想文です。

私は選んだ本はもちろん「数学ガール」。

なかなかおもしろいのでオススメです。

数学が得意というわけでもない私は、理解の手助けとしてプログラムを書こうと思いました。

そこでCoqの登場です!

本に出てくる命題を証明していくことにしました。

Coqに関する資料は決して多くありませんが、良いチュートリアルがあるので、まずはこれらを参考にしましょう。

書いてみた

正の整数型を作って加算を定義し、結合法則と交換法則を証明してみました。

Inductive P : Set :=
I : P
| PS : P -> P.
Fixpoint Padd (n m : P) : P :=
match n with
| I => PS m
| PS n' => PS (Padd n' m)
end.
Lemma Padd_assoc : forall (a b c: P), Padd a (Padd b c) = Padd (Padd a b) c.
Proof.
intros.
induction a.
reflexivity.
simpl.
rewrite IHa.
reflexivity.
Qed.
Lemma Padd_comm : forall (n m : P), Padd n m = Padd m n.
Proof.
intros.
assert (Padd I m = Padd m I).
induction m.
reflexivity.
replace (PS m) with (Padd I m).
rewrite <- Padd_assoc.
rewrite <- IHm.
reflexivity.
reflexivity.
induction n.
rewrite H.
reflexivity.
replace (PS n) with (Padd I n).
rewrite Padd_assoc.
rewrite <- H.
rewrite <- Padd_assoc.
rewrite IHn.
rewrite <- Padd_assoc.
reflexivity.
reflexivity.
Qed.
view raw pos.v hosted with ❤ by GitHub

結合法則はスラスラと書けて、「うはwっww楽勝www」とほざいていたのですが、交換法則は丸一日かかりました・・・・・

この中で使われているタクティクは
  • intros (導入)
  • induction (帰納)
  • reflexivity (再帰?反射?)
  • simpl (簡約)
  • rewrite (書き換え)
  • assert (表明)
  • replace (置換)
だけです。

プログラミングに慣れている人は、こういったタクティクを覚えていくだけで書けるようになると思います。

書いてみて

私はパズルを解くようなものだと感じました。

Qed. が書けたときはとても嬉しいです。

自然数の定義、自然数の加算・比較、整数の定義、ソースコードをみるだけでも勉強になります。

一度は触ってみてはどうでしょう。

Coq、おもしろいですよ!