2013-07-26 3 views
2

Я начал играть с Haskell, прочитал несколько руководств и одну из официальных книг (lyah). Я почувствовал, что смогу начать свой первый персональный проект. А что касается любого нового языка, который я выбираю, я хотел бы реализовать пакет для обработки линейной алгебры (операции вокруг матриц, векторов и т. Д.). Функции в порядке, но я не зашел слишком далеко с типами данных.Определение нового типа данных без конструкторов данных

Первоначально, у меня была функция, которая выглядела так:

 add_vect :: (Num a) => [a] -> [a] -> [a] 
    add_vect x y = zipWith (+) x y 

Теперь я хочу, чтобы дать имя (Vector) к тому, что [a] средства, так что vect_add будет выглядеть так:

vect_add :: Vector -> Vector -> Vector 
    vect_add x y = zipWith (+) x y 

После многих амбициозных попыток я закончил (вдохновленный определением String) с очень простым определением:

type Vector = [Int] 

Проблема заключается в том, что я теряю универсальность типа для моей функции, которая теперь работает только с [Int] вместо любого числового типа.

Мой вопрос: Есть ли способ выразить типичность (используя, например, calsses типа) определение новых типов. Нечто похожее на:

type Vector = (Num a) => [a] 

или, возможно, любой другой способ держать типичность моих Vector «S?

+0

Даже если вы могли бы делать то, что хотели, потому что 'a' не является конкретным типом, вам все равно придется превратить' Vector' в конструктор типа ** ** Vector a' .. точно так же, как вы не можете иметь «Maybe = Nothing» Просто. 'Maybe' - это конструктор типа,' Maybe Int' - это тип. –

ответ

3

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

mkVect :: [a] -> Vector -- You are going to need something with this type. 


x1 :: [Int] 
x1 = [1,2,3] 
x2 :: [Double] 
x2 = [1.0,2.0,3.0] 

v3 = add_vect (mkVect x1) (mkVect x2) 

Единственный способ тип проверки может остановить это, имея параметр типа для вектора как часть add_vect.

Так вместо этого вы должны написать

type Vector a = [a] 

Таким образом, тип проверки можно увидеть, что вы делаете.

3

Ну ...

{-# LANGUAGE RankNTypes  #-} 

type Vector = forall a . Num a => [a] 

Но это не совсем то, что вы хотите: вы бы в конечном итоге с

addVect :: (forall a.Num a => [a]) -> (forall b.Num b => [b]) -> (forall c.Num c => [c]) 

, которые не могут быть определены (каждый из векторов может иметь разные числовые типы).

Как сказал Пол Джонсон, вы могли использовать type Vector a = [a] (или, что эквивалентно, type Vector = []). Но я не думаю, что это действительно то, что вы хотите: у вас в основном заканчиваются те же подписи, что и у вас сейчас, и на самом деле не так естественно иметь параметрически полиморфные векторные функции в поле, которое охватывает векторное пространство.

Правильное решение, ИМО, является одним принятое vector-space package: (упрощенный)

class VectorSpace v where 
    type Scalar v :: *  -- This is going to be the type of the 
         -- field ("of the components of each vector") 

    (^+^) :: v -> v -> v -- You call this `addVect` 
    (*^) :: (Scalar v) -> v -> v 

    ... 

Вы можете иметь, например,

data Vectorℝ2 = Vectorℝ2 !Double !Double 

instance VectorSpace Vectorℝ2 where 
    type Scalar Vectorℝ2 = Double 
    Vectorℝ2 x y ^+^ Vectorℝ2 x' y' = Vectorℝ2 (x+x') (y+y') 
    ... 

или

newtype Vectorℝn = Vectorℝn [Double] 

instance VectorSpace Vectorℝn where 
    type Scalar Vectorℝn = Double 
    Vectorℝn xs ^+^ Vectorℝn x's = Vectorℝn $ zipWith (+) xs x's 

BTW, zipWith(+) не очень хорошее определение для переменной-мерного векторного сложения: вы получите, например,

[1,2,3] ^+^ [4,5] ≡ [5,7] 

Хотя на самом деле я бы ожидать [4,5] ≅ [4,5,0] в векторном смысле, и, таким образом, [1,2,3] ^+^ [4,5] ≡ [5,7,3] ≆ [5,7].

+0

Мне нужно пройти все это. Практикуйте эти тонкие вещи, специфичные для языка. Большое спасибо за ваши ответы. PS: Я не использую zipWith в моей версии этого кода – mbelaoucha

0

Один «правильный» способ - только отметить, что вы используете только Vector для целей документирования, и поэтому type Vector a = [a] не так уж плохо. Затем у вас есть vect_add :: Vector a -> Vector a -> Vector a, который должен соответствовать и даже vector-space's (*^) :: a -> Vector a -> Vector a.

Смежные вопросы