使用关联类型族建模x86汇编时,如何定义多态函数?
解决关联类型族中多态函数的类型歧义问题
我正在学习关联类型族(associated type families),并以建模x86汇编语言的一小部分作为练习。以下代码可正常编译:
{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE UndecidableInstances #-} module Instr where import Data.Kind(Type) class Reg r where type RegTy r :: Type rsp :: r rbp :: r instance Reg String where type RegTy String = String rsp = "%rsp" rbp = "%rbp" printReg :: String -> String printReg = id class Reg r => Src r s where type SrcTy r s :: Type srcReg :: Reg r => r -> s srcAddr :: Reg r => Int -> r -> s srcImm :: Int -> s instance Reg r => Src String String where type SrcTy String String = String srcReg reg = printReg reg srcAddr n r = concat ["(", show n, ")", r] srcImm n = '$' : show n
尝试为src...类函数定义示例时遇到类型歧义错误:
src01 :: (Reg r, Src r s) => SrcTy r s src01 = srcReg rsp src02 :: (Reg r, Src r s) => s src02 = srcAddr 42 rsp
报错信息:The type variable ‘r0’ is ambiguous
尝试添加类型应用后又报错:
src01 :: (Reg r, Src r s) => SrcTy r s src01 = srcReg @r rsp
报错信息:Not in scope: type variable ‘r’
解决方案
问题核心是编译器无法自动推导未约束的类型变量,且你尝试的类型应用方式有误——函数签名里的r是隐式量化的变量,不能直接在函数体内用@r引用。可以通过以下几种方式解决:
1. 显式指定具体类型(最直接)
如果示例是针对String类型的寄存器实现,直接固定类型即可:
src01 :: String src01 = srcReg @String rsp src02 :: String src02 = srcAddr @String 42 rsp
2. 启用作用域类型变量保持多态性
如果想保留函数的多态能力,需要启用ScopedTypeVariables扩展,显式量化类型变量,让函数体内的类型应用能关联到签名中的变量:
{-# LANGUAGE ScopedTypeVariables #-} src01 :: forall r s. (Reg r, Src r s) => SrcTy r s src01 = srcReg @r rsp src02 :: forall r s. (Reg r, Src r s) => s src02 = srcAddr @r 42 rsp
3. 调整类型类设计消除歧义
如果希望编译器自动推导类型,可以修改Src类的约束逻辑,让s类型能唯一确定r类型,或者添加额外的关联类型辅助推导,但这种方式会增加设计复杂度,适合更复杂的场景。
内容的提问来源于stack exchange,提问作者Jogger
相关产品推荐
相关产品推荐

