如何避免OverloadedStrings下Escaping类型的类型作用域问题?
解决Haskell中OverloadedStrings下esc1/esc2的类型标注问题
这个问题我之前也碰到过,核心原因是OverloadedStrings扩展让字符串字面量变成了多态值(属于IsString类的任意类型),而你定义的Escaping实例又太宽泛——所有能转换成String的类型都能实现Escaping,编译器根本不知道该把"a"推断成哪种类型(比如String、Text甚至自定义类型都有可能),所以必须加类型标注来消除歧义。
这个需求完全可行,下面是几种有效的解决方法:
方法1:添加更具体的Escaping实例(推荐)
给String单独定义一个Escaping实例,并且让泛型实例成为“可重叠”的备选。这样编译器会优先选择更具体的String实例,自动把"a"推断为String类型,无需手动标注。
修改后的代码如下:
instance IsString Escaped where fromString a = Escaped a instance ConvertibleStrings URI.URI String where convertString = show data Escaped = forall a. ConvertibleStrings a String => Escaped a instance Show Escaped where show (Escaped a) = cs a class Escaping a where esc1 :: a -> Escaped; esc2 :: a -> Escaped -- 先定义针对String的具体实例,优先级最高 instance Escaping String where esc1 = Escaped . escape1 esc2 = Escaped . escape2 -- 保留泛型实例,但添加OVERLAPPABLE pragma,让它只在没有更具体实例时生效 instance {-# OVERLAPPABLE #-} ConvertibleStrings a String => Escaping a where esc1 a = Escaped $ escape1 $ cs a esc2 a = Escaped $ escape2 $ cs a
这样你就可以直接写[esc1 "a", esc2 "b"],编译器会自动把字面量推断为String,不需要加::String标注。
方法2:定义专门针对字符串字面量的辅助函数
如果不想修改现有实例结构,可以定义两个辅助函数,明确接受IsString类型并转换为String:
-- 辅助函数:直接处理IsString类型,内部转换成String esc1Str :: IsString s => s -> Escaped esc1Str = esc1 . cs esc2Str :: IsString s => s -> Escaped esc2Str = esc2 . cs
之后使用[esc1Str "a", esc2Str "b"]即可,同样不需要手动标注类型。
为什么DefaultSignatures(DS)扩展没用?
你提到尝试过DS扩展但无效,这很正常——DefaultSignatures是用来给类提供默认实现的,而你的问题根本不是缺少默认实现,而是类型推断的歧义:编译器有太多可能的类型可以匹配"a",DS扩展无法帮它缩小选择范围,所以解决不了这个问题。
内容的提问来源于stack exchange,提问作者RandomB
相关产品推荐
相关产品推荐

