多态参数类型的模式匹配 - 替代方案

时间:2015-05-19 17:50:50

标签: haskell pattern-matching polymorphic-functions

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

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或允许我使用第一个变体(或类似的东西)的任何扩展中是否有一个功能?

2 个答案:

答案 0 :(得分:5)

这使两种不同的多态性混淆。你想要的是 ad-hoc多态,这是通过类型类完成的。类型a -> Int的函数的多态性类型是参数多态性。使用参数多态,choice的一个函数定义必须适用于任何可能的类型a。在这种情况下,这意味着它实际上不能使用类型a的值,因为它对它没有任何了解,因此choice必须是一个常量函数,例如{{1} }。这实际上为您提供了关于函数可以做什么的非常有力的保证,只需查看它的类型(此属性称为 parametricity )。

使用类型类,您可以将您的示例实现为:

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 类型的简单类型。它可能会增加许多复杂性,实例解析可能会让人感到困惑。请注意,为了使类型类解决方案起作用,需要启用两个扩展:ChoiceFlexibleInstances,因为TypeSynonymInstancesString的同义词。还需要[Char]因为类型类在“开放世界”假设上工作(意味着任何人都可以在以后出现并为新类型添加实例,并且必须考虑这一点)。这不是必然是坏事,但在这里它表明了使用类型类解决方案而不是更简单的数据类型解决方案所导致的复杂性。 OverlappingInstances尤其会让人更难以思考和使用。

答案 1 :(得分:4)

  

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

不,Haskell 2010中没有任何功能,也没有任何GHC扩展提供的功能可以让你编写类型

的功能
choice :: a -> Int

其返回值取决于其参数的类型。您可以指望未来从未存在的此类功能。

即使有hacks在运行时检查GHC的内部数据表示,也不可能将类型Int的值与类型为Int的新类型的值区分开来:这些类型具有相同的运行时表示。

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

  1. 普通值,例如data Choice a = IntChoice Int | StringChoice String | OtherChoice a; choice :: Choice a -> Int示例或

  2. 一个类型类字典,使用David Young的答案中的自定义类或内置的Typeable类:

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