2016-08-18 2 views
1

Поскольку hmatrix предоставляет экземпляр Num для типов Matrix, я могу выразить поэлементное вычитание как:Почему (-) не удается выполнить проверку типа, когда я помещаю двойную матрицу слева и двойную справа?

m = (2><2)[1..] :: Double Matrix 
m' = m - 3 

Это прекрасно работает, так как 3 является Num, и результаты в матрице создается путем вычитания 3 из каждого элемент m.

Почему это также не работает:

m' = m - (3::Double) 

Ошибка я получаю:

Couldn't match expected type ‘Matrix Double’ 
      with actual type ‘Double’ 
In the second argument of ‘(-)’, namely ‘(3 :: Double)’ 
In the expression: m - (3 :: Double) 

Я ожидал, что компилятор, чтобы понять, что Double также Num. Почему это, похоже, не так?

+0

Обратите внимание, что Double - это конкретный экземпляр Num. «Matrix Double» - это еще один конкретный экземпляр Num, но для вычитания оба аргумента должны быть одного типа. – ErikR

+1

Обратите внимание, что '3' на самом деле имеет тип' Matrix Double' в вашем примере. Номера перегружены в Haskell. – Alec

ответ

7

Что происходит, когда вы делаете m - 3 с m :: Matrix Double является то, что 3 :: Matrix Double. Тот факт, что Matrix Double является экземпляром Num, означает, что компиляторы знают, как перевести литер 3. Однако, когда вы делаете m - (3 :: Double), вы получаете ошибку типа, потому что (-) :: (Num a) => a -> a -> a, поэтому тип вычитаемого элемента должен быть экземплярами Num и соответствовать. Следовательно, вы можете вычесть два Double s, два Matrix Double s, но не Matrix Double и Double.

В конце концов, это кажется мне довольно логичным, не имеет смысла вычитать матрицу и скаляр.

+0

Я вывел из ваших слов, что то, что происходит на самом деле, когда компилятор встречает '' m-3'', состоит в том, что он преобразует 3 в матрицу Double, якобы, в ту же размерность, что и матрица слева, а затем выполняет математику матрицы? (Ваша мысль о невозможности вычитать скаляры из матриц работает как счетчик стандартной практики во многих пакетах линейной алгебры, позволяющих именно это.) – nclark

+3

@nclark: Это именно то, что происходит. '3' имеет тип' Num a => a', основанный на функции 'fromInteger'. 'Matrix' имеет экземпляр' Num', который реализует 'fromInteger', если это необходимо. –

+0

Да, точно. Он использует метод 'fromInteger' из класса' Num' для этого, поэтому ваше выражение становится 'm - fromInteger 3'. В случае 'hmatrix', я думаю,' fromInteger n' определяется как матрица, где каждый элемент равен 'n'. – villou24

5

Это распространенное недоразумение людей, новых для стиля Haskell, связанных с перегрузкой на основе типов, особенно тех, кто используется для перегрузки на основе подкласса, используемой в популярных языках OO.

Оператор вычитания имеет тип Num a => a -> a -> a; поэтому он принимает два аргумента любого типа, который находится в классе классов Num. Это кажется нравится то, что происходит, когда вы делаете m - 3, так это то, что оператор вычитания принимает Matrix Double слева и некоторый простой числовой тип справа. Но это на самом деле неверно.

Когда типа подпись, как Num a => a -> a -> a использует тот же тип переменные несколько раз, вы можете выбрать любой тип вы любите (в зависимости от contstraints перед =>: Num a в данном случае), чтобы использовать для a, но он должен быть в точно такой же тип везде, что появляется a. Matrix Double -> Double -> ??? недействительный экземпляр типа Num a => a -> a -> a (и если бы это было так, как бы вы узнали, что он вернулся?).

Причина m - 3 работы является то, что из-за оба аргумента должны быть того же типа, и m определенно типа Matrix Double, компилятор видит, что 3 также должен быть типа Matrix Double. Поэтому вместо использования 3, появившегося в исходном тексте, чтобы построить Double (или Integer или один из многих других числовых типов), он использует исходный текст 3 для создания Matrix Double. Эффективно вывод типа изменил способ, которым компилятор читает текст исходного кода 3.

Но если вы используете m' = m - (3::Double) тогда вы не позволяя ему просто выяснить, какой тип 3 должны сделать использование оператора вычитания действительно, вы говорите это, что это 3 является конкретно Double. Нет никакого пути к тому, чтобы быть правдой (ваше утверждение :: Double и требование, чтобы оператор вычитания получал два аргумента одного и того же типа), поэтому вы получаете ошибку типа.

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