ElmのStreamライブラリで画面を作るなら

# ElmのStreamライブラリを試した

Elm Advent Calender 19日目です。

Elmのアドカレは活発ですね！

去年のelm Advent Calenderではlm-native-uiでiosアプリ作ってました -> [記事](https://qiita.com/4245Ryomt/items/0309c749024fda628417)

## stream
- [github](https://github.com/naddeoa/stream)
- [Elm package](http://package.elm-lang.org/packages/naddeoa/stream/latest)
- elmにはlazy listを扱う標準ライブラリがないから作ったぜ
- 他にも似たような機能を実現するライブラリはあるけどstack over flowに対して脆弱だ
- stack over flowしない lazy listライブラリだぜ
- ほかにもStreamライブラリはたくさんありますが、これのみ試します

## どんな時に使えるだろうか？
- ドキュメントにもある通り無限に流れるデータに対してどうこうする時につかえそうだ
- elmがクライアントアプリケーションを作るのに使われるものだったら無限スクロールに表示されるようなデータだろうか？ タイムライン表示のようなリアルタイムでバシバシデータが流れて来るもの？

## 簡単な使い方
- 一つの要素なStream

```elm
singleHoge : Stream String
singleHoge = Stream.singleton "hoge"
```

- リストから生成されるStream

```elm
streamFromList : Stream String
streamFromList = Stream.fromList ["hogo", "huga", "hugi"]
```

- Stream同士を結合

```elm
concatStream : Stream String
concatStream = Stream.concat singleHoge streamFromList
```

- Streamの先頭から要素を1つと要素を抜いたStreamを取る

```elm
elementAndTail : (String, Stream String)
elementAndTail = Stream.next concatStream
-- -> ("hoge", ["hogo", "huga", "hugi"])
```

- StreamをListに変換

```elm
listFromStream : List String
listFromStream = Stream.toList concatStream
-- -> ["hoge", "hogo", "huga", "hugi"]
```

- その他
 - map, filter, reduce, zip... Listにある関数と同じようなものたち


## タイムライン的なものを作ってみて検証
- サーバサイドから無限にデータが流れて来る(websocketで流しまくるよ)
- 表示するのは最新の10件

### Listで作って見る
```elm

type alias Feed = {
 id: Int,
 subject: String,
 text: String
}

type alias Model = {
 feeds: List Feed
}

type Msg =
 ReceiveFeed Feed

update msg model =
	case msg of
		ReceiveFeed feed ->
			( { feeds = feed :: model.feeds }, Cmd.none )


feedView feed = div [] [ text feed.subject ]

feedList = List.map feedView >> List.take 10

view model = div [] (model.feeds |> feedList)

port receiveFeedPort : (Feed -> msg) -> Sub msg

init = ({ feeds = [] }, Cmd.none)

main = 
    Html.program
        { view = view
        , init = init
        , update = update
        , subscriptions = always <| receiveFeedPort ReceiveFeed
        }

```

### Streamで作って見る(変更箇所のみ記載)

```elm
type alias Model = {
 feeds: Stream Feed
}

update msg model =
	case msg of
		ReceiveFeed feed ->
			( { feeds = Stream.concat (Stream.singleton feed) model.feeds }, Cmd.none )

init = ({ feeds = Stream.fromList [] }, Cmd.none)


feedList = Stream.map feedView >> Stream.nextN 10 >> Tuple.second

```

## 動かしてみた

↓こんな感じ

<blockquote class="twitter-video" data-lang="ja"><p lang="und" dir="ltr"><a href="https://t.co/f5pC44UHfb">pic.twitter.com/f5pC44UHfb</a></p>&mdash; ス (@4245Ryomt) <a href="https://twitter.com/4245Ryomt/status/942967694881562625?ref_src=twsrc%5Etfw">2017年12月19日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


### List version
- 300000レコード程度流し込むと画面の更新がガクガクしだす
- Perfomanceツールから状況を見るとheapをものすごい勢いで消費している(消費600MBまでアガる)
- その影響かminor gcが頻発し時間が取られて画面がガクガクになっているようだ(?)
- <img width="1132" alt="スクリーンショット 2017-12-19 13.06.32.png" src="https://qiita-image-store.s3.amazonaws.com/0/153296/99abded6-637d-d609-cc94-c7c22c058c99.png">


### Stream version
- 300000レコード流しこんでも描画がガクガクになっていない
- heapの消費を見ても100MB程度で収まっている
- minor gc に時間がとられていないようだ
- <img width="1124" alt="スクリーンショット 2017-12-19 13.05.45.png" src="https://qiita-image-store.s3.amazonaws.com/0/153296/258e8361-8c09-9a46-f06c-08d0af4be51e.png">

グラフ見てて動き全然ちげえな。。。

## 感想
- 適当なサンプルでの比較で、これでいいのか‥？感あるがStreamを使ってメリットを感じることができた。
- なぜStreamだとheapを消費しないのか?
	- mapとかfilterとか読んでもすぐ新しいインスタンスにならないから？Array無駄に生成されないから？
- どうしてこのStreamだとイイ感じなのか、説明のつくように精進いたします。