如何通过Elm端口传递联合类型?

时间:2021-12-21 19:01:49

I've bumped into an issue while trying out Elm. I want to pass a union type through a port but I get this error:

我在尝试榆树时碰到了一个问题。我想通过端口传递一个union类型,但是我得到了这个错误:

Port `setStorage` is trying to communicate an unsupported type.

34| port setStorage : Model -> Cmd msg
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The specific unsupported type is:

    Todo.Importance

The types of values that can flow through in and out of Elm include:

    Ints, Floats, Bools, Strings, Maybes, Lists, Arrays,
    Tuples, Json.Values, and concrete records.

I've modified the Todo example as follows:

我修改了Todo示例如下:

type alias Task =
    { description : String
    , completed : Bool
    , editing : Bool
    , id : Int
    , importance : Importance
    }

type Importance
  = Normal
  | High
  | Low

This issue appears to be quite old. One commenter suggests to "pass Json.Values through ports and Json.Decode/Encode them" but how exactly to do that? The documentation appears a bit unclear and lacks full examples. Any help appreciated.

这个问题似乎很老了。一位评论者建议“通过端口传递Json.Values和Json.Decode / Encode”但是如何做到这一点?文档似乎有点不清楚,缺乏完整的例子。任何帮助赞赏。

2 个解决方案

#1


7  

I've made it work with Json.Decoder/Encoder. Wasn't that difficult after all although having to serialize every single field just to pass that one union type is quite a burden.

我已经使它与Json.Decoder / Encoder一起工作了。毕竟不是那么困难,虽然必须序列化每一个字段只是为了通过一个联合类型是一个相当大的负担。

Decoders:

解码器:

modelDecoder : Json.Decoder Model
modelDecoder =
  Json.object4 Model
    ("tasks" := Json.list taskDecoder)
    ("field" := Json.string)
    ("uid" := Json.int)
    ("visibility" := Json.string)

taskDecoder : Json.Decoder Task
taskDecoder =
  Json.object5 Task
    ("description" := Json.string)
    ("completed" := Json.bool)
    ("editing" := Json.bool)
    ("id" := Json.int)
    ("importance" := Json.string `andThen` importanceDecoder)

importanceDecoder : String -> Json.Decoder Importance
importanceDecoder tag =
  case tag of
    "Normal" -> Json.succeed Normal
    "High" -> Json.succeed High
    "Low" -> Json.succeed Low
    _ -> Json.fail (tag ++ " is not a recognized tag for Importance")

And encoders:

和编码器:

modelToValue : Model -> Json.Encode.Value
modelToValue model =
  Json.Encode.object
    [
      ("tasks", Json.Encode.list (List.map taskToValue model.tasks)),
      ("field", Json.Encode.string model.field),
      ("uid", Json.Encode.int model.uid),
      ("visibility", Json.Encode.string model.visibility)
    ]

taskToValue : Task -> Json.Encode.Value
taskToValue task =
  Json.Encode.object
    [
      ("description", Json.Encode.string task.description),
      ("completed", Json.Encode.bool task.completed),
      ("editing", Json.Encode.bool task.editing),
      ("id", Json.Encode.int task.id),
      ("importance", importanceToValue task.importance)
    ]

importanceToValue : Importance -> Json.Encode.Value
importanceToValue importance =
  case importance of
    Normal -> Json.Encode.string "Normal"
    High -> Json.Encode.string "High"
    Low -> Json.Encode.string "Low"

#2


3  

You can't pass a union type above all because JS does not know about such a thing. So you might as well pass a string and do a case statement in javascript - I do it all the time.

你不能首先传递一个联合类型,因为JS不知道这样的事情。所以你不妨传递一个字符串并在javascript中做一个case语句 - 我会一直这样做。

#1


7  

I've made it work with Json.Decoder/Encoder. Wasn't that difficult after all although having to serialize every single field just to pass that one union type is quite a burden.

我已经使它与Json.Decoder / Encoder一起工作了。毕竟不是那么困难,虽然必须序列化每一个字段只是为了通过一个联合类型是一个相当大的负担。

Decoders:

解码器:

modelDecoder : Json.Decoder Model
modelDecoder =
  Json.object4 Model
    ("tasks" := Json.list taskDecoder)
    ("field" := Json.string)
    ("uid" := Json.int)
    ("visibility" := Json.string)

taskDecoder : Json.Decoder Task
taskDecoder =
  Json.object5 Task
    ("description" := Json.string)
    ("completed" := Json.bool)
    ("editing" := Json.bool)
    ("id" := Json.int)
    ("importance" := Json.string `andThen` importanceDecoder)

importanceDecoder : String -> Json.Decoder Importance
importanceDecoder tag =
  case tag of
    "Normal" -> Json.succeed Normal
    "High" -> Json.succeed High
    "Low" -> Json.succeed Low
    _ -> Json.fail (tag ++ " is not a recognized tag for Importance")

And encoders:

和编码器:

modelToValue : Model -> Json.Encode.Value
modelToValue model =
  Json.Encode.object
    [
      ("tasks", Json.Encode.list (List.map taskToValue model.tasks)),
      ("field", Json.Encode.string model.field),
      ("uid", Json.Encode.int model.uid),
      ("visibility", Json.Encode.string model.visibility)
    ]

taskToValue : Task -> Json.Encode.Value
taskToValue task =
  Json.Encode.object
    [
      ("description", Json.Encode.string task.description),
      ("completed", Json.Encode.bool task.completed),
      ("editing", Json.Encode.bool task.editing),
      ("id", Json.Encode.int task.id),
      ("importance", importanceToValue task.importance)
    ]

importanceToValue : Importance -> Json.Encode.Value
importanceToValue importance =
  case importance of
    Normal -> Json.Encode.string "Normal"
    High -> Json.Encode.string "High"
    Low -> Json.Encode.string "Low"

#2


3  

You can't pass a union type above all because JS does not know about such a thing. So you might as well pass a string and do a case statement in javascript - I do it all the time.

你不能首先传递一个联合类型,因为JS不知道这样的事情。所以你不妨传递一个字符串并在javascript中做一个case语句 - 我会一直这样做。