Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 使用makeClassy制作具有相同字段名的镜头(TH)_Haskell_Template Haskell_Haskell Lens - Fatal编程技术网

Haskell 使用makeClassy制作具有相同字段名的镜头(TH)

Haskell 使用makeClassy制作具有相同字段名的镜头(TH),haskell,template-haskell,haskell-lens,Haskell,Template Haskell,Haskell Lens,这个问题是关于Edward A.Kmett的(版本4.13) 我有许多不同的数据类型,所有这些类型都有一个字段,表示包含的元素的最大数量(业务规则可能会发生运行时更改,而不是集合实现问题)。我希望在所有情况下都调用此字段容量,但我很快就会遇到名称空间冲突 我在lens文档中看到有一个makeClassy模板,但我找不到我能理解的文档。此模板功能是否允许我使用相同字段名的多个镜头 已编辑: 让我补充一点,我完全有能力围绕这个问题进行编码。我想知道makeClassy是否能解决这个问题。模板是可选

这个问题是关于Edward A.Kmett的(版本4.13)

我有许多不同的
数据
类型,所有这些类型都有一个字段,表示包含的元素的最大数量(业务规则可能会发生运行时更改,而不是集合实现问题)。我希望在所有情况下都调用此字段
容量
,但我很快就会遇到名称空间冲突

我在
lens
文档中看到有一个
makeClassy
模板,但我找不到我能理解的文档。此模板功能是否允许我使用相同字段名的多个镜头


已编辑
让我补充一点,我完全有能力围绕这个问题进行编码。我想知道
makeClassy
是否能解决这个问题。

模板是可选的;你可以自己制作眼镜和镜片

class Capacitor s where
  capacitance :: Lens' s Int
现在,任何具有容量的类型都可以成为此类的实例


另一种方法是考虑容量:

data Luggage a = Luggage { clothes :: a, capacity :: !Int }

在字段名前面加下划线和数据类型名,然后使用
makeFields

data Structure = Structure { _structureCapacity :: Int }
makeFields ''Structure

data OtherStructure = OtherStructure { _otherStructureCapacity :: String }
makeFields ''Structure

我发现文档也有点不清楚;必须通过实验找出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)
然后在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
因此,它实际上是被声明为一个类
具有容量sa
,其中容量是从s到a的镜头(已知s后,a是固定的)。它通过从字段中去掉数据类型的(小写)名称来计算名称“capcity”;我发现在字段名或镜头名上都不必使用下划线是令人愉快的,因为有时候录制语法实际上就是您想要的。您可以使用makeFieldsWith和各种透镜规则来拥有一些用于计算透镜名称的不同选项

如果有帮助,请使用ghci-ddump拼接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.
因此,第一个splace使类HasCapcity并添加了一个Foo实例;第二个使用了现有的类并为Bar创建了一个实例

如果您从另一个模块导入HasCapcity类,这也会起作用;makeFields可以将更多实例添加到现有类中,并将类型分散到多个模块中。但是如果您在另一个没有导入类的模块中再次使用它,它将生成一个新类(具有相同的名称),并且您将拥有两个不兼容的独立重载
容量
镜头


makeClassy有点不同。如果我有:

data Foo
  = Foo { _capacity :: Int }
  deriving (Eq, Show)
$(makeClassy ''Foo)
(请注意,makeClassy更喜欢在字段上使用下划线前缀,而不是数据类型名称)

然后,再次使用-ddump拼接:

[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.
它创建的类是HasFoo,而不是HasCapacity;也就是说,从任何可以得到Foo的地方,你也可以得到Foo的容量。类硬编码capcity是一个
Int
,而不是像使用
makeFields
那样重载它。因此,这仍然有效(因为
HasFoo
,您只需使用
id
即可获得Foo):


但是你不能用这个
capcity
镜头来获得一个不相关类型的容量。

是的,我正沿着这条路走,但是如果模板库能帮我做到这一点,那就不太好了?我不写的代码是我不需要调试的代码。makeFields'结构不会编译(即使启用了TypeSynonymInstances扩展)。原因如下所述:。我发现这相当有限,因为我不能对具有高阶字段的任何数据类型使用
makeFields
*Foo
λ let f = Foo 3
| 
f :: Foo

*Foo
λ f ^. capacity
3
it :: Int