Elm
関数型プログラミング
React
ElmDay 10

Elmに入門してみて思ったこと

関数型の文法でWebフロントエンドができるよーというのを聞いたので入門してみました。やってみてはまったところとか、今の自分の理解とか、作ってみたものについて書こうかと思います。HaskellとかSMLは少し読めるけどWebフロントはReactぐらいしかやってないので、そんな目線からの感想です。


はまったところ


環境構築


インストール

npm i -g elm

としたけど権限がないとかで入らない。

npm i -gでパッケージが入る場所はnpm get prefixで確認でき、例えば/usr/localだったりする。

この場合普通はsudoをつければ大丈夫だが、elmはパッケージをダウンロードしたあとinstall.jsを実行するのでこれにまで権限が伝わらず失敗する模様。

解決策

1. yarnを使う

yarn global add elm


  1. prefixを設定する

npm set prefix ~/.local

prefixはnpm i -gしたときパッケージが入る場所。上を実行すると~/.npmrcが作られる。

echo prefix=.local >> ~/.npmrc

でもいい。

ちなみにnpm i -D elmでプロジェクト毎に入れてもいいけど、npm scriptsで呼んだときオプションがうまくパースされなかったりしたのでやめた。一応vscode-elmのREADMEでは-Dでいれた場合についても書いてあるのでうまくやれば大丈夫なのかも。

あと書いてて思ったが、suなら入ったのかもしれない。


VSCodeやAtomでelm-formatが動かない

.bashrcとかでパスを通すとこうなる。

パスは.profileとかに書くようにしよう。

ちなみに自分はVSCodeで書いてますが、プラグインが強くて快適です。


文法


number、comparable

Elmでは型の名前は大文字で始まるのが普通だがこれらは小文字で始まる。

文法紹介でもnumberはInt型でもありFloat型でもあるようなことが書かれている。

comparableもcompare関数のドキュメント


compare : comparable -> comparable -> Order

Compare any two comparable values. Comparable values include String, Char, Int, Float, Time, or a list or tuple containing comparable values.


とあって、String型などはcomparable型に含まれるようなことが書かれている。

これはSMLでいう等値型(''a)みたいなやつで、Haskellでいう(Num a) => a(Ord a) => aなど制限のついた多相型という認識でよさそう。

Elmには型クラスがないのでこういう仕組みが組み込みで用意されている。


type aliasのレコードに対する挙動

type aliasは型に別名をつけるものだが


type alias Model =
{ name : String
, password : String
}

model : Model
model =
Model "" ""

というように値コンストラクタも導入される場合がある。

これはrecord constructorというやつでtype aliasのガイドのNoteに書いてある。

Elmでは状態をレコードで書くことが多いため便利。


The Elm Architecture

Elmの根幹っぽいやつ、下で書きます。


Elmのアーキテクチャ

これは今も理解が微妙な部分だけど、とりあえず自分はReact1を関数型っぽく書けるようにしたのがElmだと感じた。

Elmは関数型言語だと言われるが、Reactのコンポーネントのようにライフサイクルを持っている。

Reactでは状態をもたないコンポーネントは、引数によってのみ返すDOM要素が決まる純粋な関数として表現できたが、

function Welcome(props) {

return <h1>Hello, {props.name}</h1>;
}

逆にコンポーネントが状態を持つ場合はライフサイクルが必要で、状態の変化に合わせてランタイムが決まった関数を呼び出していた。

これはElmも同じで、ロジックの部分は普通に関数型っぽく書けて、表示するところにだけライフサイクルがついてきている。

そのライフサイクルで状態を扱う部分も、実際に状態を更新するのはElmのランタイムがやることで、プログラマーが触れないようにすることで副作用を分離している。

ライフサイクルと関数型の組み合わせは一見邪悪なものに感じるかもしれないが、宣言的に書けるし外部とのやりとりをランタイムに任せるならこうなるのかなと思った。

そういうわけでElmというとupdateとかModelとかMessageとか考えないといけない気持ちになるが、それは外部とのやりとりをするところだけであってReactと似てるし、ロジックの部分は普通に関数型っぽく書いていけばいいのかなと思った。


作ったもの

最後にQiitaとかのMarkdownを書くとプレビューを表示してくれるのを作ってみたので紹介します。

CSSもう少し書けるようになりたい。


module Main exposing (..)

import Html exposing (Html, Attribute, beginnerProgram, div, textarea)
import Html.Attributes exposing (placeholder, style)
import Html.Events exposing (onInput)
import Markdown exposing (toHtml)

main : Program Never String Msg
main =
beginnerProgram { model = "", view = view, update = update }

-- UPDATE

type Msg
= NewContent String

update : Msg -> String -> String
update (NewContent content) _ =
content

-- VIEW

view : String -> Html Msg
view content =
div []
[ textarea [ placeholder "Text to Markdown", onInput NewContent, myStyle ] []
, div [] [ toHtml [] content ]
]

myStyle : Attribute msg
myStyle =
style
[ ( "width", "100%" )
, ( "height", "50vh" )
, ( "resize", "none" )
]


追記

もう少しちゃんと書いたデモを上げました

https://ahuglajbclajep.github.io/elm-sandbox/markdown-preview/


まとめ

The Elm ArchitectureとかElmが汎用言語でないがゆえにElmの考えを読み取らないといけない部分が難しかった。

でも使えると面白そうなのでこれからもちょいちょいやっていければなと思いました!





  1. これはReduxが正しいらしい。Reduxやったことないのでわからないけど。