2

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

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

4

2 回答 2

5

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

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

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您问题中的类型这样的简单类型。它可能会增加很多复杂性,并且实例解析起初可能会令人困惑。请注意,为了使类型类解决方案起作用,需要打开两个扩展:FlexibleInstances并且TypeSynonymInstances因为String[Char]. OverlappingInstances也是需要的,因为类型类在“开放世界”假设下工作(这意味着任何人以后都可以出现并为新类型添加实例,必须考虑到这一点)。这不一定一件坏事,但这表明使用类型类解决方案而不是更简单的数据类型解决方案导致的复杂性越来越高。OverlappingInstances特别是会使事情更难思考和处理。

于 2015-05-19T18:23:28.613 回答
4

问:你知道有什么方法可以规避吗?Haskell2010、GHC 或任何扩展中是否有允许我使用第一个变体(或类似的东西)的功能?

不,Haskell 2010 中或任何 GHC 扩展都没有提供让您编写类型函数的功能

choice :: a -> Int

其返回值取决于其参数的类型。您也可以指望这种未来永远不会存在的功能。

即使有黑客在运行时检查 GHC 的内部数据表示,也无法区分类型Int值和类型为 newtype 的值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
    
于 2015-05-19T18:49:12.103 回答