将谓词类型实现为逆变monad?

时间:2017-09-07 03:01:37

标签: haskell monads functor

我非常确定我可以证明原则上Predicate类型构造函数是ContraMonadMonad的泛化,其中函子是逆变的,但我和#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作为逆变函子的情况下,违禁品类似于尽可能多地绑定。函数fPred b带到a,因此contramap fPred 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'这样的有限类型,即使幂集具有有限的基数?

2 个答案:

答案 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的工作划分为bc工作的想法{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,则DivisibleMonoid。这是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 

但那是hardly surprising

答案 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非常小,否则是不切实际的。