Правильный способ принудительно преобразовать 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 не был разработан правильно. Проблема в том, что вы пытаетесь принять решение о том, что делать, прежде чем у вас есть информация для этого. Вы можете думать об этом в двух случаях:
Если вы знаете, что вы должны делать с
Nothing
, тогда решение простое: используйтеwithDefault
, Это станет очевидным, если вы посмотрите на правильную точку в вашем коде.Если вы не знаете, что вы должны делать в том случае, если у вас есть
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