Почему (-) не удается проверить тип, когда я размещаю двойную матрицу слева и двойную справа?
Поскольку 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
, Почему это, похоже, не так?
2 ответа
Что происходит, когда вы делаете m - 3
с m :: Matrix Double
в том, что 3 :: Matrix Double
, Дело в том, что Matrix Double
это пример Num
означает, что компиляторы умеют переводить литеральное 3
, Однако, когда вы делаете m - (3 :: Double)
, вы получаете ошибку типа, потому что (-) :: (Num a) => a -> a -> a
таким образом, тип вычитаемого элемента должен быть экземплярами Num
и соответствовать. Следовательно, вы можете вычесть два Double
с, два Matrix Double
с но не Matrix Double
и Double
,
В конце концов, это кажется мне вполне логичным, не имеет смысла вычитать матрицу и скаляр.
Это распространенное заблуждение людей, плохо знакомых со стилем перегрузки на основе классов типов в Haskell, особенно тех, кто привык к перегрузкам на основе подклассов, используемым в популярных языках OO.
Оператор вычитания имеет тип Num a => a -> a -> a
; поэтому он принимает два аргумента любого типа, который находится в классе типа Num
, Кажется, что происходит, когда вы делаете m - 3
является то, что оператор вычитания принимает Matrix Double
слева и простой числовой тип справа. Но это на самом деле неверно.
Когда подпись типа, как Num a => a -> a -> a
использует одну и ту же переменную типа несколько раз, вы можете выбрать любой тип, который вам нравится (при условии =>
: 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
утверждение и требование, чтобы оператор вычитания получал два аргумента одного типа), поэтому вы получаете ошибку типа.