Я думаю, что ваша путаница связана с тем, как работают универсальные квантификаторы.
x :: Num a => a
x = 1000
Это обещание. Он говорит: «Для любого числового типа a
, который вы можете дать мне, я могу дать вам значение x
этого типа».
func3 :: Num a => a -> String
func3 p = ...
Это другое обещание. Он говорит: «Для любого числового типа a
, который вы можете дать мне, я могу дать вам функцию, которая принимает a
и возвращает String
». Таким образом, func3
должен иметь возможность действовать как Int -> String
или Double -> String
или любая другая числовая функция, такая как Matrix -> String
или Vector -> String
. Таким образом, вы не можете предположить, что аргумент Int
.
Easy Fix
sum'
использует только +
, поэтому он также может принимать любой числовой тип. Просто измените аргументы на sum'
, и ваш func3
начнет работать.
sum' :: Num a => a -> a -> a
sum' x y = x + y
Используя приведенную выше аналогию, подпись типа здесь дает обещание: «Если вы дадите мне какой-либо числовой тип a
, я могу создать функцию, которая принимает два a
, складывает их вместе и возвращает a
».
Это , вероятно, исправление, которое вы хотите. В 99% случаев это будет лучшим решением. В качестве альтернативы, вы можете изменить func3
, чтобы он занимал только Int
, и тогда у вас будут рабочие (хотя и менее абстрактные) функции.
Иногда, если вы думаете, что функция может иметь более общий тип, но не уверены, вы можете закомментировать сигнатуру типа и спросить GHCi, какова предполагаемая сигнатура. Пример использования GHCi:
> let sum' x y = x + y
> :t sum'
sum' :: Num a => a -> a -> a
Таким образом, интерпретатор дал вам наиболее общий тип для sum'
, который вы затем можете вставить в свой код, чтобы сделать его более общим и абстрактным.
Длинный ответ
Давайте еще раз посмотрим на ваш код.
y :: Num a => a
y = 1000
func3 :: Num a => a -> String
func3 p = show (1 + sum' p y)
sum' :: Int -> Int -> Int
sum' x y = x + y
Теперь подпись Num a => a -> String
ассоциируется примерно так: Num a => (a -> String)
. Основываясь на ваших комментариях, я полагаю, что вы интерпретировали его как (Num a => a) -> String
, что означало бы «Эта функция принимает аргумент, который может быть интерпретирован как любой числовой тип, и возвращает строку». Теперь, это на самом деле не действительный код на Haskell. Однако, если вы используете GHC (что, скорее всего, и есть), вы можете включить расширение компилятора Rank2Types
, чтобы получить такое поведение.
{-# LANGUAGE Rank2Types #-}
y :: Num a => a
y = 1000
func3 :: (forall a. Num a => a) -> String
func3 p = show (1 + sum' p y)
sum' :: Int -> Int -> Int
sum' x y = x + y
forall
в начале типа аргумента по существу говорит: «Да, я знаю, что то, что я делаю, не является обычной интерпретацией Haskell, но все равно делаю это». Теперь func3
на самом деле обещает: «Дайте мне аргумент, который можно интерпретировать как любой числовой тип a
, и я выведу строку». Поэтому, когда func3
вызывает sum'
, он может просто интерпретировать этот аргумент как Int
, как вы изначально предполагали.
Этот код не является идиоматическим. Rank2Types
(и его общая форма RankNTypes
) редко требуется в стандартном коде Haskell. Так что, если вы не делаете что-то действительно забавное с теорией типов, я предлагаю придерживаться краткого и простого подхода. При этом я хотел включить это, чтобы показать, что интерпретация, на которую вы рассчитывали, действительно возможна, хотя и с расширением компилятора.