Haskell类型同义词未使用参数是否为幻影类型?GHC无告警疑问
问题解答
1. 此处b的类型属性
这里的b同样属于幻影类型参数,和代数数据类型里的幻影类型逻辑一致:没有出现在类型定义的右侧,仅承担类型层面的标记作用。唯一区别是类型同义词会在编译期完全展开,你写的T a b最终会被GHC直接等价为Maybe a,b不会参与值的构造,也不会保留任何运行时信息。
2. 应用场景
这类带未使用参数的类型同义词,实际使用场景非常广:
- 无需
newtype开销即可区分逻辑不同、底层结构一致的类型:比如你可以定义type Id b = Int,再分别声明type UserId = Id 'User、type OrderId = Id 'Order,哪怕底层都是Int类型,GHC也会把UserId和OrderId判定为完全不同的类型,传参写错时直接编译报错,完全没有额外的包装、解包开销。 - 支持同一种底层类型实现多份类型类实例:比如你想给
Maybe Int写两份不同的Semigroup实例(一份取数值和、一份取数值乘积),就可以通过不同的b参数区分,给T Int 'Sum、T Int 'Product分别写不同的实例,不需要额外嵌套newtype。 - 提前做类型约束校验:可以给未使用的参数加类型类约束,比如写
type T a b = Show b => Maybe a,只有b满足Show约束时这个类型同义词才合法,能在编译期提前过滤不符合要求的使用场景。
3. 要求GHC默认告警的合理性
这个想法并不完全合理,原因如下:
- 这种未使用的类型参数是Haskell的合法用法,大量依赖幻影类型的工程、库都是基于这个特性实现的,如果
-Wall默认告警,反而会把大量正常的业务代码、库代码标记为有问题,干扰开发。 - GHC本身已经提供了专门的告警选项
-Wunused-type-patterns来检测未使用的类型参数,如果你自己的项目需要避免误写未使用的类型参数,可以手动开启这个选项,不需要放到通用的-Wall规则里。 - 类型同义词的未使用参数不会产生任何运行时开销,也不会导致隐性的类型错误:如果你是不小心多写了参数,调用的时候参数数量不对,GHC本来就会直接抛出类型不匹配的错误,不会留到运行时出问题。
内容的提问来源于stack exchange,提问作者AntC
相关产品推荐
相关产品推荐

