18
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Elm2Advent Calendar 2019

Day 4

ElmのTaskにこんにちは

Posted at

Elmは基本的に作りがシンプルで理解に詰まることが少ない言語ですが、それでもやはりプログラミング言語である以上多少なりとも壁は存在します。そのうちの一つがTaskになります。Taskはいろんな説明ができてしまいますが(非同期を扱うモジュール・および型、Effect Managerを間接的に操作するための機構)、Elmの場合せっかくThe Elm Architectureと言うわかりやすい機構があるので、Elm Runtimeに処理をお願いできるもの。ぐらいの認識で十分です。詳しく原理がわからないと落ち着かんと言う人は、基礎からわかるElmをどうぞ。

TaskはCmdを作るもの

TaskはいろんなことができるのですがElmの素晴らしくシンプルでわかりやすいこととして、どれだけTaskをいじくり回しても必ずCmd msgにしなければなりません。Taskを使う上で避け得られない関数が、performattemptの二つです。TaskはElmランタイムつまりJavaScriptに命令を出しますが、Elmは自分以外を信用しない臆病な生き物なので、それは成功するかもしれないし失敗するかもしれないと思って動きます。そんなことを言っても成功するでしょう、もし仮に失敗したとしても失敗を検知しませんよ。と言うのがperformです。わかりました失敗に備えてエラーのハンドリングもしっかりします。と言うのがattemptになります。いずれもmsgが受け取る値がa型になります。Elmの型システム上、絶対起きえないと言うことでも明記しておかないと怒られてしまうので、そんな時コンパイラを黙らせる型がNever型になります。エラーハンドリングを扱う場合には、Result型を使います。エラーは処理に依存するので固変数xとなっています。

perform : (a -> msg) -> Task Never a -> Cmd msg

attempt : (Result x a -> msg) -> Task x a -> Cmd msg

performでHello World

それでは、TaskでHello Worldをしてみましょう。デモとコードはこちら。これは単なるStringをランタイムから受け取るためのプログラムです。ランタイムから受け取るため基本的に中身はブラックボックスなのですが、固定値であればElm側から指定することができます。必ず成功する固定値を返して欲しい場合には、Task.suceedを利用します。これは誰が見ても必ず成功するだろうと言うことで、performを利用して失敗は無視します。

type alias Model =
    String


init : () -> ( Model, Cmd Msg )
init _ =
    ( ""
    , Task.perform GotGreet <| Task.succeed "Hello World"
    )



-- UPDATE


type Msg
    = GotGreet String


update : Msg -> Model -> ( Model, Cmd Msg )
update msg _ =
    case msg of
        GotGreet greet ->
            ( greet, Cmd.none )

performを使って処理すべきTaskを返すものとしては、Time.nowTime.hereRandom.generateProcess.sleepがあります。sleepはアニメーションなどを扱う場合に重宝するでしょう。

attemptでHello World

続いて、失敗するかもしれないことを考慮するattemptを利用してみます。デモとコードはこちら。succeedを利用している限り必ず成功してしまうため、必ず失敗をするTask.failを使います。もし、起こり得るエラーが複数種類ある場合やエラーであることを明示するためにはカスタムタイプでError型を用意して使うのが好ましいです。

type alias Model =
    { ok : String
    , error : String
    }


type Error
    = SomeError String


init : () -> ( Model, Cmd Msg )
init _ =
    ( { ok = "", error = "" }
    , Task.attempt GotResult <| Task.fail <| SomeError "なんか失敗しました"
    )



-- UPDATE


type Msg
    = GotResult (Result Error String)


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        GotResult result ->
            case result of
                Ok v ->
                    ( { model | ok = v }, Cmd.none )

                Err err ->
                    case err of
                        SomeError ev ->
                            ( { model | error = ev }, Cmd.none )

attemptを使って処理すべきTaskを返すものとしては、Http.get(post, request)・Browser.focusBrowser.blurなどがあります。Httpの場合は、BadRequestやNotFound、InternalServerErrorなどをハンドリングしたり、Browserなどの場合は、要素が見つからなかった(NotFound)時のハンドリングを要求されます。

ElmでHttpをわかってしまおうと言う別途記事でHttpやTaskの応用的な扱い方を紹介しています。良ければご覧ください。

まとめ

本当はTask.mapやandThenも紹介しようと思ったのですが、一番の混乱する理由は最終的にCmdに落とされると言う点とperform, attempt何故2種類あるの?どう扱うの? と言う点で、あとは具体的な関数のリファレンスに使い方が記されているケースがほとんどなので、これ以上深入りはしません。Taskであまり混乱しないようにするポイントはTaskとはなんなのか?と考えすぎない点とTaskの使い方自体はあまり追うのではなく、Taskを返す関数の使い方をしっかり読む、TEAやResult型などの基本をしっかり理解することだと思います。それでは、良いElmライフを!

18
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?