Haskell 中的扩展结构
Extending structures in Haskell
我想实现一些具有一组通用参数的算法(实际上这是一个很大的数字,这就是为什么我没有将它们单独传递到函数中的原因):
data Parameters = Parameters {
_p1 :: A,
...
}
但他们每个人都有 - 除了这个通用集 - 一组只有他们知道如何使用的参数:
data AlgorithmAParameters = AlgorithmAParameters {
_commonParameters :: Parameters,
_myp1 :: B
}
这里的问题是如何编写地道的代码。我目前正在使用镜头,所以我可以定义
p1 :: Lens' AlgorithmAParameters A
p1 = commonParameters . Common.p1
这让我可以像只使用 Parameters
一样访问所有内容。问题是我必须为每个保留自己的参数集的算法执行此操作,并且我必须小心地分别导入这些参数。
我可以更进一步,使用类型 classes
class Parameters p where
p1 :: Lens' p A
...
然后分别实现
class AlgorithmAParameters p where
p1 :: Lens' p A
myp1 :: Lens' p B
与 AlgorithmAParameters p => AlgorithmParameters p
实例一起。但是,这具有相同类型的问题(重复代码)并最终导致代码与第一个选项一样具有误导性(加上类型 class 中的整个 Lens'
不是很有用)。
有没有更简单的方法来解决这个问题?
您要查找的是 Control.Lens.TH
中的 makeClassy
。阅读有关其对字段名称的假设的文档。 (如果您无法更改您的字段名称以匹配查看朋友 makeClassyFor
和 makeClassy_
)
这里的想法是,模板创建一个 class 的事物,其中包含您的 commonParameters
并将您的数据结构添加为该 class 的实例。当许多数据结构具有相同的字段时,它们都将属于相同的 class。然后,您可以在镜头配件中使用 class。
作为编程笔记,我倾向于使用只是 commonParameters
和一个合理的名称来制作一个通用结构,这样我就可以参考class由 TH 使用 HasFoo
约定创建。
classy lenses/optics 技巧在这里很有用。
data CommonParameters = CommonParameters
{ _p1 :: A
}
makeClassy ''CommonParameters
makeClassy
模板 Haskell 指令将导致以下 class 和实例:
class HasCommonParameters a where
commonParameters :: Lens' a CommonParameters
p1 :: Lens' a A
p1 = ... -- default implementation
instance HasCommonParameters CommonParameters where
commonParameters = id
然后 AlgorithmParameters
data AlgorithmParameters = AlgorithmParameters
{ _algCommonParameters :: CommonParameters
, _myp1 :: B
}
makeClassy ''AlgorithmParameters
再次 makeClassy
做它的事情:
class HasAlgorithmParameters a where
algorithmParameters :: Lens' a AlgorithmParameters
algCommonParameters :: Lens' a CommonParameters
algCommonParameters = ... -- default implementation
myp1 :: Lens' a B
myp1 = ... -- default implementation
instance HasAlgorithmParameters AlgorithmParameters where
algorithmParameters = id
现在,您可以将 CommonParameters
光学元件与
AlgorithmParameters
类型,定义如下实例:
instance HasCommonParameters AlgorithmParameters where
commonParameters = algCommonParameters
我想实现一些具有一组通用参数的算法(实际上这是一个很大的数字,这就是为什么我没有将它们单独传递到函数中的原因):
data Parameters = Parameters {
_p1 :: A,
...
}
但他们每个人都有 - 除了这个通用集 - 一组只有他们知道如何使用的参数:
data AlgorithmAParameters = AlgorithmAParameters {
_commonParameters :: Parameters,
_myp1 :: B
}
这里的问题是如何编写地道的代码。我目前正在使用镜头,所以我可以定义
p1 :: Lens' AlgorithmAParameters A
p1 = commonParameters . Common.p1
这让我可以像只使用 Parameters
一样访问所有内容。问题是我必须为每个保留自己的参数集的算法执行此操作,并且我必须小心地分别导入这些参数。
我可以更进一步,使用类型 classes
class Parameters p where
p1 :: Lens' p A
...
然后分别实现
class AlgorithmAParameters p where
p1 :: Lens' p A
myp1 :: Lens' p B
与 AlgorithmAParameters p => AlgorithmParameters p
实例一起。但是,这具有相同类型的问题(重复代码)并最终导致代码与第一个选项一样具有误导性(加上类型 class 中的整个 Lens'
不是很有用)。
有没有更简单的方法来解决这个问题?
您要查找的是 Control.Lens.TH
中的 makeClassy
。阅读有关其对字段名称的假设的文档。 (如果您无法更改您的字段名称以匹配查看朋友 makeClassyFor
和 makeClassy_
)
这里的想法是,模板创建一个 class 的事物,其中包含您的 commonParameters
并将您的数据结构添加为该 class 的实例。当许多数据结构具有相同的字段时,它们都将属于相同的 class。然后,您可以在镜头配件中使用 class。
作为编程笔记,我倾向于使用只是 commonParameters
和一个合理的名称来制作一个通用结构,这样我就可以参考class由 TH 使用 HasFoo
约定创建。
classy lenses/optics 技巧在这里很有用。
data CommonParameters = CommonParameters
{ _p1 :: A
}
makeClassy ''CommonParameters
makeClassy
模板 Haskell 指令将导致以下 class 和实例:
class HasCommonParameters a where
commonParameters :: Lens' a CommonParameters
p1 :: Lens' a A
p1 = ... -- default implementation
instance HasCommonParameters CommonParameters where
commonParameters = id
然后 AlgorithmParameters
data AlgorithmParameters = AlgorithmParameters
{ _algCommonParameters :: CommonParameters
, _myp1 :: B
}
makeClassy ''AlgorithmParameters
再次 makeClassy
做它的事情:
class HasAlgorithmParameters a where
algorithmParameters :: Lens' a AlgorithmParameters
algCommonParameters :: Lens' a CommonParameters
algCommonParameters = ... -- default implementation
myp1 :: Lens' a B
myp1 = ... -- default implementation
instance HasAlgorithmParameters AlgorithmParameters where
algorithmParameters = id
现在,您可以将 CommonParameters
光学元件与
AlgorithmParameters
类型,定义如下实例:
instance HasCommonParameters AlgorithmParameters where
commonParameters = algCommonParameters