Elm:如何使用单个 TypeConstructor 对联合类型进行 Json 解码?

Jig*_*gar 3 json decode elm type-constructor union-types

如以下代码所示,解码 UserAlias 很容易,但是当我尝试解码 UserType 时,即 Replace D.map2 UserAliaswith D.map2 UserTypeCompiler 大声喊叫。如何修复此编译器错误?

import Json.Decode as D
import Html exposing (..)
import Result as R

type  UserType = UserType {name:String, age:Int}

type alias UserAlias = {name:String, age:Int}


userDecoder = D.map2 UserAlias
                (D.field "name" D.string)
                (D.field "age" D.int)


decodeUser json = D.decodeString userDecoder json 


json = """
  { "name": "Bob", "age": 40 }
"""


main = div [] [(text << toString << decodeUser) json]
Run Code Online (Sandbox Code Playgroud)

上面的代码工作正常。现在替换D.map2 UserAlias为D.map2 UserType

编译器哭了

Detected errors in 1 module.
==================================== ERRORS ====================================



-- TYPE MISMATCH ---------------------------------------------------------------

The 2nd argument to function `map2` is causing a mismatch.

13|               D.map2 UserType
14|>                (D.field "name" D.string)
15|                 (D.field "age" D.int)

Function `map2` is expecting the 2nd argument to be:

    D.Decoder { age : Int, name : String }

But it is:

    D.Decoder String

Hint: I always figure out the type of arguments from left to right. If an
argument is acceptable when I check it, I assume it is "correct" in subsequent
checks. So the problem may actually be in how previous arguments interact with
the 2nd.



-- TYPE MISMATCH ---------------------------------------------------------------

The 1st argument to function `map2` is causing a mismatch.

13|>              D.map2 UserType
14|                 (D.field "name" D.string)
15|                 (D.field "age" D.int)

Function `map2` is expecting the 1st argument to be:

    { age : Int, name : String } -> b -> UserType

But it is:

    { age : Int, name : String } -> UserType

Hint: It looks like a function needs 1 more argument.
Run Code Online (Sandbox Code Playgroud)

我该如何解决这个错误,请帮忙!

基本上我不再希望使用类型别名,而只希望使用 UserType 以便我可以隐藏内部记录结构,并在不破坏公共 API 的情况下对其进行重构。

谢谢。

Cha*_*ert 5

该UserType构造函数采用的一个参数UserAlias,所以我们可以简单地使用Json.Decode.map你现有的解码器中取UserAlias价值,构建一个解码器UserType是这样的:

userDecoder : D.Decoder UserType
userDecoder = D.map2 UserAlias
                (D.field "name" D.string)
                (D.field "age" D.int)
                |> D.map UserType
Run Code Online (Sandbox Code Playgroud)


Mic*_*mas 5

比乍得的答案更直接的方法是在map2函数中执行此操作:

userDecoder : D.Decoder UserType
userDecoder = D.map2 (\name age -> UserType <| UserAlias name age)
                (D.field "name" D.string)
                (D.field "age" D.int)
Run Code Online (Sandbox Code Playgroud)

这将避免第二个中的模式匹配map。不过,就性能而言,它可能不会给您带来太多好处!

如果你想更简洁一点,这也可以用pointfree的风格来写:

userDecoder = D.map2 (((<<) UserType ) << UserAlias)
                (D.field "name" D.string)
                (D.field "age" D.int)
Run Code Online (Sandbox Code Playgroud)