#haskell
#haskell
Вопрос:
data V2 a = V2 a a deriving (Show, Eq)
instance Num a => Num (V2 a) where
(-) (V2 x0 y0) (V2 x1 y1) = V2 (x0 - x1) (y0 - y1)
( ) (V2 x0 y0) (V2 x1 y1) = V2 (x0 x1) (y0 y1)
(*) (V2 x0 y0) (V2 x1 y1) = V2 (x0 * x1) (y0 * y1)
abs = undefined
signum = undefined
fromInteger = undefined
instance Fractional a => Fractional (V2 a) where
(/) (V2 x0 y0) (V2 x1 y1) = V2 (x0 / x1) (y0 / y1)
recip = undefined
fromRational = undefined
-- Multiply by scalar
(*$) :: Num a => V2 a -> a -> V2 a
(*$) (V2 x y) s = V2 (x * s) (y * s)
-- Length of the vector
len :: (Num a, Integral a, Floating b) => V2 a -> b
len (V2 x y) = sqrt $ fromIntegral $ x * x y * y
normal :: (Num a, Integral a) => V2 a -> V2 a
normal v = v *$ (1 / len v)
{-
MathV2.hs:31:20:
Could not deduce (Fractional a) arising from a use of `/'
from the context (Num a, Integral a)
bound by the type signature for
normal :: (Num a, Integral a) => V2 a -> V2 a
at MathV2.hs:31:1-27
Possible fix:
add (Fractional a) to the context of
the type signature for
normal :: (Num a, Integral a) => V2 a -> V2 a
In the second argument of `(*$)', namely `(1 / len v)'
In the expression: v *$ (1 / len v)
In an equation for `normal': normal v = v *$ (1 / len v)
MathV2.hs:31:22:
Could not deduce (Floating a) arising from a use of `len'
from the context (Num a, Integral a)
bound by the type signature for
normal :: (Num a, Integral a) => V2 a -> V2 a
at MathV2.hs:31:1-27
Possible fix:
add (Floating a) to the context of
the type signature for
normal :: (Num a, Integral a) => V2 a -> V2 a
In the second argument of `(/)', namely `len v'
In the second argument of `(*$)', namely `(1 / len v)'
In the expression: v *$ (1 / len v)
-}
У меня возникли проблемы с реализацией обычной функции, описанной выше. Как заставить его пройти проверку типа?
Комментарии:
1. Вы должны удалить
recip = undefined
, поскольку у него есть разумный метод по умолчанию.
Ответ №1:
Три варианта:
-
Измените свою подпись типа:
normal :: (Integral a, Floating b) => V2 a -> V2 b
А затем укажите функцию для преобразования
(Integral a) => V2 a
в(Floating b) => V2 b
и примените ее кv
перед*$
. -
Преобразуйте
Floating
результат из1 / len v
вIntegral
значение (round
и т.д.). -
Сделайте, как предлагает Ландей, и принудительно используйте
Floating
везде.
len
принимает (Integral a) => V2 a
и возвращает (Floating b) => b
. Затем вы выполняете 1 /
над результатом, который все еще имеет тип (Floating b) => b
. Для вашего типа *$
требуется V2 a
и a
, но в данном случае у вас есть v :: (Integral a) => V2 a
и (1 / len v) :: (Floating b) => b
, которые не являются эквивалентными типами.
Итак, вам нужно где-то использовать какую-то форму принуждения.
Ответ №2:
Минимальное исправление — просто изменить подпись типа на обычный:
normal :: Floating a => V2 a -> V2 a
Вот типы:
sqrt :: Floating a => a -> a
Таким образом, у len нет причин принимать что-то другое, кроме Floating
.
Ответ №3:
Как насчет…
len :: (Floating a) => V2 a -> a
len (V2 x y) = sqrt $ x * x y * y
normal :: (Floating a) => V2 a -> V2 a
normal v = v *$ (1.0 / len v)
Конечно, это означает, что вам нужно преобразовать V2 Int
, прежде чем вы сможете вычислить нормальное значение, но это все равно, что вам нужно преобразовать Int перед выполнением деления.
Комментарии:
1. Но это обычное поведение, это то же самое, что невозможность записи
sqrt 4
.2. @Landei: конечно, вы можете, в этом смысл
fromInteger
вNum
классе: целые литералы могут быть преобразованы в любой числовой тип!
Ответ №4:
Найдено. Поскольку len
возвращается плавающее значение, a должно быть плавающим в normal
. В противном случае вы можете попытаться определить
($*) :: Num a, ?b => V2 a -> b -> V2 a
В любом случае
normal :: (Num a, Integral a, Floating a) => V2 a -> V2 a
работает
В качестве альтернативы вы можете изменить свое определение len на
len :: (Num a) => V2 a -> a
Комментарии:
1. Нет, это не так, 1 :: (Num a) => a.
2. Я понял, в чем проблема