我有一个类看起来很像SemiAlign
,但不是:
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
将两个结构在冲突时总是取左值,alignRight
也这样做,但更倾向于取右值。
都可以用alignWith
或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
但是align
和alignWith
都不能用alignLeft
或alignRight
来定义。为了说明为什么,考虑以下实例:
instance Weakalign ((,) a) where
alignLeft =
const
感谢Daniel Wagner简化了这个例子。
这是一个完全有效的Weakalign
实例,但它不能扩展到遵守Semialign
的法律。
这是一个微不足道的例子。您可以考虑使用解析器类型:
data Parser a b
= Parser (a -> [(a, b)])
可以将alignLeft
定义为fail through操作,它尝试第一个解析器,只有在失败时才尝试第二个解析器。由于与元组相同的原因,无法将解析器扩展到完整的SemiAlign
实例。
我检查了semialign
包,它似乎不对应于任何现有的类。然而,它似乎已经存在于某个地方。我想知道它是否存在于另一个我不知道的类似align的包中,或者我可能找错了树,这与align无关。
不管怎样,我更愿意使用现有的库来做这类事情,而不是自己发明。
这是已知的抽象吗?
也许Alternative
符合您的需求。像alignLeft
这样的东西可以这样实现:
alignLeft :: Alternative f => f a -> f b -> f (Either a b)
alignLeft fa fb = (Left <$> fa) <|> (Right <$> fb)
同样适用于alignRight
,只是交换了<|>
的参数:
alignRight :: Alternative f => f a -> f b -> f (Either a b)
alignRight fa fb = (Right <$> fb) <|> (Left <$> fa)
它不完全是您指定的类。特别是没有一个真正好的(,)
实例(参见Alt
)。但它应该满足您对Parser
的需求,并且是现有解析器组合器库中非常常用的操作。