Не удалось сопоставить ожидаемый тип «Int» при параметризации функции с Num a - PullRequest
0 голосов
/ 26 июня 2018

У меня есть такой код

main :: IO ()
main = putStrLn (show func1)

x,y :: Num a => a
x = 1000
y = 1000

func1 :: String
func1 = func3  ++ "!"

func3 :: String
func3 = show (1 + sum' x y)

sum' :: Int -> Int -> Int
sum' a b = a+b

и я хочу сделать x параметром. Поэтому я решил переписать это так:

func1 :: String
func1 = func3 x ++ "!"

func3 :: Num a => a -> String
func3 p = show (1 + sum' p y)

Я ожидаю, что это будет хорошо, так как я не изменяю никаких ограничений, но я получаю

file:21:27: error:
    • Couldn't match expected type ‘Int’ with actual type ‘a’
      ‘a’ is a rigid type variable bound by
        the type signature for:
          func3 :: forall a. Num a => a -> String
        at file.hs:20:1-29
    • In the first argument of ‘sum'’, namely ‘p’
      In the second argument of ‘(+)’, namely ‘sum' p y’
      In the first argument of ‘show’, namely
        ‘(1 + sum' p y)’
    • Relevant bindings include
        p :: a (bound at file.hs:21:7)
        func3 :: a -> String (bound at file.hs:21:1)
   |
21 | func3 p = show (1 + sum' p y)
   |      

Я могу решить это с помощью x :: Int и func3 :: Int -> String, но зачем мне это? В конце концов a может быть Int, и нет никаких других вызовов, ожидающих, что это будет что-то кроме Int.

1 Ответ

0 голосов
/ 26 июня 2018

Я думаю, что ваша путаница связана с тем, как работают универсальные квантификаторы.

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. Так что, если вы не делаете что-то действительно забавное с теорией типов, я предлагаю придерживаться краткого и простого подхода. При этом я хотел включить это, чтобы показать, что интерпретация, на которую вы рассчитывали, действительно возможна, хотя и с расширением компилятора.

...