Reputation: 13
Is it good practice to use the Maybe monad only when a function can result in an error in order to preserve type safety ?
In my current project I am doing operations on lists, for my get/set functions I have used the Maybe monad in order to prevent out of bound errors :
getCell :: [String] -> Int -> Int -> Maybe [String]
setCell :: [String] -> Int -> Int -> Maybe [String]
This to me, feels like the correct way to use this Monad.
Without going into details about the project, I have another function that manipulates the lists and returns a list if there is a solution.
The question is, should this function always return a [String]
since there is nothing that can create a computation error, or should I use Maybe [String]
to signify that the computation has found no solutions ?
solve :: [String] -> [String]
-- Found -> [String], Not found -> []
solve :: [String] -> Maybe [String]
-- Found -> Just [String], Not found -> Nothing
Upvotes: 1
Views: 243
Reputation: 233150
One of the reasons I like Haskell is that the type system is so expressive. When functions are well-designed, the type of a function takes the place of much (although not all!) documentation.
Often, Maybe
is used to indicate that a function invocation can fail. A typical example is lookup
:
Prelude> :t lookup
lookup :: Eq a => a -> [(a, b)] -> Maybe b
Here, you're trying to find a value in an association list, and it's fairly obvious that the operation could fail if the key isn't in the list. In this case, there's only one thing that can go wrong, so you don't need any other information than the presence or lack of a return value.
Sometimes, however, an operation can fail in more than one way, and you want to give callers a chance to distinguish between various different causes. In that case, Either
is a better option, since you can let the left case carry information about the type of error.
Finally, when returning lists, you can sometimes use the empty list to indicate that no other result could be found.
Wrapping a list in a Maybe
, then, is only useful if the empty list []
is fundamentally different than Nothing
. If I see a function that returns Maybe []
, I know that I, as a caller, will have to deal both with Nothing
, but also with any list value, including the empty list.
Just by looking at the type signatures in the OP, it's not clear to me how Nothing
is different from []
, but this could just be my lack of in-depth knowledge of the particular domain being modelled. It does, however, seem a bit odd.
Upvotes: 1