Okay, what I really wanted to do is, I have an Array and I want to choose a random element from it. The obvious thing to do is get an integer from a random number generator between 0 and the length minus 1, which I have working already, and then applying Array.get, but that returns a Maybe a
. (It appears there's also a package function that does the same thing.) Coming from Haskell, I get the type significance that it's protecting me from the case where my index was out of range, but I have control over the index and don't expect that to happen, so I'd just like to assume I got a Just
something and somewhat forcibly convert to a
. In Haskell this would be fromJust
or, if I was feeling verbose, fromMaybe (error "some message")
. How should I do this in Elm?
I found a discussion on the mailing list that seems to be discussing this, but it's been a while and I don't see the function I want in the standard library where the discussion suggests it would be.
Here are some pretty unsatisfying potential solutions I found so far:
a
available, but I don't like this as it gives the completely wrong meaning to my code and will probably make debugging harder down the road.The existence or use of a fromJust
or equivalent function is actually code smell and tells you that the API has not been designed correctly. The problem is that you're attempting to make a decision on what to do before you have the information to do it. You can think of this in two cases:
If you know what you're supposed to do with Nothing
, then the solution is simple: use withDefault
. This will become obvious when you're looking at the right point in your code.
If you don't know what you're supposed to do in the case where you have Nothing
, but you still want to make a change, then you need a different way of doing so. Instead of pulling the value out of the Maybe
use Maybe.map
to change the value while keeping the Maybe
. As an example, let's say you're doing the following:
foo : Maybe Int -> Int
foo maybeVal =
let
innerVal = fromJust maybeVal
in
innerVal + 2
Instead, you'll want this:
foo : Maybe Int -> Maybe Int
foo maybeVal =
Maybe.map (\innerVal -> innerVal + 2) maybeVal
Notice that the change you wanted is still done in this case, you've simply not handled the case where you have a Nothing
. You can now pass this value up and down the call chain until you've hit a place where it's natural to use withDefault
to get rid of the Maybe
.
What's happened is that we've separated the concerns of "How do I change this value" and "What do I do when it doesn't exist?". We deal with the former using Maybe.map
and the latter with Maybe.withDefault
.
There are a small number of cases where you simply know that you have a Just
value and need to eliminate it using fromJust
as you described, but those cases should be few and far between. There's quite a few that actually have a simpler alternative.
Let's say you have a list of Maybe
s that you want the values of. A common strategy might be:
foo : List (Maybe a) -> List a
foo hasAnything =
let
onlyHasJustValues = List.filter Maybe.isJust hasAnything
onlyHasRealValues = List.map fromJust onlyHasJustValues
in
onlyHasRealValues
Turns out that even in this case, there are clean ways to avoid fromJust
. Most languages with a collection that has a map and a filter have a method to filter using a Maybe
built in. Haskell has Maybe.mapMaybe
, Scala has flatMap
, and Elm has List.filterMap
. This transforms your code into:
foo : List (Maybe a) -> List a
foo hasAnything =
let
onlyHasRealValues = List.filterMap (\x -> x) hasAnything
in
onlyHasRealValues
(answering my own question)
I found two more-satisfying solutions:
Pattern-match and use Debug.crash if it's a Nothing. This appears similar to Haskell's error
and is the solution I'm leaning towards right now.
import Debug
fromJust : Maybe a -> a
fromJust x = case x of
Just y -> y
Nothing -> Debug.crash "error: fromJust Nothing"
(Still, the module name and description also make me hesitate because it doesn't seem like the "right" method intended for my purposes; I want to indicate true programmer error instead of mere debugging.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With