makeLenses和makeFields之间有什么区别?

Bar*_*icz 15 haskell haskell-lens

非常不言自明.我知道makeClassy应该创建类型类,但我发现两者之间没有区别.

PS.用于解释两者默认行为的加分点.

ben*_*ofs 13

注意:此答案基于镜头4.4或更新版本.该版本的TH有一些变化,所以我不知道它有多少适用于老版本的镜头.

组织镜头TH功能

镜头TH功能都基于一个功能makeLensesWith(也称为makeFieldOptics镜头内部).该函数接受一个LensRules参数,该参数准确描述了生成的内容和方式.

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

makeLenses

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))]
         _        -> []
  }
Run Code Online (Sandbox Code Playgroud)

makeFields

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
  }
Run Code Online (Sandbox Code Playgroud)

这些是什么意思?

现在我们知道,区别在于simpleLenses,generateClasses,allowIsosfieldToDef选项.但这些选项究竟意味着什么呢?

  • makeFields绝不会生成变换型光学元件.这由simpleLenses = True选项控制.该选项在当前版本的镜头中没有黑线鳕.但是,镜头HEAD为它添加了文档:

     -- | Generate "simple" optics even when type-changing optics are possible.
     -- (e.g. 'Lens'' instead of 'Lens')
    
    Run Code Online (Sandbox Code Playgroud)

    因此makeFields,makeLenses如果可能的话,永远不会产生变换型光学器件.

  • makeFields将为字段生成类.所以对于每个领域foo,我们都有一个类:

    class HasFoo t where
      foo :: Lens' t <Type of foo field>
    
    Run Code Online (Sandbox Code Playgroud)

    这由generateClasses选项控制.

  • makeFields将永远不会生成Iso's,即使这是可能的(由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
    
    Run Code Online (Sandbox Code Playgroud)

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

    这是所有由受控_fieldToDef(导出为lensField通过Control.Lens.TH).

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