尝试 return 可扩展记录时出现编译类型错误

Compile type error when trying to return Extendable record

我已经创建了一个可扩展的记录类型,我如何才能 return 最小基类型而不让编译器抛出错误。

我有一个类型的记录,

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 ...
  ....

这里的 rigid 类型到底是什么意思?

这是关于谁可以选择通用参数的经典混淆。

当您访问(或“引用”或“使用”)其类型前面带有 forall r. 的值时,您可以选择 r 是什么。实施该值的人必须使其能够与您选择的 r 一起使用。事先不知道你会选择什么

换句话说:选择类型参数的是函数的调用者,而不是实现者

因此,稍后调用您的 currenScreenConfig 函数的人可能会决定选择 r ~ ( foo :: String )。然后你的函数必须 return 一条记录 SectionContent ( foo :: String ).

另一个调用者,在不同的地方,可能会选择 ( bar :: Int ),然后你的函数必须 return 一条记录 SectionContent ( bar :: Int ).

你知道怎么没有办法实现这样的功能吗?


现在,如果您只想 return SectionContent (),那么函数的类型应该是:

currenScreenConfig :: SectionContent ()

当然你不能 return config.section2,因为它有不同的类型。


有一些方法可以“trim”一条记录(即丢弃不需要的字段)。一种这样的方法是 record-extra 包中的 the pick function,您可以像这样使用它:

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

使用可扩展记录可能看起来很酷很闪亮,但我强烈建议您考虑使用更直接的模型。相信我:炫酷和闪亮永远不会超过 long-term 维护。