uehaj's blog

Grな日々 - GroovyとかGrailsとかElmとかRustとかHaskellとかReactとかFregeとかJavaとか -

elmでやってみるシリーズ3: xeyesっぽい目が動く

elmでやってみるシリーズ3: xeyesっぽい目が動く。

import Mouse

-- 目の輪郭を書く
eyeframe x y = move (x,y) <| outlined { defaultLine | width <- 5 } <| oval 40 50

-- 目玉を書く
eyeball x y = move (x,y) <| filled black <| circle 5

-- 目(=目玉+目の輪郭)を一個分書く。tは目玉の見ている方向。
eye r x y t = [eyeframe x y, eyeball ((r * (cos t))+x) ((r * (sin t))+y)]

-- 目を2つ書く
eyes t = collage 200 200 <| (eye 15 -20 0 t)++(eye 15 20 0 t)

-- 「目の座標位置を計算する(=「マウス座標の原点から見た角度」をatanで求める)」という純粋関数にマウス座標をリフト適用。シグナル化された目玉の角度が返る。
mouseDirec = let f x y = (atan2 (100-(toFloat y)) ((toFloat x)-100))
           in f <~ Mouse.x ~ Mouse.y

-- 目を2つ書く、という純粋関数に、シグナル化された目玉の角度(mouseDirec)をリフト適用する。
main = eyes <~ mouseDirec


この簡潔さよ! ブラボー。Elm ブラボー。(さぼってますけどね。)

ブラウザ内で編集したり実行したい場合はこちらからどうぞ。

気づいたこと

  • SignalはElmのFRPのキモである。Signalを制するものはElmを制する。
  • mainの直下にSignalを使うReactive Codeを固めて、他はなるべくピュアにしたい、と思うじゃない。でもそれはたぶん無理な相談である。いや、やれるところはそうすれば良いと思うけど、「Signalを返す関数」はElmプログラミングにとって中核的であり最重要。それをどう組むべきかという問題からは逃げられない。
  • 「なるべくピュアにしたい」という理由は、liftの適用は演算子<~,~があろうともやっぱり可読性に難があるから。今のElmにはセクションがないし、演算子関数をliftすると演算子っぽくなくなるし、flip駆使しても限界がある。もっと言えばSignalはモナドではないし*1、仮にモナドであってもElmにDo記法は無い。で、考えたのですが、ベストプラクティスとしてのコーディングパターンは、上でもしているように、lift一発でできないところは以下のように関数ごとに純粋部とリアクテイブ部に分けることではないかと今のところ思っております*2。なお今のElmはwhere句は使えません。
func = let f x y ... = 純粋コード
      in  f <~ Signal X ~ Singal Y ... -- 必要なSignalを使用するリアクティブコード

関連エントリ


「Elmでやってみる」シリーズのまとめエントリ - uehaj's blog

*1:Signalがモナドでなくアプリカティブなのは理由があって、「SignalのSignal」を禁止するためだそうな。つまりSignalにjoinは定義できない・してはならない。bind/flatMapも然り。

*2:fは無名関数でも良いかもしれない。letのセマンティクスが無名関数とその適用に差があるのか、たとえばElmにlet多相があるかどうかは不明。

elmでやってみるシリーズ2: ●を往復させる

import Window
import Debug (log)

-- 旧速度vと、現在位置xから、新しい速度を求める。(はじっこだったら速度を反転する)
newv : Int -> Int -> Int
newv v x = if (x<= -10 || 10<=x) then -v else v

-- 状態を表わす(現在位置、速度)のタプルに対して、次の状態をシグナル(fps 20)のタイミングで累積的に計算しなおすということを繰り返す。初期状態は(0,1)。結果得られるのは、(現在位置,現在の速度)を表現するシグナルである。
pos = foldp (\it (x,v) -> 
            let nv = (newv v (log "x" x))
            in (x+nv, nv)
            ) (0,1) (fps 20)

-- 指定位置x,yを中心に●を書く(半径20の円を書いて赤で塗り潰す)。
drawCircle : Float -> Float -> Form
drawCircle x y = move (x,y) <| filled red (circle 20)

-- 一連の背景線を書く
drawMatrix : [Form]
drawMatrix = map (\x -> drawLine <| x*10) [-10..10]

-- 背景線を1本書く
drawLine : Float -> Form
drawLine x = traced (solid blue) ( segment (x,-100) (x,100) )

