Haskell中能否定义由类型a参数化的泛型类型类实例?
嘿,这个问题问得挺有意思的——答案是完全可以,而且你写的代码其实已经是合法的Haskell了,甚至还能简化!
你给出的示例代码在GHC(Haskell的主流编译器)里完全可以正常编译运行,甚至那个显式的forall a .都可以省略,因为Haskell默认会为实例隐式添加这个全称量词。
简化后的代码长这样:
data Box a = Box a deriving Show class Boxable a where create :: a -> Box a -- 省略forall a. 效果完全一样 instance Boxable a where create = Box
你可以随便测试一下,比如输入create 5 :: Box Int会输出Box 5,输入create "hi" :: Box String会输出Box "hi",完全符合预期——毕竟Box构造器的类型就是a -> Box a,正好完美匹配create的签名。
Haskell允许定义这种“泛型类型类实例”,也就是给所有满足约束(这里没有约束)的类型都提供同一个实现。你的Boxable类的参数是类型a,而你给每一个可能的a都实现了create方法,只要这个实现对所有类型都适用(显然这里Box构造器对任何a都能 work),编译器就不会有意见。
当然,如果之后你给某个具体类型(比如Int)写一个更特殊的Boxable实例,会触发“重叠实例”的警告/错误,但那是另一个关于实例优先级的话题了,和你问的“泛型实例是否可行”无关。
forall的小补充 你写的instance forall a . Boxable a是显式声明这个实例对所有a生效,在Haskell中,当你省略这个forall时,编译器会自动为你补上,所以两种写法是等价的。显式写forall更多是在处理Rank-N类型这类复杂场景时才有用,在这个简单例子里完全没必要。
内容的提问来源于stack exchange,提问作者Niklas Vest

