You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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 janeESmith
    
    也就是把getBloodType、johnDoe、getBloodType、janeESmith四个值依次作为参数传递给canDonateTo。但canDonateTo的类型是BloodType -> BloodType -> Bool(柯里化特性下,它先接收一个BloodType参数,返回一个接收BloodType参数的函数),类型匹配逻辑因此混乱:
    1. 第一个参数getBloodType的类型是Patient -> BloodType,但canDonateTo要求第一个参数是BloodType,这迫使Haskell推导BloodType必须等于Patient -> BloodType(即BloodType是函数类型)。
    2. 第二个参数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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 14:30:59