shackell at cs.york.ac.uk
Thu Feb 21 03:43:45 EST 2008
> To get the hang of using Yhc.Core, I'm writing a little haskell
> interpreter. The interpreter errors out when ever it comes across an
> unknown primitive. The first such primitive I had to implement was
> SEQ. Now I'm on to YHC.Primitive;_E.
> What is the meaning of this type?
_E is a box to protect a value from evaluation. It comes about because
all primitive functions are strict in all arguments, this is correct for
the vast majority of primitives. However for a few primitives we don't
want the primitive to evaluate the argument, one such example is the
foreign import primitive primSpawnProcess :: _E a -> IO ThreadId
primSpawnProcess takes a closure and evaluates that closure in a new
thread, returning the unique Id of the thread it created.
primSpawnProcess can't evaluate it's argument, because if it did the
closure to be run in a new thread wouldn't be run in a new thread, it
would be run in this thread!
primSpawnProcess is used to implement forkIO.
forkIO :: IO () -> IO ThreadId
forkIO action = primSpawnProcess boxed
boxed = _E (unsafePerformIO action)
Thus it is passed the closure inside the _E box. When primSpawnProcess
comes to evaluate it's argument it sees the box, and notes that it is
already evaluated. Thus the closure is passed into primSpawnProcess
unevaluated, inside it's protective box.
Another place that _E is used like this (which you might just come
across) is in the implementation of Data.Array. The crucial point here
is that you are allowed to put unevaluated values inside arrays, however
the array is accessed entirely via primitives, hence the need to box the
values that are being put in an array.
> Is there a place where these
> primitive types are defined? When I print the coreDatas I see:
> data YHC.Primitive;_E b =
> YHC.Primitive;_E b
This is the correct definition of _E, it is just a box. As might be
expected from "YHC.Primitive;_E" it is defined in YHC/Primitive.hs.
> When I print coreFuncs, I see no Case expressions that could extract
> data put into _E.
Indeed this is because the primSpawnProcess function itself (which is
written in C) takes the closure out of the box. After pulling it out of
the box it does the necessary magic to ensure that the closure is
evaluated inside a new thread.
The other major place that _E is used is in the IO monad. This is
probably where you've actually managed to use _E, since I'm guessing
you're probably not using the concurrency features ;)
The IO monad is defined as:
newtype IO a = IO (World -> _E a)
again here the _E is used to prevent evaluation, though why it's
necessary is a fairly complex issue.
Unlike with the other uses of _E, in the IO monad _E is just a normal
(if somewhat boring) datatype, and it is cased on like any other
datatype. Both cases involving _E are in Prelude.hs: one in ">>=",
another in the "showsType" method of the Show instance for the IO monad.
Hope that helps :)
More information about the Yhc