Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I printf a list of tuples?

The following code calculates a list of tuples of integers:

import Data.List (sort)
import Data.Char (digitToInt)
import Text.Printf

-- Check if the given list of numbers is ascending by exactly one
isAscending :: (Eq a, Num a) => [a] -> Bool
isAscending [] = True
isAscending [x] = True
isAscending (x:y:xs) = (x+1 == y) && isAscending (y:xs)

-- Check if the digits that are in the given number are in an intervall
-- So for 12345, 54321, 45321 the function returns True
-- but for 02345 it returns False
check :: Show a => a -> Bool
check x = isAscending $ map digitToInt $ sort $ show x

-- Find all numbers with property 'check' that have 5 digits
solve :: [Integer]
solve = filter check [10^4..10^5]

-- Print results
main :: IO()
main = mapM_ (uncurry $ printf "%d: %d") (zip [1..] solve)

But when I compile it I get:

$ ghc base.hs
[1 of 1] Compiling Main             ( base.hs, base.o )

base.hs:23:25:
    No instance for (PrintfArg a0) arising from a use of `printf'
    The type variable `a0' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Note: there are several potential instances:
      instance [safe] PrintfArg Char -- Defined in `Text.Printf'
      instance [safe] PrintfArg Double -- Defined in `Text.Printf'
      instance [safe] PrintfArg Float -- Defined in `Text.Printf'
      ...plus 12 others
    In the second argument of `($)', namely `printf "%d %d"'
    In the first argument of `mapM_', namely
      `(uncurry $ printf "%d %d")'
    In the expression:
      mapM_ (uncurry $ printf "%d %d") (zip [1 .. ] solve)

base.hs:23:46:
    No instance for (Enum a0)
      arising from the arithmetic sequence `1 .. '
    The type variable `a0' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Note: there are several potential instances:
      instance Enum Data.Char.GeneralCategory -- Defined in `Data.Char'
      instance Enum Double -- Defined in `GHC.Float'
      instance Enum Float -- Defined in `GHC.Float'
      ...plus 16 others
    In the first argument of `zip', namely `[1 .. ]'
    In the second argument of `mapM_', namely `(zip [1 .. ] solve)'
    In the expression:
      mapM_ (uncurry $ printf "%d %d") (zip [1 .. ] solve)

base.hs:23:47:
    No instance for (Num a0) arising from the literal `1'
    The type variable `a0' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Note: there are several potential instances:
      instance Num Double -- Defined in `GHC.Float'
      instance Num Float -- Defined in `GHC.Float'
      instance Integral a => Num (GHC.Real.Ratio a)
        -- Defined in `GHC.Real'
      ...plus 11 others
    In the expression: 1
    In the first argument of `zip', namely `[1 .. ]'
    In the second argument of `mapM_', namely `(zip [1 .. ] solve)'

It works fine when I replace the last line with

main = mapM_ (print) (zip [1..] solve)

but that does not give the desired formatting. What did I do wrong? How can I printf a list of integer tuples?

P.S.

I tried to make this shorter, but it seemed to give other errors:

import Data.List (sort)
import Data.Char (digitToInt)
import Text.Printf

-- Print results
main :: IO()
main = mapM_ (print "%d %d") ([(1,123), (2, 123), (3, 452), (4, 2)])
like image 969
Martin Thoma Avatar asked Mar 19 '23 12:03

Martin Thoma


1 Answers

printf uses magic. Basically, the problem you encounter is the same as

main :: IO ()
main = let k = read "1234"
       in print k
SO.hs:2:16:
    No instance for (Read a0) arising from a use of `read'
    The type variable `a0' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Note: there are several potential instances:
      instance Read () -- Defined in `GHC.Read'
      instance (Read a, Read b) => Read (a, b) -- Defined in `GHC.Read'
      instance (Read a, Read b, Read c) => Read (a, b, c)
        -- Defined in `GHC.Read'
      ...plus 25 others
    In the expression: read "1234"
    In an equation for `k': k = read "1234"
    In the expression: let k = read "1234" in print k

SO.hs:3:11:
    No instance for (Show a0) arising from a use of `print'
    The type variable `a0' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Note: there are several potential instances:
      instance Show Double -- Defined in `GHC.Float'
      instance Show Float -- Defined in `GHC.Float'
      instance (Integral a, Show a) => Show (GHC.Real.Ratio a)
        -- Defined in `GHC.Real'
      ...plus 26 others
    In the expression: print k
    In the expression: let k = read "1234" in print k
    In an equation for `main': main = let k = read "1234" in print k

Since read is polymorphic in its return type, it's not clear what k's type is. We need to specify that k shall be, for example, an Int:

main :: IO ()
main = let k = read "1234" :: Int
       in print k

printf's return type is also polymorphic, it must be an instance of PrintfType. Since solve's type is fixed as [Integer], there's only one culprit left: the list [1..]. Indeed, this is also shown by the ambiguous Enum instance:

base.hs:23:46:
    No instance for (Enum a0)
      arising from the arithmetic sequence `1 .. '
    The type variable `a0' is ambiguous

The type checker cannot get the correct type for [1..]. If you specify a type and make the list's type therefore non-ambiguous, everything works fine:

main = mapM_ (uncurry $ printf "%d: %d") (zip ([1..] :: [Integer]) solve)

TL;DR: When GHC yells at you for ambiguous types, annotate them.

like image 95
Zeta Avatar answered Mar 29 '23 11:03

Zeta