Правильный способ принудительно преобразовать Maybe a в a в Elm, явно проваливаясь для Nothings

Хорошо, что я действительно хотел сделать, так это то, что у меня есть массив, и я хочу выбрать из него случайный элемент. Очевидная вещь, которую нужно сделать, это получить целое число из генератора случайных чисел от 0 до длины минус 1, с которым я уже работаю, и затем применить Array.get, но который возвращает Maybe a, (Похоже, есть также функция пакета, которая делает то же самое.) Исходя из Haskell, я получаю значение типа, которое защищает меня от случая, когда мой индекс был вне диапазона, но я контролирую индекс и не ожидаю, что это произойдет, поэтому я просто хотел бы предположить, что я получил Just что-то и несколько принудительно преобразовать в a , В Хаскеле это было бы fromJust или, если я чувствую себя многословно, fromMaybe (error "some message"), Как я должен сделать это в Вязов?

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

Вот несколько довольно неудовлетворительных потенциальных решений, которые я нашел до сих пор:

  • Просто используйте withDefault. У меня есть значение по умолчанию a доступно, но мне это не нравится, так как оно дает совершенно неверный смысл моему коду и, вероятно, усложнит отладку в будущем.
  • Поиграйтесь с портами для взаимодействия с Javascript и получите исключение, если это ничего. Я еще не тщательно исследовал, как это работает, но, видимо, это возможно. Но это, кажется, смешивает слишком много зависимостей для того, что в противном случае было бы простым чистым вязом.

2 ответа

(отвечая на мой вопрос)

Я нашел два более удовлетворительных решения:

  • Сверните мою частично определенную функцию, на которую ссылались в другом месте в связанном обсуждении. Но код выглядит таким образом неполным (я надеюсь, что когда-нибудь компилятор предупредит меня о неполных совпадениях с образцом), и сообщение об ошибке все еще неясно.
  • Сопоставление с образцом и использование Debug.crash, если это ничего. Это похоже на Haskell's error и это решение, к которому я сейчас склоняюсь.

    import Debug
    
    fromJust : Maybe a -> a
    fromJust x = case x of
        Just y -> y
        Nothing -> Debug.crash "error: fromJust Nothing"
    

    (Тем не менее, имя и описание модуля также заставляют меня колебаться, потому что это не похоже на "правильный" метод, предназначенный для моих целей; я хочу указать на истинную ошибку программиста вместо простой отладки.)

Решение

Существование или использование fromJust или эквивалентная функция на самом деле является запахом кода и говорит вам, что API не был разработан правильно. Проблема в том, что вы пытаетесь принять решение о том, что делать, прежде чем у вас есть информация для этого. Вы можете думать об этом в двух случаях:

  1. Если вы знаете, что вы должны делать с Nothing, тогда решение простое: используйте withDefault, Это станет очевидным, если вы посмотрите на правильную точку в вашем коде.

  2. Если вы не знаете, что вы должны делать в том случае, если у вас есть Nothing, но вы все еще хотите внести изменения, тогда вам нужен другой способ сделать это. Вместо того, чтобы вытащить значение из Maybe использование Maybe.map изменить значение, сохраняя при этом Maybe, В качестве примера предположим, что вы делаете следующее:

    foo : Maybe Int -> Int
    foo maybeVal =
      let
        innerVal = fromJust maybeVal
      in
        innerVal + 2
    

    Вместо этого вы захотите это:

    foo : Maybe Int -> Maybe Int
    foo maybeVal =
        Maybe.map (\innerVal -> innerVal + 2) maybeVal
    

    Обратите внимание, что изменение, которое вы хотели, все еще выполняется в этом случае, вы просто не обработали случай, когда у вас есть Nothing, Теперь вы можете передавать это значение вверх и вниз по цепочке вызовов, пока не дойдете до места, где это естественно использовать withDefault избавиться от Maybe,

Случилось так, что мы разделили вопросы: "Как мне изменить это значение" и "Что мне делать, когда его не существует?". Мы имеем дело с первым использованием Maybe.map и последний с Maybe.withDefault,

Предостережение

Есть небольшое количество случаев, когда вы просто знаете, что у вас есть Just значение и необходимость устранить его с помощью fromJust как вы описали, но этих случаев должно быть немного и далеко друг от друга. Есть немало, которые на самом деле имеют более простую альтернативу.

Пример: Попытка отфильтровать список и получить значение.

Допустим, у вас есть список Maybeс, что вы хотите значения. Общая стратегия может быть:

foo : List (Maybe a) -> List a
foo hasAnything =
  let
    onlyHasJustValues = List.filter Maybe.isJust hasAnything
    onlyHasRealValues = List.map fromJust onlyHasJustValues
  in
    onlyHasRealValues

Оказывается, что даже в этом случае существуют чистые способы избежать fromJust, Большинство языков с коллекцией, в которой есть карта и фильтр, имеют метод фильтрации с использованием Maybe встроенный. Haskell имеет Maybe.mapMaybeСкала flatMapи вяза List.filterMap, Это преобразует ваш код в:

foo : List (Maybe a) -> List a
foo hasAnything =
  let
    onlyHasRealValues = List.filterMap (\x -> x) hasAnything
  in
    onlyHasRealValues
Другие вопросы по тегам