1

有谁知道允许对模式匹配施加类型约束的扩展?例如:

{Language Extension}
IsOrderable::(Eq a)=>a->IO Bool
IsOrderable x = case x of
    o::(Ord a=>a) -> do
                         putStrLn "This equatable thing is also orderable."
                         return True
    _             -> do
                         putStrLn "This equatable thing is not orderable."
                         return False

注意:我问这个是为了让 monad 根据其输入类型做出不同的反应。具体来说,我试图制作一个概率单子,但我想检查输入类型是否相等,以便我可以合并重复项。

4

2 回答 2

2

我不知道这样的扩展,而且我怀疑用 GHC 当前实现类的方法来创建这样的扩展是非常困难的。特别是,GHC 通过传递给定类型实现给定类的“证据”来进行类;所以当你看到foo :: Eq a => a -> IO Bool这真的意味着foo两个参数:一个是一个a,另一个是一个证据,证明有一个Eq a实例。这个证据是不可伪造的。

现在想想你提议的代码中发生了什么:你在范围内有一个类型的值a和有一个Eq a实例的证据,然后你问这个问题:在这个程序的任何地方是否有证据表明有一个Ord a实例?此外,我不会提前告诉你是什么a。无论如何,请给我正确的答案,谢谢!

这是我能想到的最典型的棘手示例。

{-# LANGUAGE ExistentialQuantification #-}
data Existential = forall a. Eq a => Existential a

tricky :: Existential -> IO Bool
tricky (Existential x) = isOrderable x

该类型包装了另一种类型的值,只记住两件事:我们包装的值,以及该实例Existential存在类型的一些证据。Eq特别是,甚至不记得类型本身!所以现在,当需要运行时tricky,我们有某种类型的值(但我们忘记了是哪一种),并且有证据表明所讨论的类型是Eq. 现在,我们如何猜测它是否也是 的一个实例Ord?我们真的不知道——我们甚至不知道要寻找哪个实例!(实际上,我们甚至无法以合理的方式真正检查该值以尝试重构它是什么类型;我们能做的唯一检查是Eq类型类提供的操作。这并不能告诉我们太多。Ord Int实例?或者也许是一个Ord (Complex Double)例子?不知道。

另一方面,您已经可以执行以下操作:

{-# LANGUAGE DefaultSignatures #-}
class Eq a => QueryOrd a where
    isOrd :: a -> IO Bool
    default isOrd :: Ord a => a -> IO Bool
    isOrd _ = putStrLn "yup" >> return True

isOrdAltDef :: Eq a => a -> IO Bool
isOrdAltDef _ = putStrLn "nope" >> return False

instance Eq a => QueryOrd (Complex a) where isOrd = isOrdAltDef
instance         QueryOrd MyFancyType where isOrd = isOrdAltDef

QueryOrd...但是对于每个非Ord实例,您都需要一个实例Eq

于 2013-11-29T19:17:03.130 回答
1

有一种方法,但它并不漂亮。我们将首先在类型类实例上创建我们希望看到的函数。

class IsOrd a where
    isOrd :: a -> IO Bool

isOrd最终会有两个实例,对应两个你case的 s. 当然,简单的

instance Ord a => IsOrd a where
    isOrd = putStrLn "Orderable!" >> return True
instance IsOrd a where
    isOrd = putStrLn "Not orderable" >> return False

不起作用,因为实例头(IsOrd)是相同的,编译器的工作方式是无论约束是否成立,它都匹配实例头,然后才检查约束。

现在是复杂的部分:这里这里都有描述。(第二个链接,Oleg Kiselyov 关于类型类的集合,可能还有其他相关的东西,我不包括在这里,因为我还不知道。它也是一个很好的资源!)。它的本质是:

data HTrue
data HFalse
instance (Ord a) => IsOrd' HTrue a where
  isOrd' _ x = putStrLn "Orderable." >> return True
instance IsOrd' HFalse a where
  isOrd' _ x = putStrLn "Not orderable" >> return False

我们在实例头中添加了一个额外的类型级布尔值,以便您的示例中case的 s 成为不同的实例头。很不错的主意!

您还必须担心其他细节,其中一些我不完全理解,但无论如何都很容易让它工作:这里有一些代码可以满足您的需求(您必须一直线程化 Eq 实例它,从IsOrd开始,如果你也想要Eq约束)。这是最终结果:

*Scratch> :l scratch.hs
[1 of 1] Compiling Scratch          ( scratch.hs, interpreted )
Ok, modules loaded: Scratch.
*Scratch> isOrd (5::Int)
Orderable.
True
*Scratch> isOrd ('c')
Not orderable
False
于 2014-02-10T14:32:20.010 回答