In Haskell, find is designed to evaluate as a Maybe, because when scanning a list for an element matching a predicate, it could fail. However, consider the following:
factorize n | isPrime n = [n]
| otherwise = m : factorize (n `div` m)
where m = find ((==0) . (n `mod`)) [2..]
find here will clearly find a value in every case, assuming we’re only hitting integers greater than 2. If a number is not prime, then it has a factor somewhere in the infinite list [2..]. But of course, Haskell doesn’t know that, and will still complain that a Maybe is being used. What is the solution here?
- Use
fromMaybewith some dummy default value to extract the inner value. The default value will never be used. - Use something like
head . dropWhileto act as a kind of “unsafe find”. - Bubble the Maybes all the way up and have the factors returned as a list of Maybes.
- Some way to tell Haskell “look, the math checks out, this is safe, just trust me.”
1 and 2 are smelly, 3 is cumbersome and adds failure state to a function it shouldn’t exist in, and I don’t know whether 4 exists. What is the best solution here?
Edit: The above list of options is incomplete without mentioning fromJust, which takes a Just and returns the value inside it, and throws an error when given a Nothing. I think that's clearly the short answer to my question, and I wasn't aware of its existence until I read the answers and comments here. However, there are still good points made about safety and fail state in general in the answers below.
Solution 1 would work, but is unidiomatic.
Solution 2/4 would be to use
fromJustor pattern matching:The comment is kinda important to point out to the reader that this is safe and will never cause an exception. There's no way to tell Haskell though, it will still generate code to throw an exception should a
Nothingoccur.You'll need to prove its correctness yourself. You may notice that it's actually wrong - try with
factorize 1!Don't duplicate the work in the first place.
isPrimewill also try to find a factor, then returnFalseif it found one and returnTrueif it didn't. Instead of trying to find the factor again in the case whereisPrime nreturnedFalse, use the factor from the search you just did!or alternatively