Can anybody explain GHC's definition of IO?

前端 未结 3 1413
悲哀的现实
悲哀的现实 2021-01-13 10:23

The title is pretty self-descriptive, but there\'s one part that caught my attention:

newtype IO a = IO (State# RealWorld -> (# State# RealWorld, a #))
         


        
3条回答
  •  清歌不尽
    2021-01-13 11:25

    Can anybody explain GHC's definition of IO?

    It's based on the pass-the-planet model of I/O:

    An IO computation is a function that (logically) takes the state of the world, and returns a modified world as well as the return value. Of course, GHC does not actually pass the world around; instead, it passes a dummy “token”, to ensure proper sequencing of actions in the presence of lazy evaluation, and performs input and output as actual side effects!

    (from A History of Haskell by Paul Hudak, John Hughes, Simon Peyton Jones and Philip Wadler; page 26 of 55.)

    Using that description as a guide:

    newtype IO a   =  IO (FauxWorld -> (# FauxWorld, a #))
    

    where:

    type FauxWorld =  State# RealWorld
    

    Why bother with bulky world-values when the I/O model provides the option for using side-effects diligently?

    [...] a machinery whose most eminent characteristic is state [means] the gap between model and machinery is wide, and therefore costly to bridge. [...]
    This has in due time also been recognized by the protagonists of functional languages.[...]

    Niklaus Wirth.

    Now that we're on the topic of implementation details:

    I'm just curious as to why the particular GHC version is written like it is?

    It's primarily to avoid gratuitous runtime evaluation and heap usage:

    • State# RealWorld and the unboxed tuple (# ..., ... #) are unlifted types - in GHC, they don't take up space in the heap. Being unlifted also means they can be used immediately without prior evaluation.

    • The use of State# to define IO (rather than using a world-type directly) reduces RealWorld to an abstract tag-type:

      type ST# s a   =  State# s -> (# State# s, a #)
      
      newtype IO a   =  IO (ST# RealWorld a)
      

      ST# can then be reused elsewhere:

      newtype ST s a =  ST (ST# s a)
      

      For more information, see John Launchbury and Simon Peyton Jones's State in Haskell.

    The Realworld and unlifted types are both GHC-specific extensions.

提交回复
热议问题