Calling a Haskell MD5 hashing function from C, and returning the result back to C land
First, you have way too many comments.  They clutter the code, and some of them are completely redundant.

-- | Hash pointer to array of unsigned chars
hash :: CInt -&gt; Ptr Word8 -&gt; IO (Ptr Word16)


The comment provides no information beyond the function's name and signature.  But it doesn't answer these questions:


What hashing algorithm is being used?
Why is the output Ptr Word16?  UTF-16?
Is the output in hexadecimal, or raw bytes?  If it's hexadecimal, does it use capital or lowercase letters?


Before we dive into foreign API details, we can do a little refactoring.  There's a big chain of pure function application here.  Let's move it outside so it doesn't clutter the scary foreign pointer stuff:

hashPure :: ByteString -&gt; Text
hashPure sbyte =
    let lbyte    = chunk sbyte empty
        digest   = encode . md5 $ lbyte
        hexBytes = toLazyText . foldr (\x y -&gt; y &lt;&gt; hex x) mempty . unpack $ digest 
     in toStrict hexBytes


I think we can improve this a bit.  Let's first take a step back.  Here's all hashPure needs to do:


Hash the input string (a strict ByteString) using MD5
Convert it to a hex string (as Text)


To get there, we have to do a bunch of annoying conversions.  But we can avoid these if we tweak a couple of our library choices.

Because of the Hash instance on MD5Digest, we can eliminate a strict to lazy ByteString conversion:

import Crypto.Classes (hash')
import Data.Digest.Pure.MD5 (MD5Digest)

hashPure :: ByteString -&gt; Text
hashPure sbyte =
    let digest   = encode (hash' sbyte :: MD5Digest)
        hexBytes = toLazyText . foldr (\x y -&gt; y &lt;&gt; hex x) mempty . unpack $ digest 
     in toStrict hexBytes


Your code for converting to hex is bogus.  To illustrate why:

&gt;foldr (\x y -&gt; y &lt;&gt; hex x) mempty [1,2,3]
"321"


Namely:


Your use of foldr flips the order.  What you meant was: foldr (\x y -&gt; hex x &lt;&gt; y)
The hex function does not pad the number to two characters.


To avoid the first problem, you could have just said mconcat (map hex).  foldr/build fusion should optimize it.

To fix the second problem, and avoid two more conversions, you can use the base16-bytestring package.  You could avoid yet another conversion if you make hash return an array of chars instead of wide chars, but I won't do that yet.

hashPure :: ByteString -&gt; Text
hashPure sbyte =
    let digest   = encode (hash' sbyte :: MD5Digest)
        hexBytes = Base16.encode digest
     in decodeUtf8 hexBytes


We can make it a little prettier using point-free style:

hashPure :: ByteString -&gt; Text
hashPure =
    decodeUtf8 . Base16.encode . Serialize.encode . md5
  where
    md5 s = hash' s :: MD5Digest


Now on to the ForeignPtr stuff.  I saved the best for last.

hash :: CInt -&gt; Ptr Word8 -&gt; IO (Ptr Word16)
hash count addr =  do
    fptr &lt;- newForeignPtr_ addr
    let sbyte = fromForeignPtr fptr 0 (fromIntegral count)
    unsafeForeignPtrToPtr . fst &lt;$&gt; asForeignPtr (hashPure sbyte)


Your two points of interaction with C:


Convert a C array of bytes to a ByteString
Convert a Text to a block of wide chars, and return a pointer to it.


Your first conversion is sound, but I would use unsafePackCStringLen instead (which does the same thing):

hash :: CInt -&gt; Ptr Word8 -&gt; IO (Ptr Word16)
hash count addr =  do
    sbyte &lt;- unsafePackCStringLen (castPtr addr, fromIntegral count)
    unsafeForeignPtrToPtr . fst &lt;$&gt; asForeignPtr (hashPure sbyte)


The second conversion is wrong.  ForeignPtr is for managing C objects in Haskell, not the other way around.  The returned pointer will be invalidated when the ForeignPtr returned by asForeignPtr is garbage collected.

You fix this, you would need to malloc a large enough buffer, then copy the text content into it.  Unfortunately, Data.Text.Foreign does not provide a convenient way to do this without a needless allocation.

I would change the signature of the C function from this:

uint16_t *hash(int blksize, uint8_t *block);


To this:

void hash(uint8_t *block, size_t blksize, char out[33]);


Namely:


Write a string to a caller-supplied buffer of the correct size, rather than allocating a buffer that the caller has to free.  A hex-encoded MD5 hash is always 32 characters long.  The 33rd byte is for the zero terminator.
Output a C string instead of UTF-16.  Unless you plan to throw the output into a system call or something that takes UTF-16, you shouldn't be using UTF-16.
Flip pointer and size arguments.  This is mainly a matter of taste.


See if you can implement this part yourself.  This post is already too long.