Ticket #1200 (closed bug: fixed)

Opened 6 years ago

Last modified 5 years ago

ghci scripts ending in printf lines fail with Exception: Prelude.undefined

Reported by: dons Owned by:
Priority: normal Milestone: 6.8.2
Component: GHCi Version: 6.6
Keywords: Cc:
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: Difficulty: Unknown
Test Case: Blocked By:
Blocking: Related Tickets:

Description

There appears to be some differences in runhaskell/ghci and ghc when it comes to printf.

Consider this program:

import Text.Printf
import System.Environment

main = do
    [who] <- getArgs
    printf "hello, %s\n" who

when compiled:

$ ghc A.hs
$ ./A world
hello, world

When run in GHci:

$ ghci A.hs
Prelude Main> :set args world
Prelude Main> main
hello, world
*** Exception: Prelude.undefined

Hmm! And in runhaskell:

$ runhaskell A.hs world
hello, world
*** Exception: Prelude.undefined

An ugly 'return ()' seems to help:

import Text.Printf
import System.Environment

main = do
    [who] <- getArgs
    printf "hello, %s\n" who
    return ()

which produces:

$ runhaskell A.hs world
hello, world

As does an explicit annotation:

$ cat A.hs
import Text.Printf
import System.Environment

main = do
    [who] <- getArgs
    printf "hello, %s\n" who :: IO ()

So some defaulting is coming into play?

$ ghci
Prelude> :l A.hs
*Main> :t main
main :: IO t

*Main> :set args world
*Main> main :: IO ()
hello, world

*Main> main :: IO String
hello, world
"*** Exception: Prelude.undefined

Is GHCi/runhaskell giving an overly generous type to 'main'? I note the following is also valid "runhaskell" programs:

$ cat A.hs
main = return "hello, world"

$ runhaskell A.hs
"hello, world"

Change History

Changed 6 years ago by igloo

  • component changed from GHCi to libraries/base
  • milestone set to 6.8

The problem is that main :: IO a, and ghci now tries to print out the value resulting from running an IO action (unless that value has type (), which is why annotations fix it). runhaskell probably shouldn't share this behaviour in my opinion.

printf has things like

instance PrintfType (IO a) where
    spr fmt args = do
    putStr (uprintf fmt (reverse args))
    return undefined

so ghci tries to print out this undefined value.

We could change the instance to IO () so that we could remove the last line, but that's probably not portable enough. Making it

class Unit a where unit :: a
instance Unit () where unit = ()
instance Unit a => PrintfType (IO a) where
    ...
    return unit

would work, but would give some possibly confusing type errors when a type IO a is infered.

Or we could just change the undefineds to errors with an explanatory message.

Changed 6 years ago by simonmar

  • owner set to igloo
  • type changed from bug to merge
  • milestone changed from 6.8 to 6.6.1

I think the right way to fix this is to make runghc evaluate (main >> return ()) instead of just main, which is what I just did:

Tue Mar 13 14:46:14 GMT 2007  Simon Marlow <simonmar@microsoft.com>
  * fix #1200: don't evaluate the result of main

Changed 6 years ago by igloo

It's still going to give odd-looking behaviour in ghci, though.

Changed 6 years ago by simonmar

  • type changed from merge to bug

There's oddness certainly, but I think it's different oddness. In GHCi there's no concept of Main.main; the normal rules for main don't apply.

What I think is odd is that you can't just use printf from the prompt in GHCi without getting an exception. GHCi is defaulting the return type of printf to Integer and then trying to print it. I just talked to Simon PJ about this, and we think it would be sensible to add () to the list of default types in GHCi, which would have the effect of making printf's result default to () and thereby not get printed.

I've change the ticket back to a bug - we should still merge my patch above, but Simon and I are going to make the above change (for 6.8).

Changed 6 years ago by simonpj

  • owner changed from igloo to simonmar

I have committed a patch for this, after discussing with Simon M. I'm not sure whether or not we want it in 6.6.1. But the HEAD is happy, I think.

Still to come: modify the user manual; Simon M is going to do this, so I'm assigning the bug to him.

Simon

Changed 6 years ago by igloo

  • owner changed from simonmar to igloo

Changed 6 years ago by igloo

  • owner igloo deleted
  • milestone changed from 6.6.1 to 6.8

The HEAD doesn't seem to get this quite right, e.g. with

main :: IO ()
main = print def

def :: (Num a, Enum a) => a
def = toEnum 0

instance Num ()

I get

$ ghci def.hs -fno-warn-missing-methods -v0
*Main> main
0
*Main> def
()
$ ghc def.hs -o def -fno-warn-missing-methods -fextended-default-rules
$ ./def
()

whereas I would expect all three to print ().

Changed 6 years ago by simonmar

  • status changed from new to closed
  • resolution set to fixed
  • component changed from libraries/base to GHCi

Ian's example is actually behaving correctly above.

Changed 6 years ago by igloo

  • milestone changed from 6.8 branch to 6.8.2

Changed 5 years ago by simonmar

  • architecture changed from Unknown to Unknown/Multiple

Changed 5 years ago by simonmar

  • os changed from Unknown to Unknown/Multiple
Note: See TracTickets for help on using tickets.