I am trying to get started with the LLVM binding for Haskell. A great place to start is Hello World.
The following is from a blog by the author of the binding.
bldGreet :: CodeGenModule (Function (IO ()))
bldGreet = do
puts <- newNamedFunction ExternalLinkage "puts" :: TFunction (Ptr Word8 -> IO Word32)
greetz <- createStringNul "Hello, World!"
func <- createFunction ExternalLinkage $ do
tmp <- getElementPtr greetz (0::Word32, (0::Word32, ()))
call puts tmp -- Throw away return value.
ret ()
return func
It does not compile.
Instead I get "Ambiguous type variable n0' in the constraint:
(type-level-0.2.4:Data.TypeLevel.Num.Sets.NatI n0)
arising from a use of
getElementPtr0'
Probable fix: add a type signature that fixes these type variable(s)"
Here is a variation that does work
llvmModule :: TFunction (IO Word32)
llvmModule =
withStringNul "Hello world!" $ \s -> do
puts <- newNamedFunction ExternalLinkage "puts" :: TFunction (Ptr Word8 -> IO Word32)
main <- newNamedFunction ExternalLinkage "main" :: TFunction (IO Word32)
defineFunction main $ do
tmp <- getElementPtr0 s (0::Word32, ())
_ <- call puts tmp
ret (0::Word32)
return main
The first seems more natural. The question I have is what is the ambiguity in the first, and how do I fix it. The second question I have is why is the second not ambiguous.
Okay. So I solved the problem. It is indeed a typeclass thing. And it has only confused me more. However, I do have an answer to the solution. But do feel free to help my understanding out. First, some digging. The function createStringNul has type
createString :: String -> TGlobal (Array n Word8)
Fine. The problem the compiler was having is that the "n" in the Array type is ambiguous. It could literally be anything in the world. Lookup, Array, and you see
newtype Array n a
Now it is not so obvious, but upon a little digging, in particular wrt the call to getElementPtr, one finds that the n, really should be a Nat n, which is a type-level way to fix the size of the array. Now, the definition of Array n a, does not actually care it is really just a type synonym for [a]. So you can use D0, or D9 or whatever you want from the Data.TypeLevel.Num.Reps package. Fixing the size of the array, while a good idea was not actually taken into consideration by this function. But anyways, changing greetz <- createStringNul "Hello, World!" to greetz <- createStringNul "Hello, World!" :: TGlobal (Array D0 Word8) works.
Here is the interesting part... I did not expect it to work. D0 is supposed to be 0, so I did not understand why it was allowing me to store so many characters in a 0 size "Array" However, if you look at the source code, it is immediately clear that the type restriction is not actually payed attention to.
Fine, whatever, upon compiling one realizes that the createStringNul is deprecated, and withStringNul is preferred instead. Except I don't completely understand how withStringNul's types work.
createStringNul
is marked as deprecated in the current version of llvm
(3.0.1.0). Use withStringNul
:
import Data.Word
import LLVM.Core
bldGreet :: CodeGenModule (Function (IO ()))
bldGreet = do
puts <- newNamedFunction ExternalLinkage "puts" :: TFunction (Ptr Word8 -> IO Word32)
func <- withStringNul "Hello, World!" $ \greetz ->
createFunction ExternalLinkage $ do
tmp <- getElementPtr greetz (0::Word32, (0::Word32, ()))
_ <- call puts tmp -- Throw away return value.
ret ()
return func
As to what causes the error in the first example, it's related to the fact that withStringNul
has a more informative type: withStringNul :: String -> (forall n . Nat n => Global (Array n Word8) -> a) -> a
-- cf. to createStringNul :: String -> TGlobal (Array n Word8)
. The function argument of withStringNul
has a higher-rank type - it means that the function works for all n
where n
is a natural number.
If you really want to use createStringNul
, you can make the first example compile by adding an explicit type signature for greetz
:
{-# LANGUAGE TypeOperators #-}
module Test
where
import Data.Word
import Data.TypeLevel.Num.Reps
import LLVM.Core
bldGreet :: CodeGenModule (Function (IO ()))
bldGreet = do
puts <- newNamedFunction ExternalLinkage "puts" :: TFunction (Ptr Word8 -> IO Word32)
greetz <- createStringNul "Hello, World!"
func <- createFunction ExternalLinkage $ do
tmp <- getElementPtr (greetz :: Global (Array (D1 :* D3) Word8)) (0::Word32, (0::Word32, ()))
call puts tmp -- Throw away return value.
ret ()
return func
The :*
type constructor comes from the type-level package and is used to construct type-level numbers. D1 :* D3
means that the array has size 13.
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