如何让Haskell函数条件性依赖类型约束?
让Haskell的logFields仅在需要时要求Show约束?
问题背景
初始代码中,traverseRecord无类型约束,logFields调用它可以正常运行:
-- 初始实现 traverseRecord :: forall a. a -> String traverseRecord record = "traversed" logFields :: forall a. a -> String logFields record = "logged: " <> traverseRecord record
但当给traverseRecord添加Show a约束后:
traverseRecord :: forall a. Show a => a -> String traverseRecord record = "traversed: " <> show record
logFields会因为缺少Show a约束报错,直接给logFields加Show a约束可以解决问题,但希望让logFields仅在traverseRecord需要时才要求该约束,而非固定添加。
解决方案
方案1:动态依赖遍历函数的约束(推荐)
把traverseRecord作为参数传入logFields,让logFields的类型约束完全依赖于传入的遍历函数,这样当遍历函数需要Show a时,logFields才会要求该约束;当遍历函数无约束时,logFields也不需要:
logFields :: forall a. (a -> String) -> a -> String logFields traverser record = "logged: " <> traverser record
使用示例:
- 调用无约束的
traverseRecord:-- 即使传入无Show实例的类型也能运行(只要traverseRecord支持) logFields traverseRecord (MyUnshowableType 42) - 调用带
Show a约束的traverseRecord:-- 自动要求Show a约束,匹配traverseRecord的要求 logFields traverseRecord "test"
方案2:固定绑定traverseRecord的约束
如果traverseRecord是固定的、无法作为参数传入,那必须给logFields添加对应的Show a约束——这是Haskell类型系统的要求:调用有约束的函数时,调用者必须满足该约束:
logFields :: forall a. Show a => a -> String logFields record = "logged: " <> traverseRecord record
这种方式下,logFields的约束和traverseRecord完全绑定,当traverseRecord的约束变化时,logFields需要同步修改。
内容的提问来源于stack exchange,提问作者Saravanan
相关产品推荐
相关产品推荐

