
Hi Tom,
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 "primSpawnProcess" primitive. 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 where 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 :) Tom