-- 「一連の背景線と、●を書いたコラージュを生成する純粋関数f」に、現在位置(posタプルの第一要素)をシグナル化したまま取り出してリフト適用
main : Signal Element
main=let f = \w h p-> collage w h ([drawCircle ((toFloat p)*10) 10]++drawMatrix)
     in f <~ Window.width ~ Window.height ~ (fst <~ pos)


解説的な何か

  • foldpのpは、pastのpで、その意味は実際に「過去から現在までのシグナルの履歴値を畳み込む」です。しかし、foldp使用に伴なうオーダーは実行開始時刻から現在までの時間経過には依存せず、O(1)です。実際には、シグナルの更新1回ごとに1回呼び出される関数を登録するというものです。foldpの第一引数の関数の第二引数(上で言う(x,v))は前回のfoldpの返り値を保持しています。この値が、いわゆる状態を保持・更新するためのelmにおいての唯一の手段です。
  • Debug.logは、JavaScriptでいうConsole.logを呼び出します。正格評価だからその時点の値が普通に出ます。

続く。

関連エントリ


「Elmでやってみる」シリーズのまとめエントリ - uehaj's blog

elmでやってみるシリーズ1: ●を動かす

elmでやってみるシリーズ1: ●を動かす。

import Window

pos : Signal Int
pos =let f = \tmp -> (tmp `mod` 30) - 10
        in f <~ foldp (\it acc-> acc + 1) 0 (fps 50)

drawCircle : Float -> Float -> Form
drawCircle x y = move (x,y) <| filled red (circle 20)

drawMatrix : [Form]
drawMatrix = map (\x -> drawLine <| x*10) [-10..10]

drawLine : Float -> Form
drawLine x = traced (solid blue) ( segment (x,-100) (x,100) )

main : Signal Element
main=let f = \w h p-> collage w h ([drawCircle ((toFloat p)*10) 10]++drawMatrix)
     in f <~ Window.width ~ Window.height ~ pos

続く。

感想

  • foldpは好き。
  • inputに対応するSignalってグローバル変数じゃね?まあ「マウスがクリックされる」とか「時間が経過する」という事実はグローバルなんだが、それによって引き起こされるイベントもそうなる…。プログラムからその値を能動的に制御できないから変数とは呼べない…のかな。でもユーザがフィールドに値を入力したり、ラジオボタンをクリックしたり、という操作によって設定項目をリアクティブに変更したその結果の、Signalである設定項目って、どう見てもグローバル変数だよね。まだよくわからない。

関連エントリ

Grails 3.0先取り!? Spring Boot入門ハンズオン

次回G*Workshopは、8/1 「Grails 3.0先取り!? Spring Boot入門ハンズオン」です。
ふるってのご参加をお待ちしております。



Grails 3.0先取り!? Spring Boot入門ハンズオン

申し込みはこちらから>
Grails 3.0先取り!? Spring Boot入門ハンズオン

予定の内容

Grailsの次期バージョン3.0でベースになることが予定されている、Spring界隈の新しいトレンド"Spring Boot"のハンズオンを通じて、Spring Bootのイメージを掴んでもらいたいと思います。内容は以下の通りです。

Spring Boot概要説明
Spring Bootを用いて簡単なアプリケーションを実際に作ってみる
(合計で約二時間弱)

「プログラムでシダを描画する」をelmで描画する

やや乗り遅れているネタとして、シダを描くというのを、elm言語でやってみました。
(追記: 改良版も作りました)

elm言語は、基本はHaskellライク文法(サブセット方向)に、F#とOCaml風味の演算子・文法を振り掛けた、ヒンドリーミルナー型推論・純粋関数型・正格評価の言語で、repl上もしくは主にJSにコンパイルしてブラウザ内で実行します*1。特徴はFRP,ファンクショナルリアクティブプログラミングをサポートする言語だということです。

以下がシダを描画するelmコード。もっといい書き方あると思うので気付いたらご指摘お願いします。

import Mouse
import Generator
import Generator.Standard

sida_width=500
sida_height=500

randomSeed = 12346789
gen = Generator.Standard.generator randomSeed
main = collage (sida_width*2) (sida_height*2) <~ ((f0 13 0 0 gen) <~ (flip (/) sida_width <~ (toFloat <~ Mouse.x)))

