What prim*
means
Since you're asking this question in terms of the report, let's also answer this question in terms of the report:
Primitives that are not definable in Haskell , indicated by names starting with "prim
", are defined in a system dependent manner in module PreludeBuiltin
and are not shown here
This is still the same in Haskell2010 by the way.
How it's implemented in GHC
However, you can have a look at base
's source to see how it's implemented in GHC:
putChar :: Char -> IO ()
putChar c = hPutChar stdout c
From there you're going deep into the rabbit hole. How does hPutChar
know how to print stuff? Well, it doesn't. It only "buffers" and checks that you can write:
hPutChar :: Handle -> Char -> IO ()
hPutChar handle c = do
c `seq` return ()
wantWritableHandle "hPutChar" handle $ handle_ -> do
hPutcBuffered handle_ c
The writing is done in writeCharBuffer
which fills an internal buffer until it's full (or a line has been reached—it actually depends on the buffer mode):
writeCharBuffer h_@Handle__{..} !cbuf = do
-- much code omitted, like buffering
bbuf'' <- Buffered.flushWriteBuffer haDevice bbuf'
-- more code omitted, like buffering
So where is flushWriteBuffer
defined? It's actually part of stdout
:
stdout :: Handle
stdout = unsafePerformIO $ do
setBinaryMode FD.stdout
enc <- getLocaleEncoding
mkHandle FD.stdout "<stdout>" WriteHandle True (Just enc)
nativeNewlineMode{-translate newlines-}
(Just stdHandleFinalizer) Nothing
stdout :: FD
stdout = stdFD 1
And a file descriptor (FD
) is an instance of BufferedIO
:
instance BufferedIO FD where
-- some code omitted
flushWriteBuffer fd buf = writeBuf' fd buf
and writeBuf
uses instance GHC.IO.Device.RawIO FD
's write
, and that ultimately leads to:
writeRawBufferPtr loc !fd buf off len
| isNonBlocking fd = unsafe_write -- unsafe is ok, it can't block
| otherwise = do r <- unsafe_fdReady (fdFD fd) 1 0 0
if r /= 0
then write
else do threadWaitWrite (fromIntegral (fdFD fd)); write
where
do_write call = fromIntegral `fmap`
throwErrnoIfMinus1RetryMayBlock loc call
(threadWaitWrite (fromIntegral (fdFD fd)))
write = if threaded then safe_write else unsafe_write
unsafe_write = do_write (c_write (fdFD fd) (buf `plusPtr` off) len)
safe_write = do_write (c_safe_write (fdFD fd) (buf `plusPtr` off) len)
where we can see c_safe_write
and c_write
, which are usually bindings to C library functions:
foreign import capi unsafe "HsBase.h write"
c_write :: CInt -> Ptr Word8 -> CSize -> IO CSsize
So, putChar
uses write
. At least in GHC's implementation. The report however doesn't require that implementation, so another compiler/runtime is allowed to use other functions.
TL;DR
GHC's implementation uses write
with internal buffers to write things, including single characters.