有没有人知道一个允许人们对模式匹配放置类型限制的扩展名?例如:
{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,但我想检查输入类型是否相等,以便我可以组合重复。
答案 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>
现在出现了复杂的部分:它被描述为here和here。 (第二个链接,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