Thanks, Scott. It does help. I keep forgetting that items like
RemoteService aren’t really types but functions that construct objects of a given type. This is, at least in part, due to the fact that they are not declared like functions.
BTW, I have read about Haskell Type Constructors before but I never really thought of them as functions in the sense of them taking arguments and returning objects of a given type. I always thought of them as ‘sub-types’ of the primary type. From your book I see that isn’t (exactly) the case - though there is some kind of structural identity associated with the type constructor kept with the object created to allow for pattern matching later on.
But, the naming of these functions in this example is still a bit off-putting to me. When I read the line
|> Result.mapError RemoteService
I get the immediate impression of ‘calling’ (or using) a remote service, not creating an error type. I have to go back and look at the definition of
RemoteService to remember it is creating a kind of
PlaceOrderError. The two names don’t seem to have anything to do with one another. That is the real source of confusion for me.
RemoteService is a word describing something that does something (including, possibly, generating an error) while
RemoteServiceError is a type of error - most likely from a remote service. :)
So, using a function named
RemoteService doesn’t imply creating an error type to me.
In fact, even with the potential confusion created by shadowing, I might have considered defining
PlaceOrderError something like this:
type PlaceOrderError =
| ValidationError of ValidationError
| PricingError of PricingError
| RemoteServiceError of RemoteServiceError
This isn’t ideal either since you can now have confusion in your code as to which
RemoteServiceError you are referring to. But the line:
|> Result.mapError RemoteServiceError
is more descriptive of what is being done, here. At least it is to me.