You'd end up building a small functional runtime. That's a fun project to understand compilers better. But given that Haskell types are erased at runtime the very thing that makes monads monads wouldn't even be around anymore. All the values would be represented uniformly by some *StgClosure struct and the whole program would just be a mess of casts and projections into these values.
So, we're talking about Haskell passing around void pointers and later doing cross-your-fingers typecasts on them? That doesn't sound very much like the Haskell I keep hearing about. I was expecting something more like an ungodly stack of C++ templates eventually building up a return type containing a record of all side delayed effects of the function. That C++ template could then be manually flattened to a C struct. It would be a gross amount of manual labor. But, it would also be typesafe in plain C.
There is a translation from any typed language into an untyped language. Writing code in that untyped language is not going to be type safe, while the code generated (correctly) in that untyped language from the typed language is still guaranteed to be correct.
It is entirely possible that the only way to get anything safe out of some Haskell code is to rely on checks the Haskell compiler gives you at compile time, which the C compiler cannot give you.
That said, people often underestimate the kinds of guarantees you can bang out of a C compiler, at the cost of a bit of verbosity.
> So, we're talking about Haskell passing around void pointers and later doing cross-your-fingers typecasts on them?
Following on this thought: Every compiler is essentially an assembler programmer! And we all know how error prone it is to code in assembler. So how can the compiler ever produce error-free binaries?