Cardano friendlyValidityRange函数的Haskell语法及传参解析
你看到的写法和*偏函数(partial function)*无关,是Haskell两个核心特性加一个常规语法扩展的组合效果:
- 柯里化(Currying):Haskell中所有多参数函数本质都是单参数函数,类型签名
a -> b -> c实际等价于a -> (b -> c):函数接收第一个a类型参数后,会返回一个新的、接收b类型参数、最终返回c类型结果的函数。这就是两个参数可以"分离"传入的根本原因。 - LambdaCase语法扩展:在文件头部启用
{-# LANGUAGE LambdaCase #-}后,\case就是\param -> case param of的语法糖,不需要显式给lambda的绑定参数命名。
所以你看到的friendlyValidityRange era = \case ...写法,完全等价于下面的常规写法,没有任何特殊逻辑:
friendlyValidityRange era validityRange = case validityRange of ShelleyTtl ttl -> object ["time to live" .= ttl] (lowerBound, upperBound) | isLowerBoundSupported || isUpperBoundSupported -> object [ ... ] | otherwise -> Null where isLowerBoundSupported = isJust $ validityLowerBoundSupportedInEra era isUpperBoundSupported = isJust $ validityUpperBoundSupportedInEra era
等号左边先绑定第一个参数era,等号右边直接返回一个接收第二个参数(有效性范围元组)的函数,这个函数用LambdaCase对传入的第二个参数做模式匹配,where子句里可以直接访问已经绑定的era变量。
你之前写的示例代码问题出在模式匹配的对象搞反了:当前写法里\case匹配的是第二个传入的元组参数,但你写的Child/Adult是第一个参数Age类型的构造器,自然无法匹配。
如果要完全模仿原函数的写法,需要在文件头启用LambdaCase扩展,在LambdaCase里匹配第二个传入的体重元组,同时直接使用已经绑定的age变量,完整可运行代码如下:
{-# LANGUAGE LambdaCase #-} module Main where data Age = Child | Adult deriving (Eq, Show) weightAnalyse :: Age -> (Int, Int) -> String weightAnalyse age = \case -- 直接解构第二个参数传入的(Int, Int)元组 (wMin, wMax) | age == Child -> "儿童适宜体重范围:" ++ show wMin ++ "kg ~ " ++ show wMax ++ "kg,建议保证每日蛋白质摄入" | age == Adult -> "成人适宜体重范围:" ++ show wMin ++ "kg ~ " ++ show wMax ++ "kg,建议每周保持3次以上运动" main :: IO () main = do putStrLn $ weightAnalyse Child (30, 60) putStrLn $ weightAnalyse Adult (60, 130)
运行输出结果为:
儿童适宜体重范围:30kg ~ 60kg,建议保证每日蛋白质摄入 成人适宜体重范围:60kg ~ 130kg,建议每周保持3次以上运动
如果你希望在LambdaCase中直接匹配Age类型的构造器,只需要调整参数绑定顺序即可:不在等号左侧绑定age参数,让LambdaCase匹配第一个Age参数,返回的函数再接收第二个元组参数,写法如下:
weightAnalyse :: Age -> (Int, Int) -> String weightAnalyse = \case Child -> \(wMin, wMax) -> "儿童适宜体重范围:" ++ show wMin ++ "kg ~ " ++ show wMax ++ "kg,建议保证每日蛋白质摄入" Adult -> \(wMin, wMax) -> "成人适宜体重范围:" ++ show wMin ++ "kg ~ " ++ show wMax ++ "kg,建议每周保持3次以上运动"
两种写法执行效果完全一致,只是柯里化下参数绑定的位置选择不同,没有性能或语义上的差别。
补充说明:原Cardano代码中
ShelleyTtl ttl能直接作为元组位置的匹配分支,是因为代码中启用了模式同义词(PatternSynonyms)扩展,将早期Shelley时代仅支持TTL单值的特殊元组形式封装成了ShelleyTtl可匹配模式,不属于基础语法规则。
内容的提问来源于stack exchange,提问作者breeze wang

