简体   繁体   English

Haskell扩展模式匹配类型约束

[英]Haskell Extension Pattern Matching With Type Constraints

Does anyone know of an extension that would allow one to put type constraints on pattern matching? 有没有人知道一个扩展,允许一个类型约束模式匹配? For example: 例如:

{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

Note: I am asking this so I could make monads that react differently based on its input type. 注意:我问这个,所以我可以根据其输入类型使monad做出不同的反应。 Specifically, I was tring to make a probability monad, but I would like to check if the input type is equatable so I could combine duplicates. 具体来说,我想要一个概率monad,但我想检查输入类型是否相等,所以我可以组合重复。

I don't know of such an extension, and furthermore I suspect it would be very difficult to create such an extension with GHC's current method of implementing classes. 我不知道这样的扩展,而且我怀疑使用GHC当前实现类的方法创建这样的扩展是非常困难的。 In particular, GHC does classes by passing around "evidence" that a given type implements a given class; 特别是,GHC通过传递给定类型实现给定类的“证据”来完成类; so when you see foo :: Eq a => a -> IO Bool that really means foo takes two arguments: one is an a and the other is a piece of evidence that there is an Eq a instance. 所以当你看到foo :: Eq a => a -> IO Bool ,真正意味着foo两个参数:一个是a ,另一个是证据表明Eq a实例。 This evidence is unforgeable. 这个证据是不可伪造的。

Now think about what's happening in your proposed code: you have in scope a value of type a and evidence that there's an Eq a instance, and then you ask the question: is there evidence anywhere in this program that there's an Ord a instance? 现在考虑一下你提出的代码中发生了什么:你在范围内有一个类型a的值,并且证明有一个Eq a实例,然后你问一个问题: 这个程序中是否有证据表明有一个Ord a实例? Moreover, I'm not going to tell you ahead of time what a is. 而且,我不会提前告诉你 a是什么。 Please give me the right answer to my question anyway, thanks! 无论如何,请给我正确的答案,谢谢!

Here's the most canonical tricky example I can come up with. 这是我能想到的最规范的棘手例子。

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

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

The Existential type wraps up a value of another type, remembering only two things: the value that we wrapped, and some evidence that there is an Eq type for that instance. Existential类型包含另一种类型的值,只记住两件事:我们包装的值,以及一些证据表明该实例有一个Eq类型。 In particular, the type itself isn't even remembered! 特别是, 类型本身甚至没有被记住! So now, when it comes time to run tricky , we have a value of some type (but we've forgotten which one) and evidence that the type in question is an instance of Eq . 所以,现在,当谈到时间来运行tricky ,我们有一些类型的值(但我们已经忘记了是哪一个)和证据有问题的类型的实例Eq Now, how are we going to guess whether it's also an instance of Ord ? 现在,我们如何猜测它是否也是Ord的一个实例? We can't really know -- we don't even know which instance to look for! 我们真的不知道 - 我们甚至不知道要寻找哪个实例! (Indeed, we can't even really inspect the value in a reasonable way to try to reconstruct what type it is; the only inspection we can do are operations offered by the Eq type class. That doesn't tell us much.) Are we looking for an Ord Int instance? (实际上,我们甚至无法以合理的方式检查价值以尝试重建它的类型;我们唯一可以做的检查是Eq类型类提供的操作。这并不能告诉我们太多。)是我们在寻找Ord Int实例? Or maybe an Ord (Complex Double) instance? 或者也许是Ord (Complex Double)实例? No idea. 不知道。

On the other hand, you can do something like this already: 另一方面,你可以做这样的事情:

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

...but you will need one instance of QueryOrd for each non- Ord instance of Eq . ...但你需要的一个实例QueryOrd每个非Ord的实例Eq

There is a way, but it isn't pretty. 有一种方法,但它不漂亮。 We'll first make the function that we want to see dispatch on type class instance. 我们首先要创建我们想要在类型类实例上看到调度的函数。

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

isOrd will eventually have two instances, corresponding two each of your case s. isOrd最终将有两个实例,对应的两个您的每一个case秒。 Of course, the simple 当然,简单

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

Won't work because the instance heads ( IsOrd ) are the same, and the way the compiler works is that it matches the instance head whether or not the constraint holds, and only then checks the constraint. 将无法工作,因为实例头( IsOrd )是相同的,并且编译器工作的方式是它匹配实例头是否保持约束,然后才检查约束。

Now comes the complicated part: it is described here and here . 现在是复杂的部分:它在这里这里描述。 (The second link, Oleg Kiselyov's collecion on Type Classes, might have other stuff that's relevant, and that I'm not including here because I don't know about it yet. It's also a great resource generally!). (第二个链接,Oleg Kiselyov关于Type Classes的集合,可能还有其他相关内容,而且我不包括在这里因为我还不知道它。它通常也是一个很好的资源!)。 The essence of it is getting to: 它的本质是:

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

Where we've added an extra type-level boolean to the instance head so that what in your example were case s become distinct instance heads. 当我们增加了一个额外的类型级boolean值以实例头,这样在你的榜样是什么case š成为不同实例头。 Pretty nifty idea! 非常漂亮的想法!

There are other details that you have to worry about, some of which I don't fully understand, but it's easy enough to get it working anyhow: here's some code that does what you want (you'll have to thread Eq instances all along it, from IsOrd onwards, if you want the Eq constraint too). 还有其他一些你需要担心的细节,其中一些我并不完全理解,但它很容易让它无论如何工作: 这里有一些代码可以做你想要的(你必须一直在线程Eq实例)它,从IsOrd开始,如果你也想要Eq约束)。 And here's the end result: 这是最终结果:

*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

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

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