“These”的“Applicative”实例能否保留更多“失败”信息?

dan*_*iaz 7 haskell applicative

我正在查看这些These包中的数据类型,特别是在其实例中:Applicative

instance (Semigroup a) => Applicative (These a) where
    pure = That
    This  a   <*> _         = This a
    That    _ <*> This  b   = This b
    That    f <*> That    x = That (f x)
    That    f <*> These b x = These b (f x)
    These a _ <*> This  b   = This (a <> b)
    These a f <*> That    x = These a (f x)
    These a f <*> These b x = These (a <> b) (f x)
Run Code Online (Sandbox Code Playgroud)

如果其中之一These是 a This,则结果始终是This。然而,似乎存在一定的不对称性。

在这里,如果第二个组件是These构造函数,则其信息将被完全丢弃:

    This  a   <*> _         = This a
Run Code Online (Sandbox Code Playgroud)

这里的第一个组件是These构造函数,但该a部分保留在结果中。

These a _ <*> This  b   = This (a <> b)
Run Code Online (Sandbox Code Playgroud)

在 ghci 中测试它:

ghci> This "a" <*> These "b" True
This "a"
ghci> These "a" not <*> This "b"
This "ab"
Run Code Online (Sandbox Code Playgroud)

但是如果我们在开头添加一个案例,比如

This  a <*> These b _    = This (a <> b)
Run Code Online (Sandbox Code Playgroud)

这会违反Applicative法律吗?

dup*_*ode 3

您提出的实例是合法的。展示这一点的一种方法是通过These一对可能进行近似:

{-# LANGUAGE GHC2021, LambdaCase, PatternSynonyms #-}
import Data.These
import Data.Functor.Product
import Data.Functor.Const

newtype NotQuiteThese a b = NQT (Product (Const (Maybe a)) Maybe b)
    deriving (Show, Functor, Applicative)

pattern Neither = NQT (Pair (Const Nothing) Nothing)
pattern This' a = NQT (Pair (Const (Just a)) Nothing)
pattern That' b = NQT (Pair (Const Nothing) (Just b))
pattern These' a b = NQT (Pair (Const (Just a)) (Just b))

theseToNqt :: These a b -> NotQuiteThese a b
theseToNqt = \case
    This a -> This' a
    That b -> That' b
    These a b -> These' a b

-- nqtToThese . theseToNqt = Just
nqtToThese :: NotQuiteThese a b -> Maybe (These a b)
nqtToThese = \case
    Neither -> Nothing
    This' a -> Just (This a)
    That' b -> Just (That b)
    These' a b -> Just (These a b)
Run Code Online (Sandbox Code Playgroud)

NotQuiteTheseThese由于多余的情况,与 不同构Neither。不过,就我们当前的目的而言,这不是问题:就非案例而言,此处使用的编码产生的instance Semigroup a => Applicative (NotQuiteThese a)相当于您建议的实例Neither。(第一个Maybe将半群提升为幺半群,Const进一步将其提升为应用函子,并且应用的乘积是应用的。)下面是一个快速演示:

ghci> nqtToThese $ theseToNqt (These "foo" (2*)) <*> theseToNqt (This "bar")
Just (This "foobar")
ghci> nqtToThese $ theseToNqt (This "foo") <*> theseToNqt (These "bar" 7)
Just (This "foobar")
Run Code Online (Sandbox Code Playgroud)

规范实例中的不对称性These是为了与Monad实例兼容而强加的,因此(<*>) = ap成立。不可避免This a >>= _ = This a从句的意思是ap (This a) _ = This a。这种情况类似于及其误差累积变体之间的对比EitherValidation

最后一点,如果出于实际目的需要使用These-plus-类型,您可以粉碎中使用- 只要您不介意有一个实例,因此也不介意不对称。NeitherCanCanMonadApplicative