我有一个类型为Read a => String -> a
的函数,当a
例如String
时,是否可以使用另一个具有不同功能的函数?是否有任何GHC扩展允许这个?
类似的东西:
f :: Read a => String -> a
f = read
f :: String -> String
f = id
答案 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)