Joh*_*ler 4 haskell template-haskell haskell-lens
这个问题是关于Edward A. Kmett的镜头包(版本4.13)
我有许多不同的data类型,所有类型都有一个字段,表示包含的最大元素数(业务规则受运行时更改,而不是集合实现问题.)我想capacity在所有情况下调用此字段,但我很快遇到命名空间冲突.
我在lens文档中看到有一个makeClassy模板,但我无法找到我理解的文档.这个模板功能是否允许我有多个具有相同字段名称的镜头?
EDITED:让我补充一点,我完全能够编码的周围的问题.我想知道是否makeClassy能解决问题.
我发现文档也有点不清楚; 必须弄清楚Control.Lens.TH通过实验做了多少事.
你想要的是makeFields:
{-# LANGUAGE FunctionalDependencies
, MultiParamTypeClasses
, TemplateHaskell
#-}
module Foo
where
import Control.Lens
data Foo
= Foo { fooCapacity :: Int }
deriving (Eq, Show)
$(makeFields ''Foo)
data Bar
= Bar { barCapacity :: Double }
deriving (Eq, Show)
$(makeFields ''Bar)
Run Code Online (Sandbox Code Playgroud)
然后在ghci:
*Foo
? let f = Foo 3
| b = Bar 7
|
b :: Bar
f :: Foo
*Foo
? fooCapacity f
3
it :: Int
*Foo
? barCapacity b
7.0
it :: Double
*Foo
? f ^. capacity
3
it :: Int
*Foo
? b ^. capacity
7.0
it :: Double
? :info HasCapacity
class HasCapacity s a | s -> a where
capacity :: Lens' s a
-- Defined at Foo.hs:14:3
instance HasCapacity Foo Int -- Defined at Foo.hs:14:3
instance HasCapacity Bar Double -- Defined at Foo.hs:19:3
Run Code Online (Sandbox Code Playgroud)
所以它实际上做了什么被宣布为一个类HasCapacity s a,其中容量是从s到a的透镜'(a一旦知道就固定).它通过从字段中剥离数据类型的(小写的)名称来找出名称"capcity"; 我觉得不必在字段名称或镜头名称上使用下划线,因为有时记录语法实际上就是你想要的.您可以使用makeFieldsWith和各种lensRules来计算镜头名称.
如果它有帮助,使用ghci -ddump-splices Foo.hs:
[1 of 1] Compiling Foo ( Foo.hs, interpreted )
Foo.hs:14:3-18: Splicing declarations
makeFields ''Foo
======>
class HasCapacity s a | s -> a where
capacity :: Lens' s a
instance HasCapacity Foo Int where
{-# INLINE capacity #-}
capacity = iso (\ (Foo x_a7fG) -> x_a7fG) Foo
Foo.hs:19:3-18: Splicing declarations
makeFields ''Bar
======>
instance HasCapacity Bar Double where
{-# INLINE capacity #-}
capacity = iso (\ (Bar x_a7ne) -> x_a7ne) Bar
Ok, modules loaded: Foo.
Run Code Online (Sandbox Code Playgroud)
所以第一个splace创建了HasCapcity类并为Foo添加了一个实例; 第二个使用现有的类并为Bar创建了一个实例.
如果从另一个模块导入HasCapcity类,这也有效; makeFields可以向现有类添加更多实例,并将您的类型分散到多个模块中.但是如果你在没有导入类的另一个模块中再次使用它,它将创建一个新的类(具有相同的名称),并且你将有两个capacity不兼容的重载镜头.
makeClassy有点不同.如果我有:
data Foo
= Foo { _capacity :: Int }
deriving (Eq, Show)
$(makeClassy ''Foo)
Run Code Online (Sandbox Code Playgroud)
(注意到makeClassy更喜欢在字段上使用下划线前缀,而不是数据类型名称)
然后,再次使用-ddump-splices:
[1 of 1] Compiling Foo ( Foo.hs, interpreted )
Foo.hs:14:3-18: Splicing declarations
makeClassy ''Foo
======>
class HasFoo c_a85j where
foo :: Lens' c_a85j Foo
capacity :: Lens' c_a85j Int
{-# INLINE capacity #-}
capacity = (.) foo capacity
instance HasFoo Foo where
{-# INLINE capacity #-}
foo = id
capacity = iso (\ (Foo x_a85k) -> x_a85k) Foo
Ok, modules loaded: Foo.
Run Code Online (Sandbox Code Playgroud)
它创建的类是HasFoo,而不是HasCapacity; 它说任何你可以获得Foo的东西,你也可以获得Foo的能力.并且该类硬编码表示城市是一个Int,而不是像你一样超载它makeFields.所以这仍然有效(因为HasFoo Foo,你只是通过使用获得Foo id):
*Foo
? let f = Foo 3
|
f :: Foo
*Foo
? f ^. capacity
3
it :: Int
Run Code Online (Sandbox Code Playgroud)
但是你不能使用这个capcity镜头来获得不相关类型的容量.