Help us understand the problem. What is going on with this article?

Elmで冗長な書き方をしていたデコーダなどをリファクタリングしたメモ

More than 1 year has passed since last update.

概要

Google Spread Sheetで複数列を取得してElmのモデルに入れたメモ
で問題としていた、以下のことについてツイッターでアドバイスをいただいたので反映してみる。

  • ~でなければのときの書き方が冗長になっている。

この時点のソース

アドバイス

リファクタリングしたソース

Card.elm

  • Tagを別のファイルに
  • CardIdを別ファイルにして定義
Models/Card.elm
@@ -1,7 +1,6 @@
 port module Models.Card exposing
     ( CardData
     , CardLabelData
-    , Tag
     , cardDataListDecodeFromJson
     , cardDecodeFromString
     , cardDecoder
@@ -9,10 +8,6 @@ port module Models.Card exposing
     , illustedBy
     , initCard
     , skillCard
-    , tag
-    , tagDecoder
-    , tagParser
-    , tagsDecoder
     )

 import Browser.Dom as Dom
@@ -26,6 +21,8 @@ import Http
 import Json.Decode as D exposing (..)
 import Json.Decode.Pipeline exposing (custom, hardcoded, optional, required)
 import Json.Encode as E
+import Models.CardId as CardId exposing (CardId, decoder, fromString, toString)
+import Models.Tag exposing (Tag, tagsDecoder)
 import Session
 import Skeleton exposing (viewLink, viewMain)
 import Task exposing (..)
@@ -49,7 +46,7 @@ type alias CardLabelData =


 type alias CardData =
