了解Elm中的类型分解

时间:2019-02-05 15:34:58

标签: elm

我试图理解Elm中定义类型和模式匹配的所有不同方式。

在寻找可学习的代码时,我发现了纯Elm中的JSON解码器实现。可以找到代码here和文章系列here

我无法理解字段函数中使用的样式

type Value
    = Jnull
    | Jobject (Dict String Value)

type Decoder a
    = Decoder (Value -> Result String a)

decodeValue : Decoder a -> Value -> Result String a
decodeValue (Decoder attemptToDecode) value =
    attemptToDecode value

field : String -> Decoder a -> Decoder a
field key (Decoder parameterAttempt) =
    let
        decodeKey object =
            Dict.get key object
                |> Result.fromMaybe "couldn't find key"
                |> Result.andThen parameterAttempt

        attemptToDecode value =
            case value of
                Jobject foundIt ->
                    decodeKey foundIt

                _ ->
                    Err "not an object"
    in
        Decoder attemptToDecode

为该函数编写的测试如下:

test "decodes a field" <|
            \_ ->
                Jobject (Dict.singleton "nested" (Jnumber 5.0))
                    |> decodeValue (field "nested" int)
                    |> Expect.equal (Ok 5)

我不了解租借的内容。为什么会有这样的分配?如何评估代码?

  

Dict.get关键对象

处理并“绑定”到?

  

decodeKey对象= ...

     

attemptToDecode值= ...

从根本上讲,我正在尝试了解let中发生的情况,以便它为 Decoder tryToDecode 返回“有用”的内容。另外,有没有更好的方式表达意图?

先谢谢您!

2 个答案:

答案 0 :(得分:2)

我认为@ zh5在以下方面有道理:

  1. 这可能不是您现在要学习的正确示例(无论您要完成什么)
  2. 或者如果是的话,您应该能够理解它而无需问这个问题。

另一方面,即使不是总是很有用,尝试出于好奇也能理解是一件好事,所以我会尝试提供帮助。


这是field(我想)的意图:

  

Decoder中给我“逻辑” ,我可以用它来解码字段   给我一个名字我将从您的Decoder中删除“逻辑” ,   在其上方放置一些“额外逻辑” 以查找   JSON对象中的字段,我将把这个结合起来   新Decoder中的逻辑。

因此,“额外逻辑”在上面的代码中分为两部分:

  1. attemptToDecode捕获用于确保解码的内容是JSON对象的逻辑。此JSON对象的值表示为字典,该字典被提取并传递给第二部分。
    (显然,如果要解码的对象不是对象,则结果应该是错误。)
  2. decodeKey捕获了逻辑的另一半。有了字典形式的JSON对象的内容后,现在我们应该找到该字段,并尝试使用Decoder中提供的“逻辑” 对其进行解码。此逻辑从解码器解构,并在代码中称为parameterAttempt
    (很明显,如果在JSON对象中找不到该字段,则结果应该是错误。)

现在,attemptToDecode引用了decodeKey,然后引用了parameterAttempt(用于对该字段进行解码的原始逻辑),因此我们可以说attemptToDecode 捕获从JSON对象解码字段所需的全部逻辑。因此,此时,所有要做的就是将逻辑包装回Decoder中,这正是代码所说明的:

Decoder attemptToDecode


当然,您肯定是对的,解码器中捕获的逻辑是以函数形式捕获的,当这些函数相互引用时,它们的类型签名必须最后匹配。

答案 1 :(得分:0)

Decoder类型定义为:

        void insert_node(struct node * root, struct node * node, unsigned mask, unsigned path)
        {
            while ((mask >> 1) != 1) {
                root = mask & path? root->right: root->left;
            }
            if (mask & path) {
                assert (root->right == NULL);
                root->right = node;
            } else {
                assert (root->left == NULL);
                root->left = node;
            }
        }

        void fill_level_k(struct node * root, unsigned k)
        {
            unsigned slots = 1 << k;
            for (unsigned slot = 0; slot < slots; slot++) {
                struct node * node = generate_new_node();
                insert_node(root, node, slots, slot);
            }
        }

只有一个数据构造函数带有一个参数。这意味着要构建解码器,我们需要一个函数,该函数需要一个Value并返回结果字符串a:

type Decoder a
    = Decoder (Value -> Result String a)

let 中的分配是构建函数,并用作构建块。读取它们的类型定义可以使事情更清楚。

f: Value -> Result String a -> Decoder a

attemptToDecode 使用 decodeKey 并可以这样做,因为它们都返回

  

结果字符串a

对此表示任何反馈。