2015-05-19 60 views
1

假设我需要不同的输出,具体取决于函数的多态参数的类型。我的初始尝试失败一些神秘的错误消息:多态参数类型的模式匹配 - 替代

choice :: a -> Int 
choice (_ :: Int) = 0 
choice (_ :: String) = 1 
choice _ = 2 

然而,我们可以通过在不同的数据构造包裹所需类型固定,很容易并使用这些中的模式匹配:

data Choice a = IntChoice Int | StringChoice String | OtherChoice a 

choice :: Choice a -> Int 
choice (IntChoice _) = 0 
choice (StringChoice _) = 1 
choice (OtherChoice _) = 2 

问题:你知道一个方法来绕过这个吗? Haskell2010,GHC或允许我使用第一个变体(或类似的东西)的任何扩展中有没有功能?

+1

'Data.Typeable'允许的查询类型在运行时,但它不应该轻易使用。 – chi

回答

4

问题:你知道一种方法来规避这种情况吗? Haskell2010,GHC或允许我使用第一个变体(或类似的东西)的任何扩展中有没有功能?

没有,要么是在2010年或任何GHC扩展,让你写类型

choice :: a -> Int 

,其返回值取决于它的参数类型的函数提供哈斯克尔没有特色。你可以指望将来也不存在这样的特征。

即使有瑕疵在运行时检查GHC的内部数据表示,不可能从类型为Int的新类型的值中区分出Int类型的值:这些类型具有相同的运行时表示。

由函数返回的Int是一个存在于运行时的值,因此需要由运行时存在的另一个值来确定。这可能是要么

  1. 一个普通的价值,就像你data Choice a = IntChoice Int | StringChoice String | OtherChoice a; choice :: Choice a -> Int例如,或者

  2. 一种类字典,使用一个自定义类,如大卫年轻人的答案,或者内置Typeable类:

    choice :: Typeable a => a -> Int 
    choice a 
        | typeOf a == typeOf (undefined :: Int) = 0 
        | typeOf a == typeOf (undefined :: String) = 1 
        | otherwise        = 2 
    
+0

感谢您澄清替代方案。 _“不可能将int类型的值与类型为Int的新类型的值区分开来:这些类型具有相同的运行时表示”_ - 不是TypeSynonymInstances的用途吗? (在[大卫的回答](http://stackoverflow.com/a/30333292/3041008)中提到)。你会如此善良,并使用Typeable类型提供'choice'函数的定义吗? – mucaho

+3

@mucaho'TypeSynonymInstances'专门用于类型实例。特别是,它可以让你为一个类型同义词做一个实例。类型同义词是类型系统对待另一种类型的类型(在本例中,type = String = [Char]')。即使有了这个扩展和'OverlappingInstances',你也不能创建'String'和'[Char]'的实例,因为它们是同义词。另一方面,'newtype's被类型系统视为不同类型,但它们具有完全相同的运行时表示。 –

5

这混淆了两种不同的多态性。你想要的是 ad-hoc多态性,这是通过类型类来完成的。 a -> Int类型函数的多态性类型为参数多态性。对于参数多态,choice的一个函数定义必须适用于任意可能的类型a。在这种情况下,这意味着它实际上不能使用类型为a的值,因为它不知道任何关于它的值,因此choice必须是一个常量函数,例如choice _ = 3。这实际上给你非常强大的保证功能可以做什么,只是看它的类型(这个属性被称为参数)。

与类型类,你可以实现你的例子如:

class ChoiceClass a where 
    choice :: a -> Int 

instance ChoiceClass Int where 
    choice _ = 0 

instance ChoiceClass String where 
    choice _ = 1 

instance ChoiceClass a where 
    choice _ = 2 

现在,我要指出的是,这种类型的类的做法往往是错误的尤其是当别人谁是刚开始想用它。你绝对是不想这样做,以避免简单的类型,如Choice类型在你的问题。它可以增加很多复杂性,实例解析起初可能会让人困惑。请注意,为了使类型解决方案能够正常工作,需要打开两个扩展:FlexibleInstancesTypeSynonymInstances,因为String[Char]的同义词。 OverlappingInstances也是必需的,因为类型类在“开放世界”假设下工作(这意味着任何人都可以随后出现并为新类型添加实例,并且必须考虑这一点)。这不是必然一件坏事,但在这里它是由于使用类型解决方案而非简单的数据类型解决方案导致的爬行复杂性的一个标志。特别是可以使事情变得更难以思考和使用。

+1

你还需要'OverlappingInstances'来实际使用这个类 - 我觉得这个扩展特别棘手,很难完全掌握。例如。 http://stackoverflow.com/questions/29504107/which-dictionary-does-ghc-choose-when-more-than-one-is-in-scope – chi

+0

@chi感谢您带来了!有一段时间,我认为这是没有必要的,因为'选择'''没有它(即使我原本以为会这样)。但是,如果没有它,不可能使用其他两个实例。我发现这个扩展也很难理解,我绝对认为它增加了额外的复杂性,如果可能的话应该避免。我更新了我的答案,提到它。 –

+0

感谢您提供宝贵的信息。所以如果可能的话,你建议坚持使用数据构造函数,对吧? – mucaho