2

Haskellでは、型の「ファミリ」(たとえば、Nの一部の値の場合はN行N要素の行列)と、「関連する」型の並列ファミリ(たとえば、NのN要素ベクトル)がある場合Nの同じ値)、および各ファミリから1つの特定の型を必要とする操作(たとえば、N行N要素の行列とN要素の列ベクトルの乗算)では、その型クラスを宣言できますか?手術?

この特定の例では、次のようになります。

class MatrixNxN m where

  --| Multiplication of two N-by-N-element matrices
  mmul :: Num a => m a -> m a -> m a

  --| Multiplication of an N-by-N-element matrix and an N-element column vector
  vmul :: Num a => m a -> v a -> v a

ただし、タイプを制約する方法はわかりませんv。このようなことは可能ですか?

複数の関連する型の型クラスを宣言するという一般的な質問に対する回答と、行列とベクトルの乗算のための型クラスを宣言するという特定の質問に対する回答の両方を歓迎することに注意してください。私の特定のケースでは、N(2、3、および4)の値の小さな既知のセットしかありませんが、Haskellの型システムで何をエンコードできるかを理解することに一般的に興味があります。

編集:MultiParamTypeClasses私はこれを使用しFunctionalDependenciesて、以下のGabrielGonzalezとMFlamerによって提案されたように実装しました。これは、私の実装の関連する部分が次のようになってしまったものです。

class MatrixVectorMultiplication m v | m -> v, v -> m where
  vmul :: Num a => m a -> v a -> v a

data Matrix3x3 a = ...
data Vector3 a = ...

instance MatrixVectorMultiplication Matrix3x3 Vector3 where
  vmul = ...

これは、の型署名でありvmul、それ自体で部分的に適用されます。

vmul :: (Num a, MatrixVectorMultiplication m v) => m a -> v a -> v a
(`vmul` v) :: Matrix3x3 Integer -> Vector3 Integer
(m `vmul`) :: Vector3 Integer -> Vector3 Integer

これはすべてとてもエレガントだと思います。答えてくれてありがとう!:)

4

4 に答える 4

2

行列/ベクトルの次元は、タイプレベルの数字を使用してエンコードできることに注意してください。これにより、などをclass MatrixNum (n :: Nat)手動でコーディングする代わりに、より一般的な定義が可能になります。また、コンパイル時に互換性のないオブジェクトに対するタイプの乗算を防ぐこともできます。GHC 7.4。*では、次のように定義できます。Matrix3x3Matrix4x4

{-# LANGUAGE TypeFamilies, DataKinds, FlexibleInstances #-}

data Nat = Zero | Succ Nat

class MatrixNum (n :: Nat) where
    type Matrix n :: * -> *
    type Vector n :: * -> *
    mmul :: Num a => Matrix n a -> Matrix n a -> Matrix n a
    vmul :: Num a => Matrix n a -> Vector n a -> Vector n a

newtype ListMatrix (n :: Nat) a = ListMatrix [[a]] deriving Show
newtype ListVector (n :: Nat) a = ListVector [a] deriving Show

instance MatrixNum n where
    type Matrix n = ListMatrix n
    type Vector n = ListVector n
    mmul (ListMatrix xss) (ListMatrix yss) = ListMatrix $ error "Not implemented"
    vmul (ListMatrix xss) (ListVector ys) = ListVector $ error "Not implemented"

GHC 7.6。*ではさらに優れています。タイプレベルのプロモートリテラルがサポートされるようになったため、上記のNat定義を削除し、タイプで数値リテラルを使用してオブジェクトのディメンションを指定できますNatGHC.TypeLits

m1 :: ListMatrix 3 Int
m1 = ListMatrix [[1,2,3],[4,5,6],[7,8,9]]

v1 :: ListVector 3 Int
v1 = ListVector [1,2,3]

v2 = m1 `vmul` v1 -- has type ListVector 3 Int
于 2013-02-12T08:41:08.980 に答える
1

型クラスを使って実装することも可能だと思います。循環関数従属性を回避するために、ベクトルと行列のタイプがスカラータイプに依存することを宣言します。newtypeスカラーとそれに対応するベクトルおよび行列用のが必要ですが、いくつかの利点もあります。特に、とNum aの宣言で制約を保持する必要はありません。スカラー値にどのような制約を課すかは、インスタンスの実装に任せることができます。mmulvmul

{-# LANGUAGE TypeFamilies #-}

class MatrixNxN a where
    data Matrix a :: *
    data Vector a :: *

    -- | Multiplication of two N-by-N-element matrices
    mmul :: Matrix a -> Matrix a -> Matrix a

    -- | Multiplication of an N-by-N-element matrix
    -- and an N-element column vector
    vmul :: Matrix a -> Vector a -> Vector a

-- List matrices on any kind of numbers:
newtype ListScalar a = ListScalar a
instance Num a => MatrixNxN (ListScalar a) where
    newtype Matrix (ListScalar a) = ListMatrix [[a]]
    newtype Vector (ListScalar a) = ListVector [a]

    vmul (ListMatrix mss)  (ListVector vs)   = ...
    mmul (ListMatrix m1ss) (ListMatrix m2ss) = ...


-- We can have matrices that have no `Num` instance for
-- their scalars, like Z2 implemented as `Bool`:
newtype ListBool = ListBool Bool
instance MatrixNxN ListBool where
    newtype Matrix ListBool = ListBoolMatrix [[Bool]]
    newtype Vector ListBool = ListBoolVector [Bool]

    vmul (ListBoolMatrix mss)  (ListBoolVector vs)   = ...
    mmul (ListBoolMatrix m1ss) (ListBoolMatrix m2ss) = ...
于 2013-02-12T08:07:24.927 に答える
1

これは、上記の応答でも提案されているように、MultiParamTypeClassesを使用してこれを行った方法です。また、各クラス関数で両方のタイプが使用されていないため、FunctionalDependencies拡張機能を使用する必要があります。他の誰かがおそらくもっと完全な答えを提供するでしょうが、私は最近このパターンをたくさん使っているので、それが役立つかもしれないと思いました。

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}

module Test where

class MatrixNxN m v | m -> v where 
  mmul :: Num a => m a -> m a -> m a
  vmul :: Num a => m a -> v a -> v a
于 2013-02-11T21:56:46.390 に答える
1

MFlamerこれは、の答えの非常に小さなバリエーションであり、以下にもm依存しvます。

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}

class MatrixNxN m v | m -> v, v -> m where
    mmul :: Num a => m a -> m a -> m a
    vmul :: Num a => m a -> v a -> v a

そうすれば、次のようになります。

(`vmul` someVector)

someVector...次に、コンパイラはのタイプのみに基づいて正しいインスタンスを選択できます。

型ファミリーソリューションは同じ理由で機能しません。主な理由は、v型コンストラクターを型コンストラクターの型関数として宣言した場合m、その型関数は必ずしも1対1であるとは限らないため、コンパイラーは機能しないためです。mから推測することができvます。これが、関数従属性が型族よりも強力であると人々が言う理由です。

于 2013-02-12T01:17:22.450 に答える