Haskell:如何编写与两种包装类型的内部交互的代码?

dav*_*idA 5 haskell encapsulation vector matrix wrapper

我想知道如何创建两个相互交互的封装类型,而不会将内部实现暴露给其他模块。

举个例子,考虑我的两个模块,Vector.hsMatrix.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 乘以Matrixa 会Vector产生一个新的Vector。然而,为了实现这种操作,代码执行它需要访问的内部实现两者的VectorMatrix,为了做这样的事情:

-- 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 中有类似的概念吗?

我想我可以同时实现VectorMatrix同一模块中。然后他们可以访问彼此的数据构造函数。然而,这并不是一个真正令人满意的解决方案,因为它只是解决了这个问题。

有没有更好的方法?