Haskell中的函数类型特化

时间:2012-03-27 00:21:34

标签: haskell types ghc generic-programming

我有一个类型为Read a => String -> a的函数,当a例如String时,是否可以使用另一个具有不同功能的函数?是否有任何GHC扩展允许这个?

类似的东西:

f :: Read a => String -> a
f = read

f :: String -> String
f = id

2 个答案:

答案 0 :(得分:12)

在Haskell中,这种函数重载(ad-hoc多态)是通过使用类型类来完成的,而不是通过在多个类型下绑定相同的名称来实现的。

{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}

class F a where f :: String -> a

instance F String where f = id
instance F Int where f = read
instance F Char where f = read
instance F Float where f = read
-- etc.

现在,f可以对已声明F个实例的任何类型进行操作。

不幸的是,你无法逃避以下事情:

instance Read a => F a where f = read

也许不直观的是, not 仅为具有F实例的类型声明Read的实例。因为GHC仅使用实例声明的头部(=>右侧的部分)来解析实例,所以这实际上声明所有类型a都是F的实例,但是在任何不属于f的实例的任何内容上调用Read的类型错误。

如果您启用UndecidableInstances扩展程序,它将进行编译,但这只会导致其他问题。这是一个你真不想冒险的兔子洞。

相反,您应为您希望F操作的每个类型声明f的实例。对于像这样的简单类来说,这不是很麻烦,但是如果你使用最新版本的GHC,你可以使用以下内容使它更容易:

{-# LANGUAGE DefaultSignatures #-}

class F a where f :: String -> a
                default f :: Read a => String -> a
                f = read

现在,对于Read实例的任何类型,您可以声明其F实例,而无需明确提供f的实现:

instance F Int
instance F Char
instance F Float
-- etc.

对于任何类型的没有 Read的实例,您仍然需要为f编写显式实现。

答案 1 :(得分:4)

我得到了它的工作,但我不得不打开一堆可疑的语言选项:

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE OverlappingInstances #-}

class SpecialRead a where
  specialRead :: String -> a

instance Read a => SpecialRead a where
  specialRead = read

instance SpecialRead String where
  specialRead = id

main = do
    print (specialRead "10" :: Int)
    print (specialRead "10" :: String)