【问题标题】:In Haskell how can I take an m-ary predicate and an n-ary predicate and construct a (m+n)-ary predicate?在 Haskell 中,我如何获取一个 m 元谓词和一个 n 元谓词并构造一个 (m+n) 元谓词?
【发布时间】:2012-09-24 20:20:33
【问题描述】:

今天我玩了使用类型类来归纳构造任意数量的谓词的函数,将任意类型的任意组合作为输入,返回相同类型但应用了一些基本操作的其他谓词。例如

conjunction (>2) even

将返回一个谓词,对于大于 2 的偶数和

conjunction (>=) (<=)

会返回=

一切都好,这部分工作了,但它提出了一个问题,如果我想将两个谓词的连接定义为一个谓词,每个连接谓词的每个输入都需要一个输入,该怎么办?例如:

:t conjunction (>) not

将返回 Ord a => a -> a -> Bool -> Bool。这可以做到吗?如果有,怎么做?

【问题讨论】:

    标签: haskell typeclass polyvariadic


    【解决方案1】:

    此解决方案需要TypeFamilies

    {-# LANGUAGE TypeFamilies #-}
    

    这个想法是为 n 元谓词定义一个类Pred

    class Pred a where
      type Arg a k :: *
      split :: a -> (Bool -> r) -> Arg a r
    

    问题在于对谓词的参数重新洗牌,所以这就是该类的目标。关联类型 Arg 应该通过将最后的 Bool 替换为 k 来访问 n 元谓词的参数,所以如果我们有一个类型

    X = arg1 -> arg2 -> ... -> argn -> Bool
    

    然后

    Arg X k = arg1 -> arg2 -> ... -> argn -> k
    

    这将允许我们构建conjunction 的正确结果类型,其中两个谓词的所有参数都将被收集。

    函数split 接受a 类型的谓词和Bool -&gt; r 类型的延续,并将产生Arg a r 类型的东西。 split 的想法是,如果我们知道最后从谓词中获得的Bool 可以做什么,那么我们可以在两者之间做其他事情(r)。

    毫不奇怪,我们需要两个实例,一个用于Bool,另一个用于目标已经是谓词的函数:

    instance Pred Bool where
      type Arg Bool k = k
      split b k = k b
    

    Bool 没有参数,所以Arg Bool k 只返回k。另外,对于split,我们已经有了Bool,所以我们可以立即申请延续。

    instance Pred r => Pred (a -> r) where
      type Arg (a -> r) k = a -> Arg r k
      split f k x = split (f x) k
    

    如果我们有一个a -&gt; r 类型的谓词,那么Arg (a -&gt; r) k 必须以a -&gt; 开头,然后我们继续在r 上递归调用Arg。对于split,我们现在可以采用三个参数,x 的类型为a。我们可以将x 提供给f,然后在结果上调用split

    一旦我们定义了Pred类,就很容易定义conjunction

    conjunction :: (Pred a, Pred b) => a -> b -> Arg a (Arg b Bool)
    conjunction x y = split x (\ xb -> split y (\ yb -> xb && yb))
    

    该函数接受两个谓词并返回Arg a (Arg b Bool) 类型的内容。我们来看例子:

    > :t conjunction (>) not
    conjunction (>) not
      :: Ord a => Arg (a -> a -> Bool) (Arg (Bool -> Bool) Bool)
    

    GHCi 没有扩展这种类型,但我们可以。类型等价于

    Ord a => a -> a -> Bool -> Bool
    

    这正是我们想要的。我们也可以测试一些例子:

    > conjunction (>) not 4 2 False
    True
    > conjunction (>) not 4 2 True
    False
    > conjunction (>) not 2 2 False
    False
    

    请注意,使用Pred 类,编写其他函数(如disjunction)也很简单。

    【讨论】:

      【解决方案2】:
      {-# LANGUAGE TypeFamilies #-}
      
      class RightConjunct b where
        rconj :: Bool -> b -> b
      
      instance RightConjunct Bool where
        rconj = (&&)
      
      instance RightConjunct b => RightConjunct (c -> b) where
        rconj b f = \x -> b `rconj` f x
      
      class LeftConjunct a where
        type Ret a b
        lconj :: RightConjunct b => a -> b -> Ret a b
      
      instance LeftConjunct Bool where
        type Ret Bool b = b
        lconj = rconj
      
      instance LeftConjunct a => LeftConjunct (c -> a) where
        type Ret (c -> a) b = c -> Ret a b
        lconj f y = \x -> f x `lconj` y
      
      conjunction :: (LeftConjunct a, RightConjunct b) => a -> b -> Ret a b
      conjunction = lconj
      

      希望它是不言自明的,但如果不是,请随时提出问题。

      当然,您也可以将这两个类合并为一个,但我觉得这两个类使想法更清晰。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-07-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多