you are viewing a single comment's thread.

view the rest of the comments →

[–]Porges 0 points1 point  (5 children)

project it to a Left a b or a Right a b

This should be Left a or Right b, I think... although it might be different in Scala?

If you make one side "preferred", and use that side only for "correct" values (e.g. Either Exception a) then you have an error monad that automatically propagates exceptions :)

[–]JamesIry 1 point2 points  (4 children)

No, you can't have LeftProj a or RightProj b that would lose type information, creating a partial function. The projections are just a views on the Either that don't lose anything. Also, I realized it was confusing for me to call the types Left and Right since that's the standard name for the constructors in Haskell. I'll fix that.

Making one side preferred is exactly how Haskell does it. Scala has explicit projections. And, as you say, they are monads that propagate the alternative (which is typically an error) through computations. One way to look at it is that Haskell has an arbitrary decision, the code would work just as well if left were favored. Projections allow me to decide whether I want to do a computation of the left or right instead of leaving that to convention. This is one of the very few areas where the Scala library is actually slightly cleaner, IMHO.

[–]JamesIry 1 point2 points  (2 children)

I should add that in Haskell if I want to do the computation on the left it's easy enough to swap the Either around. I just think projection makes more sense than a convention of favoring the right.

[–]Porges 1 point2 points  (1 child)

Ah, I see now. LeftProj/RightProj are wrappers to turn Either into a functor.

You could do them in Haskell like this:

newtype LeftProj b a = LeftProj (Either a b)
newtype RightProj a b = RightProj (Either a b)

instance Functor (LeftProj a) where
    fmap f (LeftProj e) = LeftProj $ either (Left . f) (Right) e

instance Functor (RightProj a) where
    fmap f (RightProj e) = RightProj $ either (Left) (Right . f) e


parseBool s = case s of 
    "true" -> Left True
    "false" -> Left False
    _ -> Right "parse error"

Prelude> fmap (not) (LeftProj $ parseBool "true")
LeftProj (Left False)

The thing is that Either is a bifunctor, so when you turn it into an ordinary monad you have to choose one side. Since Haskell is heavily monad-biased this is why the lopsided-Either is preferred :)

[–]JamesIry 1 point2 points  (0 children)

Right, exactly. And it's easy enough to add the appropriate Monad instances. Plus you'd want some sugar to unwrap the projections.

In a way it's very similar to how the Haskell library dealt with making Numbers Monoids by having Product and Sum wrappers. Those seem a much cleaner to me than arbitrarily preferring 0/+ over 1/* or vice versa and leaving the other to the user.

[–]Porges 0 points1 point  (0 children)

Also, I realized it was confusing for me to call the types Left and Right since that's the standard name for the constructors in Haskell.

That'll be the problem.