haskell - makeLenses 和 makeFields 有什么区别?

标签 haskell haskell-lens

很不言自明。我知道makeClassy应该创建类型类,但我认为两者之间没有区别。

PS。解释两者的默认行为的奖励积分。

最佳答案

注意:此答案基于镜头 4.4 或更高版本。那个版本的 TH 有一些变化,所以我不知道它有多少适用于旧版本的镜头。

镜头 TH 功能的组织

镜头 TH 函数都基于一个函数 makeLensesWith (也称为 makeFieldOptics 内部镜头)。这个函数需要一个 LensRules参数,它准确地描述了生成什么以及如何生成。

所以比较makeLensesmakeFields ,我们只需要比较LensRules他们使用的。您可以通过查看 source 找到它们。 :

制作镜头

lensRules :: LensRules
lensRules = LensRules
  { _simpleLenses    = False
  , _generateSigs    = True
  , _generateClasses = False
  , _allowIsos       = True
  , _classyLenses    = const Nothing
  , _fieldToDef      = \_ n ->
       case nameBase n of
         '_':x:xs -> [TopName (mkName (toLower x:xs))]
         _        -> []
  }

生成字段
defaultFieldRules :: LensRules
defaultFieldRules = LensRules
  { _simpleLenses    = True
  , _generateSigs    = True
  , _generateClasses = True  -- classes will still be skipped if they already exist
  , _allowIsos       = False -- generating Isos would hinder field class reuse
  , _classyLenses    = const Nothing
  , _fieldToDef      = camelCaseNamer
  }

这些是什么意思?

现在我们知道不同之处在于 simpleLenses , generateClasses , allowIsosfieldToDef选项。但这些选项实际上意味着什么?
  • makeFields永远不会产生改变类型的光学元件。这由 simpleLenses = True 控制。选项。该选项在当前版本的镜头中没有黑线鳕。但是,lens HEAD 为其添加了文档:
     -- | Generate "simple" optics even when type-changing optics are possible.
     -- (e.g. 'Lens'' instead of 'Lens')
    

    所以makeFields makeLenses 永远不会产生类型改变的光学元件如果可能的话。
  • makeFields将为字段生成类。所以对于每个字段 foo ,我们有一个类:
    class HasFoo t where
      foo :: Lens' t <Type of foo field>
    

    这由 generateClasses 控制。选项。
  • makeFields永远不会生成 Iso的,即使这是可能的(由 allowIsos 选项控制,它似乎不是从 Control.Lens.TH 导出的)
  • makeLenses只需为以下划线开头的每个字段生成一个顶级镜头(下划线后的第一个字母小写),makeFields而是为 HasFoo 生成实例类。它还使用了不同的命名方案,在源代码的注释中进行了解释:
    -- | Field rules for fields in the form @ prefixFieldname or _prefixFieldname @
    -- If you want all fields to be lensed, then there is no reason to use an @_@ before the prefix.
    -- If any of the record fields leads with an @_@ then it is assume a field without an @_@ should not have a lens created.
    camelCaseFields :: LensRules
    camelCaseFields = defaultFieldRules
    

    所以makeFields还期望所有字段不仅带有下划线前缀,而且还包括数据类型名称作为前缀(如 data Foo = { _fooBar :: Int, _fooBaz :: Bool } 中)。如果要为所有领域生成镜头,可以省略下划线。

    这一切都由 _fieldToDef 控制。 (由 lensField 导出为 Control.Lens.TH)。

  • 如您所见,Control.Lens.TH模块非常灵活。使用 makeLensesWith ,您可以创建自己的LensRules如果您需要标准功能未涵盖的模式。

    关于haskell - makeLenses 和 makeFields 有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25585650/

    相关文章:

    list - Haskell 列表和 Control.Lens

    Haskell,控制.Lens : Couldn't match type ‘[Language.Haskell.TH.Syntax.Dec]’ with ‘Language.Haskell.TH.Syntax.Exp’

    haskell - 编写更复杂的遍历(镜头)

    scala - Scala 中的 forall

    unit-testing - 测试haskell io的自定义数据类型

    haskell - 将计算与状态单子(monad)绑定(bind)?

    parsing - 除了 aeson 还能怎样?

    haskell - 如何使用镜头在嵌套 map 中设置值

    Haskell这个文件的路径

    haskell - 为遍历和折叠实现多态 'deep' 函数