为函数类型实现Match类型类时遭遇GHC类型不匹配错误
嘿,我来帮你拆解这个GHC类型错误的问题!
你的代码
{-# LANGUAGE FlexibleInstances #-} module Match where import Tokens (Token (..)) class Match a where match :: (Match b) => a -> [Token] -> Either String b instance Match (Int -> b) where match = matchInt matchInt :: (Match b) => (Int -> b) -> [Token] -> Either String b matchInt fn (Number i : ts) = match (fn i) ts matchInt _ _ = Left "missing integer argument"
遇到的错误
error: [GHC-25897]
• Couldn't match type ‘b1’ with ‘b’
Expected: (Int -> b) -> [Token] -> Either String b1
Actual: (Int -> b) -> [Token] -> Either String b
‘b1’ is a rigid type variable bound by
the type signature for:
match :: forall b1.
Match b1 =>
(Int -> b) -> [Token] -> Either String b1
at src/Match.hs:11:5-9
错误原因解析
这个问题的核心是你的类型类方法签名设计和实例逻辑不匹配。
你写的match方法签名里,b是一个完全独立的多态类型变量——GHC会认为:对于任意满足Match b的b,match都应该能返回Either String b。但你的matchInt逻辑是固定的:它接受Int -> b,解析出整数后传给函数得到b,再对b调用match,最终返回的是和b对应的结果类型,而不是任意的b1。
简单说,GHC的困惑点是:“你说match能返回任意b1,但你的matchInt只能返回和输入函数输出类型绑定的b,这俩对不上啊!”
解决方案
要解决这个问题,我们需要让match的返回类型和输入的a类型绑定,而不是允许任意的b。这里有两种清晰的修正方式:
方式一:使用关联类型(推荐,逻辑更直观)
修改Match类型类,用关联类型Result a明确每个a对应的返回类型:
{-# LANGUAGE FlexibleInstances, TypeFamilies #-} module Match where import Tokens (Token (..)) class Match a where type Result a match :: a -> [Token] -> Either String (Result a) -- 先给基础类型(比如Int)写个实例作为示例 instance Match Int where type Result Int = Int match _ (Number i : _) = Right i match _ _ = Left "Expected integer" -- 现在函数类型的实例可以正确衔接链式匹配逻辑了 instance Match b => Match (Int -> b) where type Result (Int -> b) = Result b match = matchInt matchInt :: Match b => (Int -> b) -> [Token] -> Either String (Result b) matchInt fn (Number i : ts) = match (fn i) ts matchInt _ _ = Left "missing integer argument"
这样Result (Int -> b)就等于Result b,完美匹配你的逻辑:解析整数传给函数得到b,再匹配b得到最终的Result b。
方式二:用类型族绑定返回类型
如果你不想用关联类型,也可以用类型族直接绑定输入类型和返回类型:
{-# LANGUAGE FlexibleInstances, TypeFamilies #-} module Match where import Tokens (Token (..)) class Match a where match :: a -> [Token] -> Either String (MatchResult a) type family MatchResult a type instance MatchResult Int = Int type instance MatchResult (Int -> b) = MatchResult b instance Match Int where match _ (Number i : _) = Right i match _ _ = Left "Expected integer" instance Match b => Match (Int -> b) where match = matchInt matchInt :: Match b => (Int -> b) -> [Token] -> Either String (MatchResult b) matchInt fn (Number i : ts) = match (fn i) ts matchInt _ _ = Left "missing integer argument"
本质和方式一一致,都是通过类型绑定消除GHC对“任意返回类型”的误解。
备注:内容来源于stack exchange,提问作者azani

