Haskell扩展模式匹配类型约束

时间:2013-11-29 18:36:43

标签: haskell pattern-matching language-extension

有没有人知道一个允许人们对模式匹配放置类型限制的扩展名?例如:

{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做出不同的反应。具体来说,我想制作一个概率monad,但我想检查输入类型是否相等,以便我可以组合重复。

2 个答案:

答案 0 :(得分:2)

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

现在考虑一下你提出的代码中发生了什么:你在范围内有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的一个实例。

答案 1 :(得分:1)

有一种方法,但它不漂亮。我们首先要创建我们想要在类型类实例上看到调度的函数。

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

isOrd最终会有两个实例,分别对应两个case个实例。当然,简单

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

因为实例头(IsOrd)是相同的而无法工作,并且编译器的工作方式是它匹配实例头,无论约束是否成立,然后才检查约束。 / p>

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

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成为不同的实例头。非常漂亮的想法!

还有其他一些你需要担心的细节,其中一些我并不完全理解,但是它很容易让它无论如何都能正常工作:here's一些代码可以做你想要的(你会做的)如果你想要IsOrd约束,那么必须从Eq开始一直对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