Ответ 1
Числовые литералы (т.е. просто ввод числа в коде Haskell) не являются фиксированным типом. Они полиморфны. Их нужно оценивать в определенном контексте, что требует, чтобы они имели конкретный тип.
Таким образом, выражение 5.0 * (3 - 1)
не умножает Int
на a Float
. 5.0
должен иметь тип Fractional
, 3
и 1
- каждый тип Num
. 3 - 1
означает, что 3 и 1 оба должны быть одного и того же типа Num
, но мы до сих пор еще не имеем никаких ограничений относительно того, какой он есть; результат вычитания является одним и тем же типом.
*
означает, что оба аргумента должны быть одного и того же типа, и результат будет таким же. Так как 5.0
- это некоторый тип Fractional
, то должен быть также (3 - 1)
. Мы уже знали, что 3
, 1
и (3 - 1)
должны быть некоторые Num
, но все типы Fractional
также являются Num
, поэтому эти требования не конфликтуют.
Конечным результатом является то, что все выражение 5.0 * (3 - 1)
- это некоторый тип Fractional
, а 5.0
, 3
и 1
- все те же типы. Вы можете использовать команду :t
в GHCi, чтобы увидеть это:
Prelude> :t 5.0 * (3 - 1)
5.0 * (3 - 1) :: Fractional a => a
Но для фактического вычисления этого выражения нам нужно сделать это для определенного типа. Если бы мы оценивали это и передавали ему некоторую функцию, которая требовала бы Float
, Double
или какой-либо другой тип Fractional
, тогда Haskell выбрал бы это. Если мы просто оцениваем выражение без какого-либо другого контекста, требующего, чтобы он был конкретным типом, Haskell имеет некоторые правила дефолта, чтобы автоматически выбрать один для вас (если правила по умолчанию не применяются, вместо этого вы получите ошибку типа о неоднозначных переменных типа).
Prelude> 5.0 * (3 - 1)
10.0
Prelude> :t it
it :: Double
Выше я оценил 5.0 * (3 - 1)
, а затем спросил тип магической переменной it
, которую GHCi всегда связывает с последним оцениваемым ею значением. Это говорит мне о том, что GHCi по умолчанию присвоил моему типу Fractional a => a
только Double
, чтобы вычислить, что значение выражения было 10.0
. При выполнении этой оценки он только когда-либо многократно (и вычитал) Double
s, он никогда не умножал a Double
на Int
.
Теперь, что происходит, когда вы пытаетесь использовать несколько числовых литералов, которые выглядят так, как будто они могут быть разных типов. Но ваша функция test
не умножает литералы, она умножает переменные определенных известных типов. В Haskell вы не можете умножить Int
на Float
, потому что оператор *
имеет тип Num a => a -> a -> a
- он принимает два значения одного и того же числового типа и дает результат, который является этим типом. Вы можете умножить Int
на Int
, чтобы получить Int
или Float
с помощью Float
, чтобы получить Float
. Вы не можете умножить Int
на Float
, чтобы получить ???
.
Другие языки поддерживают эту операцию только путем неявной вставки вызовов в функции преобразования при некоторых обстоятельствах. Haskell никогда не подразумевает преобразования между типами, но имеет функции преобразования. Вам просто нужно вызвать их явно, если вы хотите, чтобы их вызывали. Это сделало бы трюк:
test :: Float -> Int -> Int -> Float
test a b c = a * fromIntegral (b - c)