I understand (I think) that there is a close relationship between Either and Except in Haskell, and that it is easy to convert from one to the other. But I'm a bit confused about best practices for handling errors in Haskell and under what circumstances and scenarios I would choose one over the other. For example, in the example provided in Control.Monad.Except, Either is used in the definition
type LengthMonad = Either LengthError
so that calculateLength "abc" is
Right 3
If instead one were to define
type LengthMonad = Except LengthError
then calculateLength "abc" would be
ExceptT (Identity (Right 3))
I'm confused about what purpose this would serve and when one one want it. Why does everything returned from calculateLength always have Identity; why not just SomeExceptionType (Right 3) or even SomeSuccessType 3?
I'm a Haskell beginner when it comes to concepts like this, so a concrete example of when I'd want the latter over the former would be much appreciated, especially why it's so (apparently to me) complex. For example, what can a caller of a function that uses the Except version of calculateLength do, that they can't (or at least can't as easily) do with the Either version?
Abstract
Use Either for normal success/error APIs. It's defined in the base library, so it doesn't push other dependencies on a consumer. Also, this is one of the most basic Haskell types, so 'everyone' understands how it works.
Only use ExceptT if you specifically need to combine Either with another monad (such as, for example IO). This type is defined in the transformers library, so pushes an extra dependency on consumers. Additionally, monad transformers is a more advanced feature of Haskell, so you can't expect everyone to understand how to use it.
Speculation on reasons
I wasn't around when those decisions were made, but it seems that there are various historical reasons for the confusion. Haskell is an old language (older than Java!), so even though efforts have been made to streamline it and rectify old mistakes, some still remain. As far as I can tell, the Either/ExceptT confusion is one of those situations.
I'm speculating that Either is older than the concept of monad transformers, so I imagine that the type Either was introduced to the base library early in the history of Haskell.
The same thing seems to be the case with Maybe.
Other monads, likes e.g. Reader and State seem to have been introduced (or at least 'retconned') together with their monad transformers. For example, Reader is just a special case of ReaderT, where the 'other' Monad is Identity:
type Reader r = ReaderT r Identity
The same goes for StateT:
type State s = StateT s Identity
That's the general pattern for many of the monads defined in the transformers library. ExceptT just follows the pattern by defining Except as the special case of ExceptT.
There are exceptions to that pattern. For example, MaybeT doesn't define Maybe as a special case. Again, I believe that this is for historical reasons; Maybe was probably around long before anyone started work on the transformers library.
The story about Either seems even more convoluted. As far as I can tell, there was, originally, an EitherT monad transformer, but apparently (I forget the details) there was something wrong with the way that it behaved (it probably broke some laws), so it was replaced with another transformer called ErrorT, which again turned out to be wrong. Third time's the charm, I suppose, so ExceptT was introduced.
The Control.Monad.Trans.Except module follows the pattern of most other monad transformers by defining the 'uneffectful' special case using a type alias:
type Except e = ExceptT e Identity
I suppose it does that because it can, but it may be unfortunate, because it's confusing. There's definitely prior art that suggests that a monad transformer doesn't have to follow that pattern (e.g. MaybeT), so I think it would have been better if the module hadn't done that, but it does, and that's where we are.
I would essentially ignore the Except type and use Either instead, but use ExceptT if a transformer is required.
Related
I am reading "Programming in Haskell" book and trying to correlate ideas of haskell to my knowledge in C#. Please correct me if I am wrong.
I feel like monads enforces the programmer to write code which can handle exceptions. So we will explicitly mention the error handling in the type system like
Optional(Int) functionName(Int a, Int b)
The return type is Optional(Int) but not an Int, So who so ever uses the library that has this kind of return types will understand that error handling is happening and the result will be like None(explains Something went wrong) and Some(explains we got some result).
Any code can result to Happy Path(where we get some results) and Sad Path(where errors occurs). Making this paths explicitly in Type system is what monad is. That's my understanding of it. Please correct me.
Monads are like a bridge between Pure Functional Programming and Impure code (that results in side effects).
Apart from this I want to make sure my understanding on Exception handling (VS) Option Types.
Exception Handling tries to do the operations without having a deeper look on to the inputs. Exception Handling's are Heavy since the Call stack has to unwind till it get to the Catch || Rescue || Handling Code.
And the Functional way of dealing things is check the inputs before doing the operations and return the "None" as result if the inputs does't match the required criteria. Option types are light weight of handling errors.
Monad is just an interface (in Haskell terms, typeclass) that a type can implement, along with a contract specifying some restrictions on how the interface should behave.
It's not that different from how a C# type T can implement, say, the IComparable<T> interface. However, the Monad interface is quite abstract and the functions can do surprisingly different things for different types (but always respecting the same laws, and the same "flavor" of composition).
Instead of seeing Monad as a functional way of error handling, it's better to go the other way: inventing a type like Optional that represents errors / absence of values, and start devising useful functions on that type. For example, a function that produces an "inhabited" Optional from an existing value, a function that composes two Optional-returning functions to minimize repetitive code, a function that changes the value inside the Optional if it exists, and so on. All of them functions that can be useful on their own.
After we have the type and and a bevy of useful functions, we might ask ourselves:
Does the type itself fit the requirements for Monad? It must have a type parameter, for example.
Do some (not necessarily all) of the useful functions we have discovered for the type fit the Monad interface? Not only they must fit the signatures, they must fit the contract.
In the affirmative case, good news! We can define a Monad instance for the type, and now we are able to use a whole lot of monad-generic functions for free!
But even if the Monad typeclass doesn't exist in our language, we can keep in mind that the type and some of the functions defined on it behave like a Monad. From the documentation of the thenCompose method of the CompletableFuture Java class:
This method is analogous to Optional.flatMap and Stream.flatMap.
This allows us to "transfer intuitions" between seemingly unrelated classes, even if we can't write monad-generic code because the shared interface doesn't exist.
Monads aren't 'just a functional way of error handling', so you are, indeed, wrong.
It would be pointless to turn this answer into a monad tutorial, so I'm not going to try. It takes some time to understand what a monad is, and the best advice I can give is to keep working with the concept until it clicks. Eventually it will.
The type described in the OP looks equivalent (isomorphic) to Haskell's more standard Maybe type, which is indeed a monad. In a pinch, it can be used for error handling, but more often you'd use another monad called Either (or types isomorphic to it), since it's better suited for that task.
A monad, though, can be many other things. Lists are monads, as are functions themselves (via the Reader monad). Trees are monads as well. These have nothing to do with error handling.
When it comes to exceptions in Haskell, I take the view that it's a legacy feature. I'd never design my Haskell code around exceptions, since exceptions aren't visible via the type system. When a function may fail to return a result, I'd let it return a Maybe, an Either, or another type isomorphic to that. This will, indeed, force the caller to handle not only the happy path, but also any failures that may occur.
Is Monad just a functional way of Error handling?
No it is not. The most prominent use for monads is handling side-effecting computations in Haskell. They can also be used for error handling, but the reason they are called "monads" and not "error handlers" is that they provide a common abstraction around several seemingly different things. For instance, in Haskell, join is a synonym for concat and =<< is an infix synonym for concatMap.
Monads are like a bridge between Pure Functional Programming and Impure code (that results in side effects).
This is a subtle point. In a lazy language such as Haskell, side effects refer to computations which may indeed be pure, such as FFI calls that require memory to be freed upon completion. Monads provide a way to track (and perform) side effects. "Pure" simply means "function returns the same value when called with the same value".
AFAIK one of the new additions to GHC8 is the ApplicativeDo language extension, which desugars the do-notation to the corresponding Applicative methods (<$>, <*>) if possible. I have the following questions.
How does it decide whether the desugaring to Applicative methods is possible? From what I know, it does a dependency check (if the later depends on the result of former) to decide the eligibility. Are there any other criteria?
Although this addition makes applicative code easier to read for classes that don't have any Monad instance (maybe ?). But for structures that both have a Monad and an Applicative instance: Is this a recommended practice (from the readability perspective)? Are there any other benefits?
How does it decide whether the desugaring to Applicative methods is possible? From what I know, it does a dependency check (if the later depends on the result of former) to decide the eligibility. Are there any other criteria?
The paper and trac page are the best sources of information. Some points:
ApplicativeDo uses applicatives wherever possible - including mixing them with >>= in some cases where only part of the do block is applicative
A sort of directed graph of dependencies is constructed to see which parts can be "parallelized"
Sometimes there is no obvious best translation of this graph! In this case, GHC chooses the translation that is (roughly speaking) locally smallest
Although this addition makes applicative code easier to read for classes that don't have any Monad instance (maybe ?). But for structures that both have a Monad and an Applicative instance: Is this a recommended practice (from the readability perspective)? Are there any other benefits?
Here is an answer about the difference between Applicative and Monad. To quote directly:
To deploy <*>, you choose two computations, one of a function, the other of an argument, then their values are combined by application. To deploy >>=, you choose one computation, and you explain how you will make use of its resulting values to choose the next computation. It is the difference between "batch mode" and "interactive" operation.
A prime example of this sort of thing is the Haxl monad (designed by Facebook) which is all about fetching data from some external source. Using Applicative, these requests can happen in parallel while Monad forces the requests to be sequential. In fact, this example is what motivated Simon Marlow at Facebook to make the ApplicativeDo extension in the first place and write the quoted paper about it.
In general, most Monad instances don't necessarily benefit from Applicative. From the same answer I quoted above:
I appreciate that ApplicativeDo is a great way to make more applicative (and in some cases that means faster) programs that were written in monadic style that you haven't the time to refactor. But otherwise, I'd argue applicative-when-you-can-but-monadic-when-you-must is also the better way to see what's going on.
So: use Applicative over Monad when possible, and exploit ApplicativeDo when it really is nicer to write (like it must have been in some cases at Facebook) than the corresponding applicative expression.
First of all a disclaimer, I might have misunderstood completely the way threepenny-gui works due to my not so advanced knowledge of Haskell, so take my assertions with a grain of salt. :-)
It seems to me that some combinators are not pure e.g.
stepper :: MonadIO m => a -> Event a -> m (Behavior a)
Is stepper necessarily operating on some type of IO monad (and it's therefore not pure), or I'm I misunderstanding something here ? Second question, if those combinators are indeed unpure, why is that ? To me this makes the code for building the event graph much less nice then reactive-banana since one has to use an IO monad instead of being just pure plain functions. The code seems to just become more complicated then in reactive-banana.
Even more scary, valueChange appears to be pure from the type
valueChange :: Element -> Event String
but it's actually using unsafeMapIO inside, so is it actually doing hidden IO ? Again, I might be misunderstanding something, but isn't this the most supreme sin in Haskell ? It was also confusing, I couldn't tell from type how the call-back registration was happening... this never happened to me before in Haskell... Was this done to spare users having to deal with the Frameworks monad ?
I'm not a threepenny-gui user, but I can give you a bit of info on your questions from my reading of the code.
Firstly regarding stepper: It seems that internally threpenny-gui uses IORefs to keep track of accumulating parameters. That's why stepper requires MonadIO. However, I do not think this requires IO to build your event graph, it just means it has to run in IO. This shouldn't cause any awkwardness for you as an API user. If you think it does please post a more specific question.
Secondly regarding valueChange being implemented in terms of an unsafe function: Yes, this is very common to see in Haskell libraries. Haskell library authors often use unsafe functions when they know the way that they are used is actually safe for all possible execution paths, but it is not possible to prove this to the type system. In other words, library authors use unsafe operations in a safe way so that you don't have to!
This answer is complementary to Tom Ellis'. It covers how I would justify the monadic FRP combinators from the perspective of a Threepenny user.
With Threepenny, odds are that a large part of your event graph will be built from Elements, which live in UI (a thin wrapper for IO) because they are tied to a DOM in a browser. That being so, it makes sense to remove the indirection involved in externally plugging something like reactive-banana (as the old reactive-banana-threepenny package used to do). In the case of reactive-banana-threepenny the indirection was not limited to having to use the Frameworks monad, but it also involved converting between the different event types of reactive-banana and Threepenny (it is also worth considering that FRP in Threepenny is optional; you can also use events in a traditional, imperative fashion).
Overall, it is a trade-off: to eliminate some indirection and make the API more immediately accessible, some cleanliness in defining the event graphs is sacrificed. In my experience, however, the trade-off is worth it.
On a closing note, this discussion I had with Heinrich around the time of the switch away from reactive-banana-threepenny may shed some more light on the matter.
So I have a project that I think is simple enough to learn with, but complex enough to be interesting that I would like to write using the Happstack library. At it's most fundamental level, this project would just be a fancy file server with some domain-specific REST methods (or whatever, I don't really care if it's truly RESTful or not) for searching and getting said files and metadata. Since I'm also trying to really learn monad transformers right now, I decided this would be the perfect project to learn with. However, I'm running into some difficulties getting it started, particularly with how to construct my transformer stack.
Right now, I'm only worried about a few things: config, error reporting, and state, and logging, so I started with
newtype MyApp a = MyApp {
runMyApp :: ReaderT Config (ErrorT String (StateT AppState IO)) a
} deriving (...)
Since I'm always going to be in IO, I can really easily use hslogger with this to take care of my logging. But I also knew I needed to use ServerPartT in order to interact with Happstack, thus
runMyApp :: ReaderT Config (ErrorT String (StateT AppState (ServerPartT IO))) a
I can get this to run, see requests, etc, but the problem I've run into is that this needs FilterMonad implemented for it in order to use methods like dir, path, and ok, but I have no idea how to implement it for this type. I just need it to pass the filters down to the underlying monad. Can someone give me some pointers on how to get this obviously crucial type class implemented? Or, if I'm just doing something terribly wrong, just steer me in the right direction. I've only been looking at Happstack for a few days, and transformers are still quite new to me. I think I understand them enough to be dangerous, but I don't know enough about them that I could implement one on my own. Any help you can provide is greatly appreciated!
FULL CODE
(X-posted from /r/haskell)
The easiest thing for you to do would be to get rid of ErrorT from your stack. If you look here you can see that Happstack defines built-in passthrough instances of FilterMonad for StateT and ReaderT, but none for ErrorT. If you really want to keep ErrorT in your stack, then you need to write a passthrough instance for it. It will probably look a lot like the one for ReaderT.
instance (FilterMonad res m) => FilterMonad res (ReaderT r m) where
setFilter f = lift $ setFilter f
composeFilter = lift . composeFilter
getFilter = mapReaderT getFilter
I tend to think that you shouldn't have ErrorT baked into your application monad because you won't always be dealing with computations that can fail. If you do need to handle failure in a section of your code, you can always drop into it easily just by wrapping runErrorT around the section and then using ErrorT, lift, and return as needed. Also, extra layer in your transformer stack adds a performance tax on every bind. So while the composability of monad transformers is really nice, you generally want to use them sparingly when performance is a significant consideration.
Also, I would recommend using EitherT instead of ErrorT. That way you can make use of the fantastic errors package. It has a lot of really common convenience functions like hush, just, etc.
Also, if you want to see a real example of what you're trying to do, check out Snap's Handler monad. Your MyApp monad is exactly the problem that snaplets were designed to solve. Handler has some extra complexity because it was designed to solve the problem in a generalized way so that Snap users wouldn't need to build this common transformer stack themselves. But if you look at the underlying implementation you can see that at its core it is really just the Reader and State monads condensed into one.
Do the compiler or the more "native" parts of the libraries (IO or functions that have access to black magic and the implementation) make assumptions about these laws? Will breaking them cause the impossible to happen?
Or do they just express a programming pattern -- ie, the only person you'll annoy by breaking them are people who use your code and didn't expect you to be so careless?
The monad laws are simply additional rules that instances are expected to follow, beyond what can be expressed in the type system. Insofar as Monad expresses a programming pattern, the laws are part of that pattern. Such laws apply to other type classes as well: Monoid has very similar rules to Monad, and it's generally expected that instances of Eq will follow the rules expected for an equality relation, among other examples.
Because these laws are in some sense "part of" the type class, it should be reasonable for other code to expect they will hold, and act accordingly. Misbehaving instances may thus violate assumptions made by client code's logic, resulting in bugs, the blame for which is properly placed at the instance, not the code using it.
In short, "breaking the monad laws" should generally be read as "writing buggy code".
I'll illustrate this point with an example involving another type class, modified from one given by Daniel Fischer on the haskell-cafe mailing list. It is (hopefully) well known that the standard libraries include some misbehaving instances, namely Eq and Ord for floating point types. The misbehavior occurs, as you might guess, when NaN is involved. Consider the following data structure:
> let x = fromList [0, -1, 0/0, -5, -6, -3] :: Set Float
Where 0/0 produces a NaN, which violates the assumptions about Ord instances made by Data.Set.Set. Does this Set contain 0?
> member 0 x
True
Yes, of course it does, it's right there in plain sight! Now, we insert a value into the Set:
> let x' = insert (0/0) x
This Set still contains 0, right? We didn't remove anything, after all.
> member 0 x'
False
...oh. Oh, dear.
The compiler doesn't make any assumptions about the laws, however, if your instance does not obey the laws, it will not behave like a monad -- it will do strange things and otherwise appear to your users to not work correctly (e.g. dropping values, or evaluating things in the wrong order).
Also, refactorings your users might make assuming the monad laws hold will obviously not be sound.
For people working in more "mainstream" languages, this would be like implementing an interface, but doing so incorrectly. For example, imagine you're using a framework that offers an IShape interface, and you implement it. However, your implementation of the draw() method doesn't draw at all, but instead merely instantiates 1000 more instances of your class.
The framework would try to use your IShape and do reasonable things with it, and God knows what would happen. It'd be kind of an interesting train wreck to watch.
If you say you're a Monad, you're "declaring" that you adhere to its contract and laws. Other code will believe your declaration and act accordingly. Since you lied, things will go wrong in unforeseen ways.