I have a class which looks a lot like SemiAlign
, but isn't:
class
( Functor f
)
=> Weakalign f
where
{-# Minimal alignLeft | alignRight #-}
alignLeft :: f a -> f b -> f (Either a b)
alignLeft =
(fmap swap .) . flip alignRight
alignRight :: f a -> f b -> f (Either a b)
alignRight =
(fmap swap .) . flip alignLeft
swap :: Either a b -> Either b a
swap (Left x) =
Right x
swap (Right x) =
Left x
alignLeft
aligns two structures always taking the left value on conflict, and alignRight
does the same but preferring the right value.
Both can be defined in terms of alignWith
or align
:
alignLeft ::
( Semialign f
)
=> f a -> f b -> f (Either a b)
alignLeft =
alignWith go
where
go (This x) =
Left x
go (That y) =
Right y
go (These x _) =
Left x
But neither align
nor alignWith
can be defined in terms of alignLeft
or alignRight
. As a demonstration of why consider the following instance:
instance Weakalign ((,) a) where
alignLeft =
const
Thanks to Daniel Wagner for simplifying this example.
This is a perfectly valid Weakalign
instance, but it cannot be extended to a law abiding Semialign
.
That is a bit of a trivial example though. You might consider instead a parser type:
data Parser a b
= Parser (a -> [(a, b)])
We could define alignLeft
as a fall through operation, which tries the first parser and only if it fails tries the second parser. For the same reason as the tuple there would be no way to extend the parser to a full SemiAlign
instance.
I checked the semialign
package and it doesn't seem like this corresponds to any existing class there. However it seems like something that would already exist somewhere. And I'm wondering if maybe it exists in another align-like package I don't know of, or maybe I'm barking up the wrong tree and this isn't related to align.
Either way I'd prefer to use an extant library for this sort of thing instead of inventing my own.
Is this a known abstraction?
Perhaps Alternative
meets your needs. Something like alignLeft
can be implemented this way:
alignLeft :: Alternative f => f a -> f b -> f (Either a b)
alignLeft fa fb = (Left <$> fa) <|> (Right <$> fb)
Similarly for alignRight
, with the arguments to <|>
simply swapped:
alignRight :: Alternative f => f a -> f b -> f (Either a b)
alignRight fa fb = (Right <$> fb) <|> (Left <$> fa)
It isn't exactly the class you specified. In particular there's not really a good (,)
instance (but see also Alt
). But it should meet your needs for Parser
, and is a very commonly provided operation in existing parser combinator libraries.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With