Haskell умножить Int и вещественное число

Я имею

coefficient :: ???????
coefficient = 1.0

а также

val :: Int

и я хотел бы сделать

result :: ???????
result val coefficient = val * coefficient

Какие сигнатуры типов и функции преобразования мне нужно сделать, чтобы это работало? Что я должен делать в дополнение к этому, если я хочу иметь возможность обобщать val для любого вида Num?

Это:

coefficient = 1.0

val :: Int
val = 3

result :: Num a => a
result = coefficient * (fromIntegral val)

выдает мне это предупреждение компилятора:

Could not deduce (a ~ Double)
from the context (Num a)
  bound by the type signature for result :: Num a => a
  at Move.hs:17:1-41
  `a' is a rigid type variable bound by
      the type signature for result :: Num a => a at Move.hs:17:1
In the first argument of `(*)', namely `coefficient'
In the expression: coefficient * (fromIntegral val)
In an equation for `result':
    result = coefficient * (fromIntegral val)

Я знаю, что это не то, что я просил изначально, я сделал несколько ошибок при очистке моего кода.

Теперь с типом для коэффициента:

coefficient :: Num a => a
coefficient = 1.0

val :: Int
val = 3

result :: Num a => a
result = coefficient * (fromIntegral val)

Полученная ошибка:

Could not deduce (Fractional a) arising from the literal `1.0'
from the context (Num a)
  bound by the type signature for coefficient :: Num a => a
  at Move.hs:12:1-17
Possible fix:
  add (Fractional a) to the context of
    the type signature for coefficient :: Num a => a
In the expression: 1.0
In an equation for `coefficient': coefficient = 1.0

1 ответ

Решение

Существует функция fromIntegral, которая преобразует целое число в любой другой числовой тип. Так что вы можете сделать:

result :: (Integral n, Num m) => n -> m -> m
result val coefficient = fromIntegral val * coefficient

Или, в бессмысленном стиле:

result = (*) . fromIntegral

Обновление об обновленном вопросе (@Drew)

Рассмотрим этот код:

coefficient :: (Num a) => a
coefficient = 1.0

Это недействительно само по себе, следующим образом. Поскольку 1.0 является литералом для дробного числа (а не целого числа), то GHC может кодировать его только как любой тип, способный представлять дробные числа (так как a. Fractional a => a). Однако вы указали, что он должен быть действительным для любого числового типа (forall a. Num a => a). Некоторые числовые типы (например, Integer) не могут представлять дробные значения и не являются экземплярами Fractional (правильно), поэтому это не может проверять тип. Вы можете исправить это следующим образом:

coefficient :: (Fractional a) => a
coefficient = 2.0

Здесь GHC может вывести тип, а коэффициент работает нормально. Важно отметить, что Fractional является подклассом Num, поэтому все, что является Fractional, также должно быть Num. Если мы посмотрим на функцию в первой части моего ответа, коэффициент должен быть только типом Num (так как мы используем его только с (*)), поэтому мы можем использовать это определение коэффициента вместо этого параметра. Ваша проблема возникает по той же причине.

result :: (Num a) => a
result = coefficient * fromIntegral val

Опять же, результат этой функции должен быть того же типа, что и коэффициент. Поскольку коэффициент не может быть любым типом Num, а только дробным типом, нам нужно изменить это на:

result :: (Fractional a) => a
result = coefficient * fromIntegral val

И тогда это должно проверить тип. @singpolyma прав в том, что ваша первоначальная ошибка была частично связана с ограничением мономорфизма, но вам просто нужно было сделать сигнатуры типов немного более конкретными. Если вы хотите, чтобы он работал с (Num a) => a, то коэффициент должен быть целым числом (например, 1).

Обновление о GHCi (@Marcin)

Для использования этого в GHCi, я бы предложил GHCi сделать вывод о типе. Если в этом случае вы набираете (в GHCi):

let result val coefficient = fromIntegral val * coefficient

Тогда GHCi правильно выведет тип результата. Вы можете спросить GHCi, какого типа он думает, что-то использует команда:: t:

Prelude> :t result
result :: (Integral a1, Num a) => a1 -> a -> a

Если вы должны иметь явную сигнатуру типа, вы можете сделать:

let result = (\val coefficient -> fromIntegral val * coefficient) :: (Integral a, Num b) => a -> b -> b

Чтобы попытаться иметь явный тип, но GHCi сделает это мономорфным:

Prelude> :t result
result :: Integer -> Integer -> Integer

Что нам не нужно (это потому, что аннотация типа относится к значению лямбда-выражения, а не к объявлению результата). Я не знаю, как заставить работать явный тип здесь, так что, возможно, кто-то более знающий, чем мы, может ответить:P

Другие вопросы по тегам