简体   繁体   English

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

[英]Implementing predicate types as contravariant monads?

I'm pretty sure I can show that, in principle, the Predicate type constructor is a ContraMonad , a generalization of Monad where the functor is contravariant, but I'm not sure how it would be implemented. 我很确定我可以证明,原则上, Predicate类型构造函数是一个ContraMonad ,是Monad的泛化,其中函子是逆变的,但我不确定它是如何实现的。

First, let's define some terms: 首先,让我们定义一些术语:

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)

This shows that a predicate, which is a function that takes values of a and generates propositions from them, is a contravariant functor from Hask to Hask^{op}. 这表明谓词是一个从a取值并从中生成命题的函数,是一个从Hask到Hask ^ {op}的逆变函子。 An example of a predicate would be: 谓词的一个例子是:

isEven :: Integer -> Bool
isEven n = if (mod n 2 == 0)
           then True
           else False

The function isEven is a predicate in the mathematical sense, while Pred isEven is a Predicate in the sense implemented here. 函数isEven是数学意义上的谓词,而Pred isEven是这里实现的意义上的Predicate

Implementing Predicate as a ContraMonad is trickier. Predicate作为ContraMonad实现是比较棘手的。

There is only one natural choice for return . return只有一个自然的选择。

return :: a -> Predicate a
return x = Pred(const True)

You may consider the possibility of 'return' giving a Predicate for which x is true and no other elements of type a are true. 你可以考虑'return'给出一个谓词的可能性,其中x是真的,没有其他类型的元素是真的。 The problem is that this can only be implemented if a is a member of the Eq typeclass. 问题是只有当a是Eq类型类的成员时才能实现。

It is easier to think about join than contrabind . 考虑加入比contrabind更容易。 Clearly, we want 显然,我们想要

contrabind x f = join.contramap f x

so that contrabind resembles bind as much as possible while taking account of f as a contravariant functor. 因此,在考虑到f作为逆变函子时,违禁品类似于尽可能多地绑定。 The function f takes Pred b to a , so contramap f takes Pred a to Pred(Pred b) . 函数fPred ba ,因此contramap fPred aPred(Pred b)

So, what should join do? 那么,应该join什么呢? It must take a Predicate of a Predicate of type a to a Predicate of type a. 它必须将类型a的谓词谓词设置为类型a的谓词。 A Predicate of a Predicate of type a makes a judgement about predicates of type a. 类型a谓词的谓词判断类型a的谓词。 As an example, consider a = Integer. 例如,考虑a =整数。 An example of Pred( Pred Integer) is: Pred( Pred Integer)一个例子是:

Pred("The predicate f is true for all even numbers.")

Where I have used a quote in place of an actual implementation. 我在哪里使用引用代替实际实现。 If it is the case that f is true for all evens, then this statement is true. 如果所有evens都是f的情况,则该陈述为真。 For example, Pred isEven would evaluate to True . 例如, Pred isEven将评估为True

Considering the fact that predicates on a set A are correspond to subsets of A, a Pred(Pred A) is a wrapper for a function that takes all subsets of A and judges them as "True" or "False." 考虑到集合A上的谓词对应于A的子集这一事实, Pred(Pred A)是一个函数的包装器,它接受A的所有子集并将它们判断为“True”或“False”。 We want join to give us a predicate, which is the same as giving a subset of A. This subset should be as lossless as possible. 我们希望join给我们一个谓词,这与给出A的子集相同。这个子集应该尽可能无损。 In fact, it should care about the truth value of every single Pred X , wrt the Pred(Pred X) judgment. 事实上,它应该关注每一个Pred X的真值,以及Pred(Pred X)判断。 The natural solution seems, to me, to be the intersection of all subsets judged as "True," which is the same thing as "ANDing" together all true predicates where 对我来说,自然解决方案似乎是被判断为“真实”的所有子集的交集,这与所有真实谓词“并行”在一起的情况相同

predAnd :: (a -> Bool) -> (a -> Bool) -> (a -> Bool)
predAnd p q = \x -> ((getPred p) $ x) && ((getPred q) $ x)