-    { cardId : String
+    { cardId : Maybe CardId
     , cardName : String
     , cardType : String
     , kind : String
@@ -73,14 +70,8 @@ type alias CardData =
     }


-type alias Tag =
-    { name : String
-    , level : Int
-    }
-
-
 initCard =
-    CardData "" "" "" "" 0 "" 0 0 0 "" 0 "" "" [] "" "" "" "" "" "" 0
+    CardData (CardId.fromString "") "" "" "" 0 "" 0 0 0 "" 0 "" "" [] "" "" "" "" "" "" 0



@@ -92,7 +83,7 @@ initCard =
 skillCard =
     let
         cardData =
-            CardData "B-001" "走る" "能力" "基本能力" 10 "アクション" 4 0 0 "自身" 1 "移動1" "逃げてもいいし、向かってもいい。\n君たちは何処にだっていける。\n一歩ずつではあるけれど。" [ Tag "移動" 0, Tag "基本能力" 0 ] "/assets/images/card/main/run.png" "ヒューマンピクトグラム2.0" "http://pictogram2.com/" "/assets/images/card/frame/report.gif" "" "https://google.com" 0
+            CardData (CardId.fromString "B-001") "走る" "能力" "基本能力" 10 "アクション" 4 0 0 "自身" 1 "移動1" "逃げてもいいし、向かってもいい。\n君たちは何処にだっていける。\n一歩ずつではあるけれど。" [ Tag "移動" 0, Tag "基本能力" 0 ] "/assets/images/card/main/run.png" "ヒューマンピクトグラム2.0" "http://pictogram2.com/" "/assets/images/card/frame/report.gif" "" "https://google.com" 0
     in
     cardView cardData

@@ -123,6 +114,14 @@ cardView cardData =

             else
                 elm
+
+        cardId =
+            case cardData.cardId of
+                Just id ->
+                    CardId.toString id
+
+                Nothing ->
+                    ""
     in
     div [ class "skill-card" ]
         [ div [ class "wrapper" ]
@@ -153,7 +152,7 @@ cardView cardData =
                     , div [ class "description" ] [ text (labelData.description ++ cardData.description) ]
                     ]
                 , div [ class "bottomContent" ]
-                    [ div [ class "cardId" ] [ text cardData.cardId ]
+                    [ div [ class "cardId" ] [ text cardId ]
                     , illustedBy cardData
                     ]
                 ]
@@ -226,7 +225,7 @@ cardDecodeFromString s =
 cardDecoder : Decoder CardData
 cardDecoder =
     D.succeed CardData
-        |> Json.Decode.Pipeline.custom (D.index 0 D.string)
+        |> Json.Decode.Pipeline.custom (D.index 0 CardId.decoder)
         |> Json.Decode.Pipeline.custom (D.index 1 D.string)
         |> Json.Decode.Pipeline.custom (D.index 2 D.string)
         |> Json.Decode.Pipeline.custom (D.index 3 D.string)
@@ -247,57 +246,3 @@ cardDecoder =
         |> Json.Decode.Pipeline.custom (D.index 18 D.string)
         |> Json.Decode.Pipeline.custom (D.index 19 D.string)
         |> Json.Decode.Pipeline.custom (D.index 20 GSAPI.decoderIntFromString)
-
-
-tagsDecoder : Decoder (List Tag)
-tagsDecoder =
-    D.map tagsParser string
-
-
-tagsParser : String -> List Tag
-tagsParser s =
-    let
-        list =
-            String.split "," s
-    in
-    List.map (\str -> tagParser str) list
-
-
-tagDecoder : Decoder Tag
-tagDecoder =
-    D.map tagParser string
-
-
-tagParser : String -> Tag
-tagParser s =
-    let
-        list =
-            String.split ":" s
-
-        name =
-            case List.head list of
-                Just a ->
-                    a
-
-                _ ->
-                    s
-
-        value =
-            case List.tail list of
-                Just t ->
-                    case List.head t of
-                        Just a ->
-                            case String.toInt a of
-                                Just n ->
-                                    n
-
-                                _ ->
-                                    0
-
-                        _ ->
-                            0
-
-                _ ->
-                    0
-    in
-    Tag name value

Tags.elm

アドバイスでいただいたソースをそのまま適用したもの。
0のときにネストしていた部分が横並びになって、可読性が増している。

Models/Tags.elm
module Models.Tag exposing (Tag, tagsDecoder)

import Json.Decode as D exposing (Decoder)


type alias Tag =
    { name : String
    , level : Int
    }


fromString : String -> Maybe Tag
fromString str =
    case String.split ":" str of
        [ "" ] ->
            Nothing

        [] ->
            Nothing

        name :: level :: _ ->
            Just <| Tag name (String.toInt level |> Maybe.withDefault 0)

        name :: _ ->
            Just <| Tag name 0


tagsDecoder : Decoder (List Tag)
tagsDecoder =
    D.map
        (\str ->
            String.split "," str
                |> List.filterMap fromString
        )
        D.string

CardId.elm

値が制約を満たしていることを型で保証するを参考に、CardIdを作る。

Models/CardId.elm
module Models.CardId exposing (CardId, decoder, fromString, toString)

import Json.Decode as D exposing (Decoder)


type CardId
    = CardId String


{-| `CardId` 型の値を作成する唯一の方法

    fromString ""
    --> Nothing

    fromString "B-001"
    --> Just (CardId "B-001")

-}
fromString : String -> Maybe CardId
fromString s =
    if String.length s /= 0 then
        Just (CardId s)

    else
        Nothing


{-| `CardId` 型の値を文字列に変換する唯一の方法
-}
toString : CardId -> String
toString (CardId s) =
    s


decoder : Decoder (Maybe CardId)
decoder =
    D.map fromString D.string

2019/4/30 追加修正

コメントで指摘いただいたので修正

  • CardId.fromStringのような形で呼び出す場合にはexposing不要
  • 不要なexposingは、例えばfromStringを使った場合に、何のfromStringか分からなくなるので混乱のもと
Models/Card.elm
-import Models.CardId as CardId exposing (CardId, decoder, fromString, toString)
+import Models.CardId as CardId exposing (CardId)

参考

値が制約を満たしていることを型で保証する

hibohiboo
Javascriptが好きです。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away