FRP
Elm

Signalのカタログ+Signalについて

More than 3 years have passed since last update.

Signalのカタログ

http://giisyu.github.io/elm-signalCatalog/
Signalの性質がひと目で分かる、かもしれないSignalのデモを作った。
Signalによっては画面をクリックしたり、キーボードを押したり、押しっぱなしにする必要があります。
ソースコード

Signalがどんな挙動をするのか、関数を通すとSignalの型がどうなるのか、どのSignalが優先されるのかとか、カタログで確認してみてください。

表示の見方

1
map : (a -> result) -> Signal a -> Signal result
                () ------------------------()----------- Mouse.clicks
                4                3                        2
  1. 関数の型
  2. Signal a になるコード
  3. タイムライン、右から左に時間が流れている。シグナルが出る(発火する)と、その時の型が出る。(少しズレます。)
  4. Signal を Element.show した値です。つまりSignalの現在の型です。さらに言い換えれば画面に表示した時(View関数に持ってきた時のタイミングで)こんな感じになるということです。ブラウザをリロードすると、初期値がわかります。

作った感想

  • elm-signal-extra最強、オススメは、Signal.ExtraのswitchWhenswitchSample。TrueとFalseで、二つのSignalの切り替えができる。クリック時に発火するのがsample。しないのがwhen。
  • カタログ作ってよくわかったのが、limitRatedropWithin。クリックしまくると違いがわかる。

  • カタログのソースコードはMVUみたいな形ではない。(推奨的なソースコードではない)ExtraにあるmapManyという関数を使って、Signalを即Signal Elementにして並べてる。(Signalを作ったらすぐ試せるのが理想でそうしたんだけど、できてるかよくわからない。)

  • Elmコンパイラ、文字列型に日本語使ってもbyteエラーするようになって日本語が使えない。。issue出してくれてる人もいるんですけど、開発者の手が空いてないのか、コメントもついてなく、という状況なので誰か直してくれーお願いします。。直ってるぞー

  • ソースコードを文字列にする方法ないかな。というかもっと自動化出来そうだけどまったく思いつかない。

  • 表示がズレるのは、直してみてもあんまり面白く無かった。なのでこれでいきます。

  • Signal面白い。勉強になったし解説含めて結構おもいっきり書いたので楽しかった。

以下、書こうと思った関数の解説と、ElmのSignalについてわかったこと。

ElmとSignal

Signalを使うと、複雑なユーザーイベントを組み合わせで記述できたり、アプリケーション全体の構造化ができる(FRP)。
ElmはFRPを第一級に、GUI記述に使うことにしたというのが新しい言語だ。

Signal

Signal にはグラフの性質と、発火する型みたいな性質がある。
グラフ:回路をつなぐような感覚でSignalをつなぐ。出来たSignalグラフは増えたり減ったりせずずっと変わらない静的。決まってる。
発火する型:カタログの表示の4のイメージ。。型が決まっている、途切れない、常に何かの「値」がある感じ。

他の性質に、Signalには最初から値がセットされている。初期値がある。あとロード時に発火するSignalは無い。ロード時は初期値が表示される。

map : (a -> result) -> Signal a -> Signal result
mapはSignalの値を関数で変化させる関数。() から True に変えている。
Mouse.click と map always True したSignalは同じタイミングになっている。

map2 : (a -> b -> result) -> Signal a -> Signal b -> Signal result
繋がったSignalのうち1つが発火すると、繋がった全てのSignalが現在の型でmapする。

foldp : (a -> state -> state) -> state -> Signal a -> Signal state
Signalの状態を司る関数。関数は1つ前の状態を取って新しい状態を返す。あと、二引数目が以降のSignalの初期値になる。ロード時に発火しない。

merge : Signal a -> Signal a -> Signal a
Signalを合わせる。同時に発火すると一引数目のSignalが優先される。
違う型Aと型BのSignalを合わせると、A | B という型になる。同じ型同士だとその型になる。

filter : (a -> Bool) -> a -> Signal a -> Signal a
Trueになったのが通る

dropRepeats : Signal a -> Signal a
状態の変わってない同じSignalを除外する。つまり変化したら通る。

sampleOn : Signal a -> Signal b -> Signal b
一引数目のSignalが発火すると、二引数目の関数が発火する。
現在の型表示部分を見るとわかるが、二引数目のSignalの型になっている。

Time

時間が関係するSignalがある。

fps : number -> Signal Time
とりあえずコレに合わせてゲーム作る

