I'm trying to figure out how the basic IO
Haskell functions are defined, so I used this reference and I got to the putChar
function definition:
putChar :: Char -> IO ()
putChar = primPutChar
Now, however, I cannot find more information about this primPutChar
function anywhere. Maybe it might refer to a pre-compiled function, available as binary from a shared object? If that's the case, is it possible to see its source code?
prim*
meansSince 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 modulePreludeBuiltin
and are not shown here
This is still the same in Haskell2010 by the way.
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.
GHC's implementation uses write
with internal buffers to write things, including single characters.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With