所用で(?)シンプルな web アプリを作ることになり、Elm で書いてみることにした。
要件としてはルーレットのような感じで文章をシャッフルしてランダムに表示するもの。もう少し作り込む予定だけれどひとまず載せておこう。
とりあえずの感想
- 見よう見まねで書いていると値は immutable ということを忘れがちだった。
- エラーメッセージが親切なのでギリギリやっていけるという感じ。
- バージョンが上がるごとに結構いろいろ API が変わっているようで、ブログ等の情報が参考にならない場合も多い印象。
update
の処理を連鎖させるのはどうしたらいいのか、よくわからなかった。- 結局
Task.perform
で繋ぐ感じでやっているが正しいのか不明。
- 結局
- 公式のフォーマッター
elm-format
があるので助かる。- でもインデントがずれていると動かなかったりするので Go の
gofmt
と比べると少々頼りない気がする。
- でもインデントがずれていると動かなかったりするので Go の
もし、JavaScript で書いたとしたら、オンラインで公開されている参考にできるサンプルの数が多いので、おそらく半分くらいの時間でできたのではないかと思う。
まぁこうやって新しい概念に触れて、チャレンジしてみるのも大事だからね。
コード
Elm はシンタックスハイライトに対応していないか、そうか。
(190404 追記)
言語指定を Haskell にすることでそれっぽい感じでハイライトされるようになった。
module Main exposing (Model, Msg(..), init, main, subscriptions, update, view) import Array import Browser import Html exposing (..) import Html.Events exposing (..) import Random import Task import Time -- MAIN main = Browser.element { init = init , update = update , subscriptions = subscriptions , view = view } -- MODEL type alias Model = { sentences : List String , index : Int , sentence : String , shuffling : Bool , count : Int } init : () -> ( Model, Cmd Msg ) init _ = ( { sentences = sampleSentences , index = 0 , sentence = "" , shuffling = False , count = 0 } , Cmd.none ) -- https://www.thetoptens.com/random-sentences/ sampleSentences : List String sampleSentences = [ "I am so blue I'm greener than purple." , "I stepped on a Corn Flake, now I'm a Cereal Killer." , "Everyday a grape licks a friendly cow." , "Llamas eat sexy paper clips." , "Banana error." , "Don't touch my crayons, they can smell glue." , "There's a purple mushroom in my backyard, screaming Taco's!" ] maxCount : Int maxCount = 20 -- UPDATE type Msg = Pick | GetIndex Int | Tick Time.Posix | CheckCount Time.Posix | Shuffle Time.Posix update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case msg of Pick -> ( { model | shuffling = True } , Random.generate GetIndex (randomIndex model.sentences) ) GetIndex i -> let s = pickByIndex model.sentences i in ( { model | index = i, sentence = s } , Cmd.none ) Tick newTime -> let c = if model.shuffling then model.count + 1 else model.count in ( { model | count = c } , Task.perform CheckCount Time.now ) CheckCount newTime -> ( resetStatus model , Task.perform Shuffle Time.now ) Shuffle newTime -> ( shuffleSentence model , Cmd.none ) randomIndex : List String -> Random.Generator Int randomIndex sentences = Random.int 0 <| List.length sentences - 1 pickByIndex : List String -> Int -> String pickByIndex sentences index = let default = "this is default sentence." arr = Array.fromList sentences in Maybe.withDefault default (Array.get index arr) resetStatus : Model -> Model resetStatus model = if model.count == maxCount then let s = pickByIndex model.sentences model.index in { model | shuffling = False, count = 0, sentence = s } else model shuffleSentence : Model -> Model shuffleSentence model = let randomId = modBy (List.length model.sentences) model.count in let randomSentence = pickByIndex model.sentences randomId in if model.shuffling then { model | sentence = randomSentence } else model -- SUBSCRIPTIONS subscriptions : Model -> Sub Msg subscriptions model = Time.every 40 Tick -- VIEW showBool : Bool -> String showBool bool = if bool then "True" else "False" view : Model -> Html Msg view model = div [] [ h1 [] [ text model.sentence ] , h2 [] [ text (String.fromInt model.count) ] , h2 [] [ text (showBool model.shuffling) ] , button [ onClick Pick ] [ text "Pick" ] ]
Ellie というオンライン開発環境(いわゆる playground 的なやつ)にも保存してみたけれど、表示されないかもしれない。