This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(()) |
Rangeオブジェクトはカーソルが指すNodeとoffsetを持っています。
getRangeは現在のRangeを返すものとします。
moveはそのRangeにカーソル移動するものとします。
nextNodeとprevNodeはそれぞれ次のNodeと前のNodeを返します。
getRange,nextNode,prevNode,moveは擬似的なものなので、定義は気にしないで下さい。
この4つの関数は参照透過とかそんなわけないのでIOを返します。
これらに対して、4方向にカーソルを動かす関数を定義します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) |
IOはモナドなのでforが使えます。
predとsuccはscalaz.Enumのシンタックスでデクリメントとインクリメントのようなものです。
実際には移動するときに、offsetなどを調べるものですが、OptionT[IO, Unit]などと複雑になるので省略します。
これらの関数を組み合わせてみます。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def movemovemove = moveLeft >> moveRight >> moveUp >> moveDown |
さて、これらの関数、ちょっと冗長ですね。
いちいちmoveを実行しているところもいただけません。
これらをStateを使って書きなおしてみます。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) |
squaredは値をペアにして返します。
同じように、合成したStateとそれを使って移動する関数を定義します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def movemovemove = for { | |
_ <- moveLeft.lift[IO] | |
_ <- moveRight.lift[IO] | |
_ <- moveUp | |
r <- moveDown | |
} yield r | |
def run = getRange >>= movemovemove.eval >>= move |
getRangeとmoveの呼び出しは1回で済むようになりました。
movemovemoveというStateを使い回すことも出来ます。
さらに、Stateモナドであることを活かして、次のようなStateを定義することが出来ます。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def moveDownStart = for { | |
r <- moveDown | |
rr <- List.fill(r.offset)(moveLeft).foldRight(State.init[Range])(_ >> _).lift[IO] | |
} yield rr |
移動した直後の状態を扱えるのがStateモナドの利点ですね。
最近は大学生がなかなか忙しいです。
俺、定期試験が終わったらScalaz勉強会開くんだ・・・・・