dav*_*idA 5 haskell encapsulation vector matrix wrapper
我想知道如何创建两个相互交互的封装类型,而不会将内部实现暴露给其他模块。
举个例子,考虑我的两个模块,Vector.hs
和Matrix.hs
,包装Linear.V4
(一个 4 元素向量)和Linear.M44
(一个 4x4 元素矩阵)。我希望编写一个函数,将 aMatrix
乘以 a Vector
,返回 a Vector
,并使用包装的数据类型来执行操作。
在这个例子中,Vector.hs
我有:
-- Vector.hs
module Vector (Vector, vector) where
import Linear (V4 (V4))
newtype Vector = Vector (V4 Double) deriving (Eq, Show, Read)
vector :: (Double, Double, Double, Double) -> Vector
vector (x, y, z, w) = Vector (V4 x y z w)
Run Code Online (Sandbox Code Playgroud)
请注意,我仅导出新类型和工厂函数 -Vector
未导出数据构造函数。据我了解,这隐藏了 的内部实现Vector
(即它实际上是一个V4
)。
在Matrix.hs
我有类似的东西:
-- Matrix.hs
module Matrix (Matrix, vector)
import Linear (V4 (V4), M44)
type Row = (Double, Double, Double, Double)
newtype Matrix = Matrix (M44 Double) deriving (Eq, Show, Read)
matrix :: (Row, Row, Row, Row) -> Matrix
--matrix ((a00, a01, a02, a03), (a10, ... ) = Matrix (V4 (V4 a00 a01 a02 a03) (V4 a10 ...))
Run Code Online (Sandbox Code Playgroud)
客户端代码可以非常有效地使用这两个模块 - 客户端代码不知道它们是用Linear
数据结构实现的,而且客户端代码显然没有办法利用它(这是真的吗?)。
当这两种类型需要在其包装类型的级别上相互交互时,就会出现问题 - 在这种特殊情况下,将 a 乘以Matrix
a 会Vector
产生一个新的Vector
。然而,为了实现这种操作,代码执行它需要访问的内部实现两者的Vector
和Matrix
,为了做这样的事情:
-- Matrix.hs
-- ... include earlier definitions
{-# LANGUAGE FlexibleInstances #-}
import qualified Linear.Matrix ((!*))
class MatrixMultiplication a b c | a b -> c where
infixl 7 |*| -- set same precedence and associativity as *
(|*|) :: a -> b -> c
instance MatrixMultiplication Matrix Vector Vector where
(|*|) (Matrix a) q0 =
let (V4 x y z w) = a Linear.Matrix.!* _impl q0
in vector (x, y, z, w)
Run Code Online (Sandbox Code Playgroud)
要做到这一点,我需要一个函数,_impl
,它允许Matrix
模块进入以下内部Vector
:
-- Vector.hs
-- ... include earlier definitions
_impl :: Vector -> V4 Double
_impl (Vector q) = q
-- also added to export list
Run Code Online (Sandbox Code Playgroud)
因此,在仔细隐藏了 的内部结构Vector
(通过不导出 的数据构造函数函数Vector
)之后,似乎我必须使用不同的导出函数公开它们,而我唯一的防御是一些文档告诉客户端代码他们不应该使用它。这似乎很不幸 - 我实际上只是重命名了数据构造函数。
在像 C++ 这样的语言中,friend
关键字可用于提供对其中一种类型的私有数据成员的函数访问。Haskell 中有类似的概念吗?
我想我可以同时实现Vector
与Matrix
同一模块中。然后他们可以访问彼此的数据构造函数。然而,这并不是一个真正令人满意的解决方案,因为它只是解决了这个问题。
有没有更好的方法?
归档时间: |
|
查看次数: |
90 次 |
最近记录: |