As an example, let's go back to "The predicate f is true for all even numbers." 举个例子,让我们回到“谓词f对于所有偶数都是正确的”。 Every predicate evaluated as True under this judgment must be true for all evens, so every possible subset contains the evens. 在此判断下评估为True每个谓词对于所有均值都必须为真,因此每个可能的子集都包含均值。 The intersection of all these sets will simply be the set of evens, so join would return the predicate Pred isEven . 所有这些集合的交集将只是一组Pred isEven ,因此join将返回谓词Pred isEven

My question is this, how would 'join' actually be implemented? 我的问题是,“加入”实际上将如何实施? Can it be implemented? 可以实施吗? I can see potential problems with undecidable sets arising for infinite types like 'Integer', but could it even be implemented for finite types like 'Char', where even the power set has finite cardinality? 我可以看到像“整数”这样的无限类型产生不可判定集的潜在问题,但是它甚至可以用于像'Char'这样的有限类型,即使幂集具有有限的基数?

There is something related, the contravariant version of Applicative is Divisible , which I've simplified here 有一些相关的东西, Applicative的逆变版本是Divisible ,我在这里简化了

class Contravariant f => Divisible f where
  divide  :: f b -> f c -> f (b, c)
  conquer :: f a

The divide in Data.Functor.Contravariant.Divisible is written in terms of a -> (b, c) which highlights the idea of dividing the work for a into the work for b and c . divideData.Functor.Contravariant.Divisible写入方面a -> (b, c)其中突出除以工作的想法a到用于工作bc

{-# 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

Any Op is Divisible if its result is a Monoid . 如果其结果是Monoid则任何Op都是Divisible This is a generalization of Predicate , which is Op All 这是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)

As a bonus, Op r is a Monoid as long as the result r is a moniod, which provides a straightforward way to define predAnd 作为奖励, Op rMonoid ,只要结果r是moniod,它提供了一种直接定义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 

But that's hardly surprising . 但这并不奇怪

I think I may have thought of a partial answer to my own question. 我想我可能已经想到了对我自己的问题的部分答案。 First, we must force a to be in the Eq typeclass. 首先,我们必须强制a进入Eq类型类。

join p = Pred(\x -> not ((getPred p) (\y -> y /= x)))

This is a predicate which takes an element of type a and constructs a predicate out of it, namely the predicate where x is false and everything else is true. 这是一个谓词,它接受一个类型为a的元素并构造一个谓词,即谓词,其中x为false,其他一切都为真。 Then, it evaluates this predicate according to the original Pred( Pred a) judgment. 然后,它根据原始的Pred( Pred a)判断来评估该谓词。

If this predicate is true, than it is the case that x is not in the intersection of all predicates, and thus the final predicate sends x to False. 如果此谓词为真,那么x不在所有谓词的交集中,因此最终谓词将x发送到False。 On the other hand, if this predicate is false, it is not necessarily the case that x is in the intersection, unless we make an additional rule: 另一方面,如果此谓词为false,则不一定是x在交集中的情况,除非我们另外制定一条规则:

If the Pred( Pred a) judgment rules a nearly maximal predicate false, the element ruled false in the nearly maximal predicate must appear in all predicates judged to be true. 如果Pred( Pred a)判断规则接近最大谓词false,则在近似最大谓词中被判断为false的元素必须出现在被判断为真的所有谓词中。

Thus, the judgment "The predicate f is true for all even numbers." 因此,判断“谓词f对于所有偶数都是正确的”。 is allowed under this rule, but the judgment "The predicate f is true only for even numbers." 根据这条规则是允许的,但是判断“谓词f仅适用于偶数。” is not allowed. 不被允许。 If a judgment of the first kind is used, join will give the intersection of all true predicates. 如果使用第一种判断,则join将给出所有真实谓词的交集。 In the second case, join will simply give a predicate which can sometimes tell you if x is unnecessary. 在第二种情况下, join只会给出一个谓词,有时可以告诉你x是否是不必要的。 If it returns False , x is definitely unnecessary. 如果返回False ,则x绝对不必要。 If it returns True , the test is inconclusive. 如果它返回True ,则测试结果不确定。

I'm now convinced that a full implementation of "ANDing" is impossible unless the type a is finite and even then is impractical unless a is very small. 我现在确信,除非a类型是有限的,否则完全实现“ANDing”是不可能的,即使这样也是不切实际的,除非a非常小。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM