Ошибка типа с классами числового типа

#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. Я понял, в чем проблема