Elm 'Json.Decode.succeed':如果它应该始终 return 相同的值,它如何在解码管道中使用?
Elm 'Json.Decode.succeed': how is it used in a decode pipeline if it is supposed to always return the same value?
我正在学习 Elm,令我困惑的一件事是 'Json.Decode.succeed'。根据docs
succeed : a -> Decoder a
Ignore the JSON and produce a certain Elm value.
decodeString (succeed 42) "true" == Ok 42
decodeString (succeed 42) "[1,2,3]" == Ok 42
decodeString (succeed 42) "hello" == Err ...
我明白了(虽然,作为初学者,我还没有看到它的用途)。但是这种方法也用于解码管道,因此:
somethingDecoder : Maybe Wookie -> Decoder Something
somethingDecoder maybeWookie =
Json.Decode.succeed Something
|> required "caterpillar" Caterpillar.decoder
|> required "author" (Author.decoder maybeWookie)
这是怎么回事?也就是说,如果 'succeed' 忽略传递给它的 JSON,它如何用于读取 JSON 并将其转换为 Elm 值?任何线索表示赞赏!
刚开始,解码器管道的直觉是它就像柯里化函数,其中管道 required
和 optional
逐个应用参数。期望一切,包括函数、它的参数 和 以及 return 值都包含在 Decoder
中。
举个例子:
succeed Something
|> required (succeed 42)
|> required (succeed "foo")
等同于
succeed (Something 42 "foo")
和
decodeString (succeed (Something 42 "foo")) whatever
将 return Ok (Something 42 "foo")
只要 whatever
有效 JSON.
当一切都成功时,这只是一个非常复杂的函数调用。解码器更有趣的方面,也是我们首先使用它们的原因,在于错误路径。但由于 'succeed' 是这里感兴趣的内容,我们将忽略它并节省大量时间、文本和脑细胞。只要知道,如果不考虑错误路径,这一切都会显得很做作。
无论如何,让我们尝试重新创建它,看看它是如何工作的。
解码.map2
除了管道运算符之外,管道的关键是 Decode.map2
函数。如果您尝试在不使用管道的情况下编写 JSON 解码器,那么您可能已经使用过它或它的兄弟。我们可以像这样使用 map2
来实现上面的示例:
map2 Something
(succeed 42)
(succeed "foo")
这与上面的示例完全一样。但是,从用户 POV 来看,这个问题是,如果我们需要添加另一个参数,我们还必须将 map2
更改为 map3
。而且 Something
没有包含在解码器中,这很无聊。
调用包装在解码器中的函数
无论如何这是有用的原因是因为它使我们能够同时访问多个值,并且能够以我们想要的任何方式组合它们。我们可以使用它来调用 Decoder
中的函数,并在 Decoder
中使用参数,并将结果也包装在 Decoder
:
中
map2 (\f x -> f x)
(succeed String.fromInt)
(succeed 42)
柯里化和部分应用
不幸的是,如果我们需要更多参数,这仍然存在需要更改 map
函数的问题。如果只有一种方法可以一次将一个参数应用于一个函数……就像我们有柯里化和部分应用一样。既然我们现在有办法调用包装在解码器中的函数,那么如果我们 return 一个部分应用的函数而不是稍后应用剩余的参数呢?
map2 (\f x -> f x)
(succeed Something)
(succeed 42)
将 return 变成 Decoder (string -> Something)
,所以现在我们只需冲洗并重复这个和最后一个参数:
map2 (\f x -> f x)
(map2 (\f x -> f x)
(succeed Something)
(succeed 42))
(succeed "")
Et voila,我们现在已经重新创建了 JSON 解码管道!虽然表面上看可能不像。
Ceci n'est pas une pipe
最后一个技巧是将 map2
与管道运算符一起使用。管道本质上定义为 \x f -> f x
。看看这与我们一直在使用的函数有多相似?唯一的区别是参数被交换了,所以我们也需要交换我们传递参数的顺序:
map2 (|>)
(succeed "")
(map2 (|>)
(succeed 42)
(succeed Something))
然后我们可以再次使用管道运算符来达到最终形式
succeed Something
|> map2 (|>)
(succeed 42)
|> map2 (|>)
(succeed "")
现在应该很明显 required
只是 map2 (|>)
的别名。
仅此而已!
我正在学习 Elm,令我困惑的一件事是 'Json.Decode.succeed'。根据docs
succeed : a -> Decoder a
Ignore the JSON and produce a certain Elm value.
decodeString (succeed 42) "true" == Ok 42
decodeString (succeed 42) "[1,2,3]" == Ok 42
decodeString (succeed 42) "hello" == Err ...
我明白了(虽然,作为初学者,我还没有看到它的用途)。但是这种方法也用于解码管道,因此:
somethingDecoder : Maybe Wookie -> Decoder Something
somethingDecoder maybeWookie =
Json.Decode.succeed Something
|> required "caterpillar" Caterpillar.decoder
|> required "author" (Author.decoder maybeWookie)
这是怎么回事?也就是说,如果 'succeed' 忽略传递给它的 JSON,它如何用于读取 JSON 并将其转换为 Elm 值?任何线索表示赞赏!
刚开始,解码器管道的直觉是它就像柯里化函数,其中管道 required
和 optional
逐个应用参数。期望一切,包括函数、它的参数 和 以及 return 值都包含在 Decoder
中。
举个例子:
succeed Something
|> required (succeed 42)
|> required (succeed "foo")
等同于
succeed (Something 42 "foo")
和
decodeString (succeed (Something 42 "foo")) whatever
将 return Ok (Something 42 "foo")
只要 whatever
有效 JSON.
当一切都成功时,这只是一个非常复杂的函数调用。解码器更有趣的方面,也是我们首先使用它们的原因,在于错误路径。但由于 'succeed' 是这里感兴趣的内容,我们将忽略它并节省大量时间、文本和脑细胞。只要知道,如果不考虑错误路径,这一切都会显得很做作。
无论如何,让我们尝试重新创建它,看看它是如何工作的。
解码.map2
除了管道运算符之外,管道的关键是 Decode.map2
函数。如果您尝试在不使用管道的情况下编写 JSON 解码器,那么您可能已经使用过它或它的兄弟。我们可以像这样使用 map2
来实现上面的示例:
map2 Something
(succeed 42)
(succeed "foo")
这与上面的示例完全一样。但是,从用户 POV 来看,这个问题是,如果我们需要添加另一个参数,我们还必须将 map2
更改为 map3
。而且 Something
没有包含在解码器中,这很无聊。
调用包装在解码器中的函数
无论如何这是有用的原因是因为它使我们能够同时访问多个值,并且能够以我们想要的任何方式组合它们。我们可以使用它来调用 Decoder
中的函数,并在 Decoder
中使用参数,并将结果也包装在 Decoder
:
map2 (\f x -> f x)
(succeed String.fromInt)
(succeed 42)
柯里化和部分应用
不幸的是,如果我们需要更多参数,这仍然存在需要更改 map
函数的问题。如果只有一种方法可以一次将一个参数应用于一个函数……就像我们有柯里化和部分应用一样。既然我们现在有办法调用包装在解码器中的函数,那么如果我们 return 一个部分应用的函数而不是稍后应用剩余的参数呢?
map2 (\f x -> f x)
(succeed Something)
(succeed 42)
将 return 变成 Decoder (string -> Something)
,所以现在我们只需冲洗并重复这个和最后一个参数:
map2 (\f x -> f x)
(map2 (\f x -> f x)
(succeed Something)
(succeed 42))
(succeed "")
Et voila,我们现在已经重新创建了 JSON 解码管道!虽然表面上看可能不像。
Ceci n'est pas une pipe
最后一个技巧是将 map2
与管道运算符一起使用。管道本质上定义为 \x f -> f x
。看看这与我们一直在使用的函数有多相似?唯一的区别是参数被交换了,所以我们也需要交换我们传递参数的顺序:
map2 (|>)
(succeed "")
(map2 (|>)
(succeed 42)
(succeed Something))
然后我们可以再次使用管道运算符来达到最终形式
succeed Something
|> map2 (|>)
(succeed 42)
|> map2 (|>)
(succeed "")
现在应该很明显 required
只是 map2 (|>)
的别名。
仅此而已!