
Hello, I am experimenting with GHC to write low level code (e.g., device drivers, interrupt handlers). The ultimate goal is to write a prototype operating system in Haskell, using the GHC RTS as a kind of microkernel (this appears to be relatively easy by removing all the dependencies on an operating system, and providing a block allocator and interval timers). I have added the necessary primitive operations to generate privileged instructions for IA32 to GHC, and it seems to work fine, good quality assembly code is generated. Now, I would like to make use of do-notation to write cleaner code (threading a State# RealWorld through computations is quite ugly). I want to avoid allocating memory in parts of the code, so I would like to be able to have monadic computations which return unboxed values. Sadly, GHC currently does not appear to allow that. The following example illustrates the problem:
{-# OPTIONS -fglasgow-exts #-}
import GHC.Prim
x :: IO Int# x = return 1#
GHC (HEAD from 2003-08-01) reports: T.lhs:5: Kind error: Expecting a lifted type, but `Int#' is unlifted When checking kinds in `IO Int#' In the type: IO Int# While checking the type signature for `x' T.lhs:7: Couldn't match `*' against `#' When matching types `a' and `Int#' Expected type: a Inferred type: Int# In the first argument of `return', namely `1#' After looking into the problem for a while, I realized that GHC does not have polymorphic kinds - after checking a bunch of definitions, it "zonks" all kind variables to (Type *) and all boxity variables to *. So IO has kind (Type * -> Type *), and cannot be applied to an unboxed value of kind (Type #). GHC has had explicit kind annotations for a while: http://www.haskell.org/pipermail/haskell/2002-February/008906.html So I ought to be able define an IO# type for computations that return unboxed values. (By the way, is there already an unboxed unit value and type?) Strangely, GHC did not recognizes # as the kind of unlifted types, so I added that to the compiler, and hoped that things would work out. Well, almost.
{-# OPTIONS -fglasgow-exts #-}
import GHC.Prim
newtype IO# (a :: # ) = IO# (State# RealWorld -> (# State# RealWorld, a #))
x :: IO# Int# x = IO# (\ s -> (# s, 1# #))
GHC reports: T.lhs:8: Illegal unlifted type argument: Int# In the type: IO# Int# While checking the type signature for `x' The example is accepted if "newtype" is replaced by "type" (and by removing uses of the IO# value constructor), so I can proceed with my experiment by not using do-notation (defining return#, then#, etc appears to work fine). Not being able to use do-notation is slightly inconvenient; is there a reason why newtypes cannot be applied to unlifted type arguments? I expect that I would eventually have been beaten anyway when trying to make an instance of the Monad type class for the above IO# newtype, since Monad is probably regarded as a predicate of kind (* -> *) -> P (where P would be the hypothetical kind of propositions); application to an operator of kind (# -> *) would fail. What would need to be done to extend the compiler so that it allows IO computations to be able to return unboxed values? For some reason I am worried that "just" having polymorphic kind may not be enough. I also fail to understand all the implications of having polymorphic kinds, so I would greatly appreciate if one of the GHC experts could say something about that too. -- Sebastien

The ultimate goal is to write a prototype operating system in Haskell, using the GHC RTS as a kind of microkernel
As a useful stepping stone towards that goal, you might look at Utah's OSKit: http://www.cs.utah.edu/flux/oskit/ It gives you a bunch of useful bits like bootloaders, device drivers, filesystems and network stacks with well-defined interfaces which let you quickly get a system up and running and then gradually replace the bits. People have used the OSKit to build Scheme, Java and ML-based systems. See the links at the end of this page: http://www.cs.utah.edu/flux/oskit/projects.html I'd also recommend reading the papers about the Janos project (a Java OS) especially: Processes in KaffeOS: Isolation, Resource Management, and Sharing in Java Drawing the Red Line in Java. both available from: http://www.cs.utah.edu/flux/janos/pubs.html These talk about OS features which appear to be redundant when you work in a high-level, strongly-typed language but which prove to have significant value when you look more closely. Finally, I recommend looking at INRIA's Devil project http://compose.labri.fr/prototypes/devil/ Devil is an IDL for defining the interface to devices - like what does each bit in a control register or interrupt mask do? They have somewhat limited goals but for what they do, they are many, many, many times better than the status quo as exemplified by the Linux/*BSD/Windows/etc kernels. Hope this is helpful. -- Alastair Reid
participants (2)
-
Alastair Reid
-
Sebastien Carlier