5 ms·
Interesting article. Note that the article describes the maybe monad as used in Haskell and explains what it solves and then comes with this criticism: "...we
by TacticalCoder 12y ago
Interesting article. Note that the article describes the maybe monad as used in Haskell and explains what it solves and then comes with this criticism:
"...we still need to run the program to observe that the computation fails somewhere and then figure out what happened"
As if figuring out where a problem happened was an issue in Haskell (or, really, in any language supporting something like the maybe monad).
If figuring out what happened when something failed is complicated because there were several maybe monads and in the end you got nothing, then Haskell solves this very nicely if I'm not mistaken: there's "Either", which can carry an error message along with the fact that there's no result.
Now I'm not disputing that a proof that something cannot happen would be great (and hence in this case not requiring Maybe / Either): all I'm saying is that people shouldn't misread that thinking that Haskell has no clean way of reporting why nothing got returned.
- seanmcdirmid 12y ago> If figuring out what happened when something failed is complicated because there were several maybe monads and in the end you got nothing, then Haskell solves this very nicely if I'm not mistaken: there's "Either", which can carry an error message along with the fact that there's no result. Does that really work? Say you have an error in a long pipeline, wouldn't "Either" cause processing down the line to treat the error as valid data (the error wouldn't be None)? What you really want is a maybe monad where you can propagate wither a Valid answer or an Error condition, where operations on the value merely propagate the error condition. Also, it would be nice if the monad was integrated into a debugger so the error condition could carry along enough context to allow the user to jump to execution context where the error occurred during debugging (if we are talking about programmatic errors, a distinction must be made between errors of programming vs. exceptions from the environment that can/should be handled).
- chriswarbo 12y agoIf the pipeline is constructed with fmap, <*> or >>= then the pipeline's constituents will only be applied to Right values. An error (a Left value) will short-circuit the pipeline and be returned as-is. Alternatively, we could be more fancy and branch such that errors received their own, separate, processing (eg. logging).
- seanmcdirmid 12y agoCool. One of the advantages of null pointer exceptions is that you can break into the debugger when they happen. Masking them and continuing is absolutely what you don't want to do.
- deleted 12y ago[deleted]
- tel 12y agoEither tends to be used for error conditions that arise from the outside—after all, you are (a) explicitly denoting them and (b) presumably handling them. Programmer errors tend to arise in invariant violations. Sometimes these are marked with `error "cannot occur"` or silly things like that. Dropping into a debugger here would be tremendous.
- chriswarbo 12y agoThe point is that in Haskell, once you start chaining Maybes together you are forced to deal with the Nothings, even if they won't actually arise. For example: :t Data.Map.lookup 123 (singleton 123 "hello") Data.Map.lookup 123 (singleton 123 "hello") :: Maybe [Char] Even though this lookup will clearly succeed, I am forced to handle the Nothing case. Dependent types let me encode more information about the value in my types, providing evidence that it will always be a Just, which I can use at compile time to convince the type checker that the Nothing case is unreachable. As soon as the Nothing case is unreachable, I can easily unwrap the Maybe, since my only remaining branch is a "Just x", so I can simply return "x".
- codygman 12y agofmap is very useful for dealing with Maybes also: Prelude M> import qualified Data.Map as M Prelude M> let val = M.lookup 123 (M.singleton 123 "hello") Prelude M> fmap reverse val Just "olleh" So this means that you only deal with the chained maybes at the top level or in a call-site that needs to know a Nothing existed as the Nothings are propagated.
- chriswarbo 12y agoYou can't use fmap to "break out" of a Functor, since its return type is still wrapped in the functor: fmap : (a -> b) -> Maybe a -> Maybe b Likewise we can't escape from an Applicative: (<*>) : Maybe (a -> b) -> Maybe a -> Maybe b Nor from a Monad: (>>=) : Maybe a -> (a -> Maybe b) -> Maybe b Dependently typed languages use these all the time, but they also allow you to write a function: unMaybe : (A : Type) -> Maybe A -> Type unMaybe _ Nothing = _|_ -- Empty type; impossible to satisfy unMaybe A (Just _) = A -- The raw element type And use it like this: unWrap : (x : Maybe a) -> unMaybe a x unWrap Nothing = ...insert proof that you'll never get a Nothing here... unWrap (Just x) = x I haven't included a proof, since it depends completely on the application, but it's easy to understand how it works. In the "Just" case, we need to return a value of type "a", which is trivial. In the "Nothing" case, we need to return a value of type "_|_", but we no that there aren't any! The only way we can satisfy the type checker is to return a value of type _|_, and the only way we can do that is if we already have such a value. In other words, to get an impossible value we must be in an impossible branch. Hence we can prove by contradiction that the "Nothing" will never be taken!
- andrewflnr 12y agoI don't think that's what the author meant in that passage. They're not bemoaning error reporting, they're complaining about the fact that you have to run the code to find out if there was a problem, rather than finding out at compile time. Having to track down the source of the runtime error is a minor injury in comparison.