The overloaded return type printf :: PrintfType r => String -> r may be a bit confusing, but it helps to look at the available PrintfType instances:
First we have this instance:
instance (PrintfArg a, PrintfType r) => PrintfType (a -> r)
This one is used to allow printf to accept a variable number of arguments. This makes sense because of currying, but you probably won't think of it as a βtrueβ function type return. For more information on how this works, see How printf works in Haskell? .
Then we have this instance,
instance PrintfType (IO a)
This means that we can use it as an IO action. Like print , this will output the result to standard output. The result of the action is undefined , so you should just ignore it. one
> printf "Foo %d\n" 42 :: IO () Foo 42 > it *** Exception: Prelude.undefined
And finally
instance IsChar c => PrintfType [c]
The type class here basically allows you to make it compatible with Haskell 98. Since Char is the only instance of IsChar , you can think of it as
instance PrintfType String
but this will require an extension of the GHC-specific extension of FlexibleInstances .
What this instance means is that you can also just return a String without printing it, similar to sprintf in C.
> printf "Foo %d\n" 42 :: String "Foo 42\n"
So, depending on whether you want to print the result or just return it, you can replace ??? on IO () or String in your example.
However, there is another problem with your code that the compiler cannot determine which type you need the last argument to, so you need help with the type annotation:
line_length :: Integer -> Integer -> Integer -> Integer -> String line_length ax ay bx by = printf ("The length of the line between the points" ++ "(%d,%d) and (%d,%d) is %.5f\n") ax ay bx by ((((fromIntegral (ax - bx)) ** 2.0) + ((fromIntegral (ay - by))) ** 2.0) ** 0.5 :: Double)
1 This is to avoid extensions. With extensions, we could write instance PrintfType (IO ()) , and the result would be () .
source share