-- Original: w1x x y = 0.836 * x + 0.044 * y
w1x x y n = n * x + (1-n) * y
w1y x y = -0.044 * x + 0.836 * y + 0.169
w2x x y = -0.141 * x + 0.302 * y
w2y x y = 0.302 * x + 0.141 * y + 0.127
w3x x y = 0.141 * x - 0.302 * y
w3y x y = 0.302 * x + 0.141 * y + 0.169
w4x x y = 0
w4y x y = 0.175337 * y

f0 k x y gen0 n =
     let (seq, _)=f k x y gen0 n
     in [(move (0,0)) (filled black <| rect (sida_width*2) (sida_height*2))
     ]++seq

f k x y gen0 n =
    if (0 < k) then
      let (rnd1, gen1) = Generator.int32 gen0
          (seq1, gen2) = (f (k - 1) (w1x x y n) (w1y x y) gen1 n)
          (seq2, gen3) = if (rnd1 `mod` 3 == 0) then (f (k - 1) (w2x x y) (w2y x y) gen2 n) else ([], gen2)
          (seq3, gen4) = if (rnd1 `mod` 3 == 1) then (f (k - 1) (w3x x y) (w3y x y) gen3 n) else ([], gen3)
          (seq4, gen5) = if (rnd1 `mod` 3 == 2) then (f (k - 1) (w4x x y) (w4y x y) gen4 n) else ([], gen4)
      in
          (seq1 ++ seq2 ++ seq3 ++ seq4, gen5)
    else
        ((plot (x * sida_width * 0.98)
               (y * sida_height * 0.98)), gen0)

plot x y = [move (x,y) (filled green <| rect 1 1)]

リアクティブプログラミングというのは、私の理解では、時間経過とかマウスクリックとか、なんらかの(外部/外部)イベントに刻々と反応することの記述を容易にできるってことです(雑すぎる説明)。それがファンクショナルなのは、関数型ってことで、elmはさらにピュア・ファンクショナルなので副作用は記述できません。Signalというのが「変化する値」を表わしていて、これはHaskellのIOアクションが副作用を一手に背負っているようなものです。SignalはしかしモナドではなくArrowで、これでFRPを実現しているのを Arrowized FRPと呼ぶとか。ただそこらへんの詳しいことはわからなくても書けます*2

上記のコードは、マウスのx座標で反応するようにしました。FRPなのはmainだけであとは全部ピュアな関数です。x座標に対応して、シダの図のどのパラメータをどう動かすかは、適当にやったので動きは不自然です。あと再帰の深さを深くすると非常に遅くなります。Canvasに書き捨ててるんじゃなくて、描画命令(Form,形状)をリストで持ってるからです。

以下にはコンパイル結果のJSを登録しましたので、ブラウザ上で実際に動かして試すことができます。マウスを左右に動くとなんか動くわけです。

以下は、静止画像のスナップショットです(なので動きません)。

f:id:uehaj:20140703023021p:plain
f:id:uehaj:20140703023027p:plain

枝ぶりが貧弱なのは、速度上の問題ですorz。

以下はelm_dependencies.jsonの内容。標準関数以外には、random generatorというのを使っています。

{
    "version": "0.1",
    "summary": "concise, helpful summary of your project",
    "description": "full description of this project, describe your use case",
    "license": "BSD3",
    "exposed-modules": [],
    "elm-version": "0.12.3",
    "dependencies": {
        "jcollard/generator": "0.3"
    },
    "repository": "https://github.com/USER/PROJECT.git"
}


elmについては別途紹介記事を書くつもり。


「Elmでやってみる」シリーズのまとめエントリ - uehaj's blog

*1:elmをAltJS、すなわちJSの他の選択肢(オルタネティブ)と言うべきなのかはわからない。言うならばJS+CSS+HTML+DOM全体のオルタネティブな気がする。

*2:ちなみにdo記法もproc記法もelmにはありません。lift(n)もしくはアプリカティブスタイルを使って書きます。Haskellの<$>は<~、<*>は~に対応。

Groovyのtraitを使い倒す!

JGGUG G*Workshopに久々に参加しまして、GroovyのTraitについて、以下を話してきました!

盛況でした。いくにんかAndroid開発をされている人もいらっしゃったようで、Groovy on Androidが注目されているのも理由かもしれませんね。

