2015-02-10 2 views
1

Почему связь между a и b не выводима?Неприводимое отношение

class Vector a where 
    (<.>) :: Num b => a -> a -> b 

data Vec2 a 
    = Vec2 (a, a) 

    deriving Show 

instance Num a => Vector (Vec2 a) where 
    Vec2 (a, b) <.> Vec2 (c, d) = a * c + b * d 

Я хотел бы иметь алгебраическую структуру данных Vec2, где компонентами могут быть любые числа.

+2

[Здесь] (http://hackage.haskell.org/package/vector-space-0.8.7/docs/Data-VectorSpace.html) - стандартная реализация класса векторного пространства. Как вы увидите, он использует «Scalar» в качестве семейства ассоциированных типов, как предполагал Эржан. (Класс с '<.>' фактически является подклассом ['InnerSpace'] (http://hackage.haskell.org/package/vector-space-0.8.7/docs/Data-VectorSpace.html#t:InnerSpace) : не все векторные пространства допускают скалярное/скалярное произведение, что все они позволяют только умножать векторы со скалярами ('* ^') и вместе добавлять векторы ('^ + ^' из 'AdditiveGroup'). – leftaroundabout

ответ

7
(<.>) :: Num b => a -> a -> b 

Вышесказанное означает, что (<.>) способен производить любой тип номера Вызывающий этой функции, возможно, захотите.

Например, если x,y являются Vec2 Double, то x <.> y можно вызвать для возврата Integer. Затем компилятор жалуется, что его реализация в размещенном экземпляре не является достаточно общей, поскольку она возвращает Double s вместо любой тип, который может выбрать вызывающий.

Я думаю, что это не то, что этот код предназначен для моделирования.

Вы можете перейти к классу многопараметрической (вам необходимо включить несколько расширений, GHC покажет вам, какие из них):

class Vector a b where 
    (<.>) :: a -> a -> b 

instance Num a => Vector (Vec2 a) a where 
    Vec2 (a, b) <.> Vec2 (c, d) = a * c + b * d 

Поскольку теперь компилятор не может определить тип x <.> y от типа x,y, вы можете добавить функциональную зависимость с помощью

class Vector a b | a -> b where 
    (<.>) :: a -> a -> b 

или в качестве альтернативы использовать семейный тип

class Vector a where 
    type Scalar a 
    (<.>) :: a -> a -> Scalar a 

instance Num a => Vector (Vec2 a) where 
    type Scalar (Vec a) = a 
    Vec2 (a, b) <.> Vec2 (c, d) = a * c + b * d 
2

Как ответ @ chi, ваш код не делает b зависит от a. Для того, чтобы получить то, что я думаю, что вы хотите, вы можете использовать соответствующий тип семьи:

{-# LANGUAGE TypeFamilies #-} 

class Vector a where 
    type Element a 
    (<.>) :: a -> a -> Element a 

data Vec2 a 
    = Vec2 (a, a) 

    deriving Show 

instance Num a => Vector (Vec2 a) where 
    type Element (Vec2 a) = a 
    Vec2 (a, b) <.> Vec2 (c, d) = a * c + b * d 

Другой вариант заключается в использовании многопараметрической класса типов с функциональной зависимостью, но я думаю, что это более сложный здесь.