fpsWhen : number -> Signal Bool -> Signal Time
Trueの間、fps numberする。アニメーションとかに使う。

every

timestamp

delay : Time -> Signal a -> Signal a
指定時間分Signalをずらす。

since : Time -> Signal a -> Signal Bool
二引数目のシグナルの発火があると、Trueを出して、指定時間の空きが来るとFalseを出す。空き時間の計測は空きが来ないまでリセットされる。

elm-signal-extra

signalの拡張ライブラリ。Signalをタイミング、初期値、状態+型から見た関数がある。
Discreteにはタイミングを構築、表現する関数があって、その成果がTimeに。
Extraには、状態を細かくいじる関数があって、その成果のsample,when系の関数がある。あとSignalをタプル、リストで扱う関数がある。
Streamは初期値がNothingのSignalがある。

Signal.Discrete

Signal は、型とタイミングの二つの面から作る必要がある。
Signal.Discrete はEventSource(== Signal ())になる関数を使ってタイミングを記述できる。
EventSouce型からは、foldeかmapで最後に型を付ければいいと思われる。

whenChangeTo: a -> Signal a -> EventSource
Signal が a になったタイミング

folde : (b -> b) -> b -> EventSource -> Signal b
EventSourceのタイミングで、状態のbを関数で変換して 、Signal b になる。

Signal.Extra

まだよくわかってない、のも多いんだけど、Signalをタプルで扱うもの、状態に関するもの、Signalを切り替えたりするもの、SignalをListで扱うもの、何かがある。

zip : Signal a -> Signal b -> Signal (a,b)

runBuffer : Int -> Signal a -> Signal (List a)
指定した数だけ、バッファをリストで取る。

switchWhen : Signal Bool -> Signal a -> Signal a -> Signal a
switchSample : Signal Bool -> Signal a -> Signal a -> Signal a
Signal Bool でSignalを切り替える。
whenとついている関数は、切替時に発火しない。sampleとついている関数は発火する。

combine : List (Signal a) -> Signal (List a)
mapMany : (List a -> b) -> List (Signal a) -> Signal b
combine はListのSignalをSignal (List)にする。mapManyはそれに関数を掛ける。
今回カタログ作るのに、一番活躍した。mapMany (flow down) list.. として、Signal Elmentをリストにしてから並べた。

Signal.Stream

初期値がないSignal。アプリの状態モデルなんかを表現するSignalに対する、キーボードなんかのイベント系のSignal。になるらしい。わかったと思ってたんだけどまだわかってないです。。Taskと便利そうだなとか思う。

Signal.Time

使うと便利そうなタイミングを作る関数がある。

limitRate : Time -> Signal a -> Signal a
最初のSignalから指定時間Signalを受け付けない。

dropWithin : Time -> Signal a -> Signal a
最初のSignalから指定時間Signalを受け付けない。指定時間は最後のシグナルごとから測定される。
クリック連打すると、limitRateとの違いがわかる。

settledAfter : Time -> Signal a -> Signal a
最後のSignalから指定時間後に発火。入力しながら検索したりする検索窓みたいな動作作れる。

TaskとSignal

0.15からTaskが導入された。Taskとの組み合わせもある。
カタログのは並行計算を行っている(たぶん)。

逆引き(まだ検証してないのもある)

値をスイッチしたい

Signal.Discrete.whenChangeTo + Signal.Discrete.folde または
Signal.Extra.keepThen または
Signal.Extra.switchWhen(切り替わり時、発火しない)
 Signal.Extra.switchSample(切り替わり時、発火する)

ゲームを作りたいときに、初期発火値にランダムな値(現在時間)が欲しい。

  1. port のインバウンド機能
Elm.fullscreen(Elm.App,{time:Date.now()})
port time : Float
state = {seed = time}
  1. 参考 evancz/task-tutorial(最新commitバグが直ってます(まだ反映はされてない))js側では、現在時間返す関数をTask型を使ってラップして、elm側では、初期値NotingのSignalにportからTaskを送り発火。初期値を得る。

Signalのグラフをループさせたい

mailbox + Task + port

Signalまとめ

まだよくわからないw。
SignalとTaskを使えるようになって、、、この計算の後にこの計算、それしてる間にこの計算する、この時間アニメーション、その間クリック不可、いやクリックするとこの表示、この表示の前にこの計算、こういう動作、ああいう動作、さらにテスト、みたいな状況に、華麗に変幻自在、バグも少なく書ける。みたいになれるといいなぁ。。