http://togetter.com/li/682540
http://jggug.doorkeeper.jp/events/11365

SpockにおけるMock、Stub、Spyの違い

テスティングフレームワークSpockで相互作用中心のテストをする場合、いわゆるテストダブルとして、MockとStubとSpyの3種類を使えます。それぞれの意味などを簡単に解説します。
Spock全体含め詳しい解説はこちらなどを参照ください。
Mockに関しては、id:yamkazu 氏によるこちらのJGGUG G*Workshopの資料も秀逸です。

Stubとは

最初に、Stub(GroovyStub)についてです。Stubは後述Mockの低機能版であり、具体的には「モッキングができないMock」がStubです。StubができることはMockでできるので、本来Stubを使う必要はありません。モッキングしないことを明示したいなら、使えば良いでしょう。なお、Spock用語の「スタビング」とは別のものです(スタビングおよびモッキングについては後述)。

Mockとは

Mock(もしくはGroovyMock)は、テスト対象が使うクラスを指定して、そのクラスのインスタンスの真似をするオブジェクト(モックオブジェクト)を作ります。真似をするというのは、「元のクラスにあるのと同じシグネチャのメソッドを、モックオブジェクトに対して呼び出したりできる」ということです。Mockのメソッドを呼んでも、Mock元になったオブジェクトの本物のメソッドは呼び出されません。

たとえると、モックは「代役」です。本人は楽屋かなにかで休んでおり、リハーサル(テスト)では代役が呼び出しに応じて努めます。

以下、Mockの使用の例です:

class TestUnit3Spec extends UnitSpec {

   def test00() {
      setup:
        def bean = Mock(SomeBean3)
      when:
        def result = bean.methodOfSomeBean()
        println result
      then:
        1 * bean.methodOfSomeBean() >> 123
        result == 123
   }
}

class SomeBean3 {
   int methodOfSomeBean() {
     println "hoge"
     100
   }
}

上の例では、実際のメソッド methodOfSomeBean では「100」を返しているのにもかかわらず、あるいは"hoge"を出力しているのにもかかわらず、帰ってくる値は「123」だし(assert result=123)、println "hoge"は実行されず、当然"hoge"も出力されません。

ではその代役(モック)は何をするのか?というとですね、モックは、自身のメソッドが呼び出されたときに、「どんなメソッドがどんな引数で呼び出されようとしたか」を記録しており、期待するものだったかどうかを後でチェックできるようになっています。以下がそのチェック部分です.

        1 * bean.methodOfSomeBean() >> 123

この場合、methodOfSomeBeanが1回、呼ばれるならチェックが成功します。


利点としては、上の場合SomeBean3のメソッドが実装されているかどうか、どのように実装されているか、正しいかバグがあるかと関係なく、シグネチャさえ決まっていれば「SomeBean3を使うコード」の試験ができることです。本来「単体試験」というのはかくあるべきだと思いますね。

注意

上の例では、SomeBean3をMockしてますが、「試験対象クラス」がSomeBean3だという想定ではありません。その意味で、上は、試験対象を試験してないテストという変な例です。「試験対象クラスが使用するクラス」に代役を立てることができるのであって、試験対象クラスそのものに代役をたてたら意味ありません。コラボレータとよばれる、「試験対象クラスの動作に必要なクラス」、脇役とか背景の群集とかに、代役を立てるってことです。

Mockまとめ:

  • Mockは代役であり、本人の代りを務める。
    • なので本番オブジェクトのメソッドができあがってなくても、それを使うオブジェクトの試験ができる。
  • Mockに行なわれたメソッド呼び出しの有無と回数、引数などを記録をしてくれる機能ももっている
    • それを後で確認できる。試験がはかどるわ〜。

Spyについて

これに対して、Spy(もしくはGroovySpy)は、その対象とするオブジェクトが、実際に存在する必要があります。偽物だけではなく、本物インスタンスが存在していることが必要です。

Spyの役割りは、「横からメソッド呼び出しの様子を監視し、その呼び出しメソッドや引数を記録するというものです。加えて、最後にその記録された情報、どんなメソッドがどんな回数呼びだされたかをチェックすることができます。Mockと同じようですが、Spyでは戻り値の「本当の値」でのチェックができます。反面、試験対象オブジェクト以外のコラボレータの振舞いに結果が左右され得ることになるので、単体試験としてはどうなんでしょう、という感じもします。

