Понимание типа разрушения в Elm - PullRequest
0 голосов
/ 05 февраля 2019

Я пытаюсь понять все возможные способы определения типов и сопоставления с шаблоном в Elm.

При поиске кода для изучения я нашел реализацию на чистом Elm JSON-декодера.Код можно найти здесь и серии статей здесь .

Я не могу понять стиль , используемый в поле function:

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)

Я не понимаю тело let.Почему существует такое назначение и как оценивается код?Как

Ключевой объект Dict.get

обрабатывается и «привязывается» к?

decodeKey object = ...

значение tryToDecode = ...

По сути, я пытаюсь понять, что происходит в let, так что она возвращает что-то "полезное" для Decoder tryToDecode .Кроме того, есть ли лучший способ выразить то, что предназначено?

Заранее спасибо!

Ответы [ 2 ]

0 голосов
/ 07 февраля 2019

Я думаю, что @ 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

И, конечно, вы правы в своем собственном ответе, что логика, захваченная в декодерах, записывается в виде функций, и когда эти функции ссылаются друг на друга, их сигнатуры типов должны совпадать в конце.

0 голосов
/ 06 февраля 2019

Тип декодера, определенный как:

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

, имеет один конструктор данных, принимающий один аргумент.Это означает, что для построения декодера нам нужна функция, которая принимает значение и возвращает строку результата a:

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

Назначения внутри let являются строительными функциями и используютсякак строительные блоки.Чтение их с определениями их типов делает вещи более понятными.

let
    decodeKey: Dict String Value -> Result String a
    decodeKey object =
        case Dict.get key object of
            Just value ->
                parameterAttempt value

            Nothing ->
                Err "couldn't find key"

    attemptToDecode: Value -> Result String a
    attemptToDecode value =
        case value of
            Jobject foundIt ->
                decodeKey foundIt

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

tryToDecode использует decodeKey и может сделать это, потому что оба возвращают

Result String a

Любая обратная связь по этому вопросу очень ценится.

...