我非常确定我可以证明原则上Predicate
类型构造函数是ContraMonad
,Monad
的泛化,其中函子是逆变的,但我和#39;我不确定如何实施。
首先,让我们定义一些术语:
class Contravariant f where
contramap :: (b -> a) -> f a -> f b
class ContraMonad m where
return :: a -> m a
join :: m(m a) -> m a
contrabind :: m a -> (m b -> a) -> m b
data Predicate a = Pred {getPred :: a -> Bool}
instance Contravariant Predicate where
contramap f x = Pred ((getPred x).f)
这表明谓词是一个从a取值并从中生成命题的函数,是一个从Hask到Hask ^ {op}的逆变函子。谓词的一个例子是:
isEven :: Integer -> Bool
isEven n = if (mod n 2 == 0)
then True
else False
函数isEven
是数学意义上的谓词,而Pred isEven
在此处实现的意义上是Predicate
。
将Predicate
实施为ContraMonad
比较棘手。
return
只有一个自然选择。
return :: a -> Predicate a
return x = Pred(const True)
您可以考虑“返回”的可能性。给出一个谓词,其中x为真,没有其他类型的元素为真。问题是只有当a是Eq
类型类的成员时才能实现。
考虑加入比contrabind
更容易。显然,我们想要
contrabind x f = join.contramap f x
因此,在考虑f作为逆变函子的情况下,违禁品类似于尽可能多地绑定。函数f
将Pred b
带到a
,因此contramap f
将Pred a
带到Pred(Pred b)
。
那么join
应该怎么做?它必须将类型a的谓词谓词设置为类型a的谓词。类型a谓词的谓词判断类型a的谓词。例如,考虑a =整数。 Pred( Pred Integer)
的一个示例是:
Pred("The predicate f is true for all even numbers.")
我使用引号代替实际实现。如果所有evens都是f的情况,则该陈述为真。例如,Pred isEven
将评估为True
。
考虑到集合A上的谓词对应于A的子集这一事实,Pred(Pred A)
是一个函数的包装器,它接受A的所有子集并将它们判断为" True"或"错误。"我们希望join
给我们一个谓词,这与给出A的子集相同。这个子集应该尽可能无损。事实上,它应该关心每一个Pred X
的真值,w.r.t。 Pred(Pred X)
判断。对我来说,自然解决方案似乎是所有子集的交集,判断为" True,"与" ANDing"相同所有真正的谓词在哪里
predAnd :: (a -> Bool) -> (a -> Bool) -> (a -> Bool)
predAnd p q = \x -> ((getPred p) $ x) && ((getPred q) $ x)
作为一个例子,让我们回到"谓词f对于所有偶数都是正确的。"对于所有均值,在此判断下评估为True
的每个谓词都必须为真,因此每个可能的子集都包含均值。所有这些集合的交集将只是一组平均值,因此join
将返回谓词Pred isEven
。
我的问题是,如何加入'实际上是实施?可以实施吗?我可以看到对于诸如“整数”之类的无限类型产生的不可判定集合的潜在问题,但是它甚至可以用于像Char'这样的有限类型,即使幂集具有有限的基数?
答案 0 :(得分:3)
有一些相关的东西,Applicative
的逆变版本是Divisible
,我在这里简化了
class Contravariant f => Divisible f where
divide :: f b -> f c -> f (b, c)
conquer :: f a
divide
中的Data.Functor.Contravariant.Divisible
是根据a -> (b, c)
撰写的,其中强调了将a
的工作划分为b
和c
工作的想法{1}}。
{-# LANGUAGE RankNTypes #-}
-- The divide from Data.Functor.Contravariant.Divisible
divide' :: Divisible f => (a -> (b, c)) -> f b -> f c -> f a
divide' f b c = contramap f $ divide b c
-- And a proof they types are equivalent
proof :: (forall a b c. (a -> (b, c)) -> f b -> f c -> f a) -> f b -> f c -> f (b, c)
proof divide' = divide' id
如果结果为Op
,则Divisible
为Monoid
。这是Predicate
的概括,Op
All
import Data.Monoid
newtype Op r a = Op {runOp :: a -> r}
instance Contravariant (Op r) where
contramap f (Op g) = Op (g . f)
instance Monoid r => Divisible (Op r) where
divide (Op f) (Op g) = Op (\(b, c) -> f b <> g c)
conquer = Op (const mempty)
作为奖励,只要结果Op r
是moniod,Monoid
就是r
,这提供了一种直接的方式来定义predAnd
instance Monoid a => Monoid (Op a b) where
mempty = Op (const mempty)
mappend (Op p) (Op q) = Op $ \a -> mappend (p a) (q a)
type Predicate a = Op All a
predAnd :: Predicate a -> Predicate a -> Predicate a
predAnd = mappend
答案 1 :(得分:0)
我想我可能已经想到了对我自己的问题的部分答案。首先,我们必须强制a
加入Eq
类型类。
join p = Pred(\x -> not ((getPred p) (\y -> y /= x)))
这是一个谓词,它接受类型a
的元素并构造一个谓词,即谓词,其中x为false,其他一切都为真。然后,它根据原始Pred( Pred a)
判断来评估此谓词。
如果此谓词为真,那么x不在所有谓词的交集中,因此最终谓词将x发送到False。另一方面,如果此谓词为false,则不一定x
在交集中,除非我们制定其他规则:
如果Pred( Pred a)
判断规则接近最大谓词为假,则在近似最大谓词中判断为false的元素必须出现在判断为真的所有谓词中。
因此,判断“谓词f对于所有偶数都是正确的”。根据这条规则是允许的,但是判断“谓词f仅适用于偶数。”不被允许。如果使用第一种判断,join
将给出所有真实谓词的交集。在第二种情况下,join
只会提供一个谓词,有时可以告诉您x
是否不必要。如果它返回False
,则x
绝对不必要。如果它返回True
,则测试结果不确定。
我现在确信,除非a
类型是有限的,否则完全实现“AND”是不可能的,即使这样,除非a
非常小,否则是不切实际的。