たとえると、スパイは「善意のマンインザミドル攻撃」のようなものです。呼び出し側と対象オブジェクトの間に挟まった代理人で、あとから整合性を確認するための記録を取ってくれます。

なお、Spyでは、スタビング指定すると、実際の値以外の値を返させ、元メソッドを呼ばない動作をさせることもできます。(メソッド個別にMockのように動作させることもできる)。

Spyまとめ:

  • Spyは、本人とのやりとりを横から監視してやりとり記録する
    • なので本番オブジェクトは必要。本番オブジェクトのメソッドの呼び出しの実際の実行、およびその返り値を使っての試験ができる。
      • 本番オブジェクトのメソッドを呼ばずに、特定の別の値を返すこともできる(スタビング指定する)。
      • 本番オブジェクトのメソッドを呼び、かつ、特定の別の値を返すこともできる(スタビングでクロージャ指定, callRealMethod使用)。
  • 自身に行なわれたメソッド呼び出しの有無と回数、引数などを記録をしてくれる機能ももっている
    • それを後で確認できる。試験がはかどるわ〜。

MockとSpyの違いと使い分け

MockとSpyの実用上の最も大きな違いは、Mockの場合はメソッドの返り値がテキトーなもの、0とかnullとかになるということです。実体がないからしょうがありませんね。試験の都合のために、Mockでそれらしい特定の値が返ってほしいときは>>で返り値を明示指定します(スタビング)。

Spyは実際の値がかえるから、>>をする必要はありません。あえて>>しても良いですけれども。

どちらをどのようなときに使い分ければ良いか、という指針ですが、SpockのドキュメントにはSpyについて「(この機能を使用する前に一度考えなおしてください。もしかすると仕様対象のコード設計自体を見なおしたほうが良いかもしれません。)」とのことです。なので、

「なるべくMockで、それでなんらかの不都合があるならSpyで」

でしょうか。

なお、SpockのMock,Spy,Stubという機能は、世に一般的に言う、モック、スパイ、スタブの説明とは微妙な違いがあるかもしれません。上はあくまでSpockのそれらの説明です。

おまけ: モッキングとスタビング

モッキング

Spockにおいて、モッキングとは、テスト対象クラスに対するMock, Spy, Stubのいずれかで、発生を期待するイベントすなわち「どんな引数でのメソッド呼び出しが何回、どんな順序で発生するか」の期待を記述しておくことを言います。

ジョセフ・ジョースターで言えば、「おまえは次に〜する!」「おまえは次に〜言う!」と予告のことです。テストが成功したら、「ハッやってしまったぁ〜!!」と言わねばなりません。

こういう感じになります(以下、こちらより引用)。(*)のところで「1 * ...」しているところがモッキング。


次の例を見てください。

def "should send messages to all subscribers"() {
    when:
    publisher.send("hello")

    then:
    1 * subscriber.receive("hello") // (*)
    1 * subscriber2.receive("hello") // (*)
}

文章としてコードを読んでみると「publisher が ‘hello’ のメッセージを send したとき、 それぞれの subscriber は 1回 message を receive すべき」になります。

スタビング

スタビングは、メソッド呼び出しの返り値を(上書き)指定します。

こちらからまた引用しますが、以下のコード例の (**)のところで>>指定しているのがスタビングです。


固定の値を返す
すでにここまでの例の中で出てきましたが、固定の値を返すには算術右シフト(>>)演算子を使用します。

subscriber.receive(_) >> "ok"

呼び出し毎に異なる値を返すには、それぞれ個別のインタラクションとして定義します。

subscriber.receive("message1") >> "ok" // (**)
subscriber.receive("message2") >> "fail" // (**)

これは"message1"を受信すると"ok"を返し、"message2"を受信すると"fail" を返します。返す値に制限はありませんが、メソッドの戻り値型の制約を外れることはできません。

スタビングとモッキングは同時に行うことも可能です。

1 * subscriber.receive("message1") >> "ok"

モッキングはMockに、スタビングはStubに、という対応はありません。
ただし、Stubはモッキングができず、スタビングのみが行えます。
つまり以下のとおりです。

スタビング モッキング
Mock
Spy *1
Stub ×

*1:固定値を指定してSpyをスタビングすると元メソッドの呼び出しは抑制される。