Haskell类型不匹配错误:直接传getBloodType调用为何报错?
Haskell函数调用语法导致的类型匹配错误问题
以下代码可正常运行:
type FirstName = String type MiddleName = String type LastName = String data Name = Name FirstName LastName | NameWithMiddle FirstName MiddleName LastName data Sex = Male | Female showName :: Name -> String showName (Name f l) = f ++ " " ++ l showName (NameWithMiddle f m l) = f ++ " " ++ m ++ " " ++ l data RhType = Pos | Neg data ABOType = A | B | AB | O data BloodType = BloodType ABOType RhType showRh :: RhType -> String showRh Pos = "+" showRh Neg = "-" showABO :: ABOType -> String showABO A = "A" showABO B = "B" showABO AB = "AB" showABO O = "O" showBloodType :: BloodType -> String showBloodType (BloodType abo rh) = showABO abo ++ showRh rh canDonateTo :: BloodType -> BloodType -> Bool canDonateTo (BloodType O _) _ = True canDonateTo _ (BloodType AB _) = True canDonateTo (BloodType A _) (BloodType A _) = True canDonateTo (BloodType B _) (BloodType B _) = True canDonateTo _ _ = False --otherwise data Patient = Patient Name Sex Int Int Int BloodType johnDoe :: Patient johnDoe = Patient (Name "John" "Doe") Male 30 74 200 (BloodType AB Pos) janeESmith :: Patient janeESmith = Patient (NameWithMiddle "Jane" "Elizabeth" "Smith") Female 28 62 140 (BloodType AB Pos) getName :: Patient -> Name getName (Patient n _ _ _ _ _) = n getAge :: Patient -> Int getAge (Patient _ _ a _ _ _) = a getBloodType :: Patient -> BloodType getBloodType (Patient _ _ _ _ _ bt) = bt main = do let johnDoeBloodType = getBloodType(johnDoe) let janeESmithBloodType = getBloodType(janeESmith) print(canDonateTo (BloodType AB Pos) (BloodType AB Pos)) print(canDonateTo johnDoeBloodType janeESmithBloodType)
将main中的print(canDonateTo johnDoeBloodType janeESmithBloodType)替换为print(canDonateTo getBloodType(johnDoe) getBloodType(janeESmith))时,会抛出以下错误:
Couldn't match expected type ‘BloodType’ with actual type ‘Patient’
已知相关函数类型签名:
canDonateTo :: BloodType -> BloodType -> Bool getBloodType :: Patient -> BloodType
单独调用getBloodType(johnDoe)并赋值给变量时,其类型能被正确识别为BloodType,但直接作为canDonateTo的参数时却被视为Patient类型,请问问题成因是什么?
问题成因
核心原因是Haskell的函数调用语法规则与常见命令式语言不同:
- Haskell中函数调用不需要用括号包裹参数,仅通过空格分隔即可;括号的唯一作用是改变表达式优先级,而非标记函数调用。
- 你写的
canDonateTo getBloodType(johnDoe) getBloodType(janeESmith)会被Haskell解析为:
也就是把canDonateTo getBloodType johnDoe getBloodType janeESmithgetBloodType、johnDoe、getBloodType、janeESmith四个值依次作为参数传递给canDonateTo。但canDonateTo的类型是BloodType -> BloodType -> Bool(柯里化特性下,它先接收一个BloodType参数,返回一个接收BloodType参数的函数),类型匹配逻辑因此混乱:- 第一个参数
getBloodType的类型是Patient -> BloodType,但canDonateTo要求第一个参数是BloodType,这迫使Haskell推导BloodType必须等于Patient -> BloodType(即BloodType是函数类型)。 - 第二个参数
johnDoe是Patient类型,而canDonateTo返回的函数要求参数是BloodType,这就需要Patient等于BloodType,最终抛出“预期BloodType,实际是Patient”的类型错误。
- 第一个参数
正确写法
需要用括号明确包裹getBloodType johnDoe的执行结果,确保它作为单个参数传递给canDonateTo:
print(canDonateTo (getBloodType johnDoe) (getBloodType janeESmith))
或者遵循Haskell惯用写法,去掉冗余括号,用$运算符简化嵌套:
print $ canDonateTo (getBloodType johnDoe) (getBloodType janeESmith)
内容的提问来源于stack exchange,提问作者Ahmad Ismail
相关产品推荐
相关产品推荐

