Проблемы с printf и переменными неоднозначного типа

У меня небольшая проблема с переменной неоднозначного типа. Я люблю haskell, но это действительно то, с чем я до сих пор не справился. Проблема очень проста и включает в себя printf из Text.Printf. Поскольку проблема очень общая, я просто приведу пример кода:

program = do
    d <- addd 4 8
    printf "%d" d

addd x y = return (x+y)

Конечно, printf импортируется. Затем компилятор выдает очевидную неоднозначную ошибку переменной типа между Num и PrintfArg. Я просто не знаю, куда вписать правильную подпись типа.


person derwahre_tj    schedule 15.09.2012    source источник


Ответы (2)


Есть несколько мест, где вы можете поставить подпись типа. Во-первых, addd имеет самый общий тип (а самый общий тип — это (почти всегда) то, что выводит GHC, когда вы опускаете подпись):

addd :: (Monad m, Num a) => a -> a -> m a

Вы можете ограничить это, чтобы работать только с определенным типом, указав addd явную сигнатуру типа, чтобы он вообще не был полиморфным в аргументах, например:

addd :: Monad m => Int -> Int -> m Int
-- or,
addd :: Monad m => Integer -> Integer -> m Integer

Или вы можете сообщить GHC о типе ввода при вызове addd, например:

d <- addd 4 (8 :: Integer)

и тогда вывод типа сделает вывод, что 4 и d оба являются Integers.

Наконец, вы можете указать d тип. Либо когда вы используете его (если вы используете d несколько раз, вам нужна только одна аннотация), например:

printf "%d" (d :: Integer)

Или когда вы устанавливаете его (требуется расширение GHC ScopedTypeVariables):

{-# LANGUAGE ScopedTypeVariables #-}

[...]
add = do
    (d :: Integer) <- addd 4 8
person huon    schedule 15.09.2012
comment
Большое спасибо, что помогло! Но я должен признать, что это выглядит очень некрасиво. Разве нет более красивого способа обойти это? Также еще один вопрос для понимания: почему недостаточно иметь тип Num для возвращаемого значения addd? Почему я должен рассказывать haskell о Integer? - person derwahre_tj; 15.09.2012
comment
Ах, я вижу, это связано с форматированием %d, которое требует, чтобы d был целочисленного типа. - person derwahre_tj; 15.09.2012

Я попытаюсь объяснить, что не так с вашей программой.

  • Попробуйте дать явную сигнатуру типа, это поможет компилятору вывести типы, а также поможет вам лучше понять вашу программу.
  • addd — это чистая функция, поэтому не используйте return.
  • вернуть не то, что вы ожидаете от императивного фона.
  • зачем вам printf в конце концов, используйте print или putStrLn если хотите вывести на консоль. Используйте show, если вы хотите преобразовать тип (чей экземпляр show определен) в строку.

Вот ваша исправленная программа в любом случае

import Text.Printf

program :: String
program = do
    let d = addd 4 8
    printf "%d" d

addd :: Int -> Int -> Int
addd x y = x+y

Вы можете написать это, просто используя print как

program :: IO ()
program = do
    print $ addd 4 8

addd :: Int -> Int -> Int
addd x y = x+y

Попробуйте прочитать вводные материалы по Haskell.

person Satvik    schedule 15.09.2012
comment
Фоном было использование printf для красивого табличного форматированного вывода на экран. Я знаю основной оператор печати, но он немного отстает от форматирования. - person derwahre_tj; 15.09.2012