我已经创建了一个可扩展的记录类型,我怎样才能返回最小的基本类型而不会出现编译器抛出错误。
我有一个类型的记录,
type SectionContent r =
{ titleText :: String
, subTitleText :: String
, ....
| r
}
我正在扩展它,比如
type MyConfig =
{ section1 : SectionContent ()
, section2 : SectionContent (someContent :: {...})
..
}
当我尝试像这样访问我的代码时,
-- This is inside another function & I have access to config
currenScreenConfig :: forall r. SectionContent r
currenScreenConfig = if predicate
then config.section1
else config.section2
它抛出错误,
Could not match type
( someContent :: {...})
with type
r0
.....
where r0 is a rigid type variable
bound at ...
....
刚性类型在这里到底是什么意思?
这是关于谁可以选择通用参数的经典混淆。
forall r.
当您访问(或“引用”或“使用”)其类型前面有 a 的值时,您可以选择是什么r
。并且无论谁实现了该值,都必须使它与r
您选择的值一起使用。不知道你会提前选择什么。
换句话说:选择类型参数的是函数的调用者,而不是实现者。
因此,currenScreenConfig
稍后调用您的函数的人可能会决定选择r ~ ( foo :: String )
. 然后你的函数必须返回一个记录SectionContent ( foo :: String )
。
另一个调用者,在不同的地方,可能会选择( bar :: Int )
,然后你的函数必须返回一个记录SectionContent ( bar :: Int )
。
你看到没有办法实现这样的功能吗?
现在,如果你想返回 just SectionContent ()
,那么函数的类型应该是:
currenScreenConfig :: SectionContent ()
但是当然你不能 return config.section2
,因为它有不同的类型。
有一些方法可以“修剪”记录(即丢弃不需要的字段)。其中一种方法是包中的pick
函数,record-extra
您可以像这样使用它:
currenScreenConfig :: SectionContent ()
currenScreenConfig = if predicate then config.section1 else pick config.section2
但是,我鼓励您改用更自然的模型。如果各种屏幕内容的“共同”部分通常是这样提取的,那么将其作为一个字段包含而不是合并会更自然:
type SectionContentCommon =
{ titleText :: String
, subTitleText :: String
, ....
}
type SectionContent r =
{ common :: SectionContentCommon
| r
}
type MyConfig = ... same ...
currenScreenConfig :: SectionContent ()
currenScreenConfig = if predicate then config.section1.common else config.section2.common
使用可扩展记录可能看起来很酷很闪亮,但我强烈建议您考虑一个更直接的模型。相信我:凉爽和闪亮永远不会超过长期维护。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句