Search code examples
haskellvariadic-functionsfunction-parameterpolyvariadic

Printf-like function


I'm trying to write a function with an arbitrary number of arguments. These arguments can be Int or String. And I have a problem with ints. For some reason (which I don't understand), the value 1 becomes a source of ambiguity. How to deal with this error and what is the source of it?

{-# LANGUAGE FlexibleInstances #-}
import Data.Typeable

data PackArgTypes = PackInt Int | PackString String
                              deriving Show

class PackArg t where
  toPackArg :: t -> PackArgTypes

instance PackArg Int where
  toPackArg = PackInt . fromIntegral
instance PackArg Integer where
  toPackArg = PackInt . fromIntegral

instance PackArg String where
  toPackArg = PackString

class PackType t where
  pack' :: String -> [PackArgTypes] -> t

instance PackType (IO a) where
  pack' fmt acc = do
    print fmt
    mapM_ print acc
    return undefined

instance (PackArg a, PackType r) => PackType (a -> r) where
    pack' fmt acc = \x -> pack' fmt $ acc ++ [toPackArg x]

pack :: (PackType t) => String -> t
pack fmt = pack' fmt []

main :: IO ()
main = do
  pack "asd" "qwe" "asd"(1 :: Int) -- Ok
  pack "asd" "qwe" "asd" 1         -- Sad

In said case I've got the error

test1.hs:33:3-6: No instance for (PackArg a0) arising from a use of ‘pack’ …
The type variable ‘a0’ is ambiguous
Note: there are several potential instances:
  instance PackArg String
    -- Defined at /home/knesterov/test1.hs:13:10
  instance PackArg Int -- Defined at /home/knesterov/test1.hs:10:10
In a stmt of a 'do' block: pack "asd" "qwe" "asd" 1
In the expression: do { pack "asd" "qwe" "asd" 1 }
In an equation for ‘main’: main = do { pack "asd" "qwe" "asd" 1 }
test1.hs:33:26: No instance for (Num a0) arising from the literal ‘1’ …
The type variable ‘a0’ is ambiguous
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 7 others
In the fourth argument of ‘pack’, namely ‘1’
In a stmt of a 'do' block: pack "asd" "qwe" "asd" 1
In the expression: do { pack "asd" "qwe" "asd" 1 }
Compilation failed.

EDIT

Thanks to user5402 who pointed to the difference between ghci and ghc. Ghci enables extension ExtendedDefaultRules by default

So with three additional lines my sample works with no errors.

{-# LANGUAGE ExtendedDefaultRules #-}
instance PackArg Integer where
  toPackArg = PackInt . fromIntegral

Solution

  • The problem is here:

    The type variable ‘a0’ is ambiguous
    Note: there are several potential instances:
      instance Num Double -- Defined in ‘GHC.Float’
      instance Num Float -- Defined in ‘GHC.Float’
      ...
    

    Recall that the literal 1 can be interpreted as a value any Num type, so GHC doesn't know which one to select - e.g. Int, Double, Rational, etc.

    The standard Text.Printf module has the same problem:

    import Text.Printf
    
    main = putStrLn $ printf "%d" 3
    

    gives he same error.

    Note that if you won't get an error if you enter this code into ghci because there are defaulting rules which make the expression 1 monomorphic instead of polymorphic.