目次
Elm触ってみた
以下は2019年2月に社内勉強会で発表した内容です。本文の記述は最新の情報ではない場合があります。
最新の情報はElm の公式ドキュメントをご覧ください。
発表すること
- Elm触ったらFluxが理解できたよ
発表しないこと
-
関数型言語の一般的な説明
- 「第一級の関数(first class function)」
- 高階関数
- カリー化
- 作用・副作用の話
- などなど
Elmという言語
特徴
- 静的型付けの関数型言語(typed functional language)
- コンパイルしてJSを吐き出す
- SPAが作れる
- The Elm Architecture
- バージョンは0.19
An Introduction to Elm
「Elmで何か作ってみると、Elmの考え方が身につくのでJSやReactがうまく書けるようになるよ」
If you are on the fence, I can safely guarantee that if you give Elm a shot and actually make a project in it, you will end up writing better JavaScript and React code. The ideas transfer pretty easily!
Reduxに影響を与えてる。
チュートリアル
カウンターを作る
import Browser
import Html exposing (Html, button, div, text)
import Html.Events exposing (onClick)
main =
Browser.sandbox { init = init, update = update, view = view }
-- MODEL
type alias Model = Int
init : Model
init =
0
-- UPDATE
type Msg = Increment | Decrement
update : Msg -> Model -> Model
update msg model =
case msg of
Increment ->
model + 1
Decrement ->
model - 1
-- VIEW
view : Model -> Html Msg
view model =
div []
[ button [ onClick Decrement ] [ text "-" ]
, div [] [ text (String.fromInt model) ]
, button [ onClick Increment ] [ text "+" ]
]
https://guide.elm-lang.org/architecture/buttons.html
Elmの構造
The Elm Architectureに基づいてアプリケーションを作る。 MVCではない。
The Elm Architectureでは下記の関数を使用する
- Model
- Update
- View
Model
- アプリケーションの本体(と言ってもいいと思う)
- 型で定義する
- カウンターの場合、ただのint型。
- initで初期化する。ここではModelを数値の0と定義している。
-- MODEL
type alias Model = Int
init : Model
init = 0
(これは下記と同義)
init : Int
init = 0
update関数
- Msg -> Model -> Model
- メッセージと前のモデルを受け取って、新しいモデルを返す
- モデルの更新(操作)を担う
- Modelの変更はここに集約されている
-- UPDATE
-- メッセージを定義
type Msg = Increment | Decrement
update : Msg -> Model -> Model
update msg model =
-- メッセージの内容に応じて、モデルを操作する
case msg of
Increment ->
model + 1
Decrement ->
model - 1
view関数
- Model -> Html Msg
- モデルを受け取って、Htmlを返す
- ただの表示を担う箇所と、Msgが埋め込まれている箇所がある
-- VIEW
view : Model -> Html Msg
view model =
div []
-- ボタンがクリックされた時onClick関数が発火し、update関数にMsgが送られる
[ button [ onClick Decrement ] [ text "-" ]
, div [] [ text (String.fromInt model) ]
, button [ onClick Increment ] [ text "+" ]
]
ElmとRedux
- ElmはReduxの設計思想に影響を与えている
- ReduxはFlux。Elmは The Elm Architecture
- ただ、データの流れは一方向という点で同じ
FluxとThe Elm Architectureの比較
Flux | Elm | 共通点 |
---|---|---|
Action | Msg | イベント |
Dispatcher | Update | 状態の更新 |
Store | Model | 状態の管理 |
View | View | 表示 |
You Might Not Need Redux(Dan Abramov)
以下はReactでReduxなしでFluxを表現したコード
import React, { Component } from 'react';
const counter = (state = { value: 0 }, action) => {
switch (action.type) {
case 'INCREMENT':
return { value: state.value + 1 };
case 'DECREMENT':
return { value: state.value - 1 };
default:
return state;
}
}
class Counter extends Component {
state = counter(undefined, {});
dispatch(action) {
this.setState(prevState => counter(prevState, action));
}
increment = () => {
this.dispatch({ type: 'INCREMENT' });
};
decrement = () => {
this.dispatch({ type: 'DECREMENT' });
};
render() {
return (
<div>
<button onClick={this.increment}>+</button>
{this.state.value}
<button onClick={this.decrement}>-</button>
</div>
)
}
}
https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367
Elmと似てる
イベント
increment = () => {
this.dispatch({ type: 'INCREMENT' });
};
decrement = () => {
this.dispatch({ type: 'DECREMENT' });
};
Msg
type Msg = Increment | Decrement
状態の更新
現在の状態を受け取る。
dispatcher
const counter = (state = { value: 0 }, action) => {
switch (action.type) {
case 'INCREMENT':
return { value: state.value + 1 };
case 'DECREMENT':
return { value: state.value - 1 };
default:
return state;
}
}
dispatch(action) {
this.setState(prevState => counter(prevState, action));
}
update
update : Msg -> Model -> Model
update msg model =
case msg of
Increment ->
model + 1
Decrement ->
model - 1
状態の管理
state = counter(undefined, {});
const counter = (state = { value: 0 }, action) => {
switch (action.type) {
case 'INCREMENT':
return { value: state.value + 1 };
case 'DECREMENT':
return { value: state.value - 1 };
default:
return state;
}
}
type alias Model = Int
init : Model
init = 0
表示
view
render() {
return (
<div>
<button onClick={this.increment}>+</button>
{this.state.value}
<button onClick={this.decrement}>-</button>
</div>
)
}
view : Model -> Html Msg
view model =
div []
[ button [ onClick Decrement ] [ text "-" ]
, div [] [ text (String.fromInt model) ]
, button [ onClick Increment ] [ text "+" ]
]
(注)Elmはコンポーネント指向ではない
- Reactはコンポーネントの組み合わせ
- Elmは1つのアプリケーション
(参考)「Elm のコンポーネント論争とは何か」 http://jinjor-labo.hatenablog.com/entry/2017/05/12/183154
結論
- Elmを学ぶことでFluxが理解できる!
- Reactもうまく書けそう!
- 型でプログラミングする、という感覚がつかめた!
Elm関連サイト
Elm - A delightful language for reliable webapps.
An Introduction to Elm(チュートリアル)