Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to compile a resource into a binary in Haskell?

Say I have a dictionary.txt file, I need to read it into a map and use it in my program, how can I make this dictionary.txt file contained in the compiled exe file?

like image 680
Sawyer Avatar asked Jun 19 '11 11:06

Sawyer


4 Answers

You'll have to come up with your own way to get it into a Map, but http://hackage.haskell.org/package/file-embed will get it into your compiled binary. We use it for embedding templates in some of our web applications.

like image 151
clintm Avatar answered Sep 22 '22 23:09

clintm


You can store serialized data types as bytestring literals. Here's an example:

  • http://code.haskell.org/~dons/code/compiled-constants/
  • http://haskell.org/haskellwiki/Compiling_in_constants

However, the file-embed automates this process, making it easier to do non-trivial embeddings.

All approaches essentially boil down to representing the static data as a bytestring literal,

{-# LANGUAGE OverloadedStrings #-}

import Data.Binary
import qualified Data.Map as M
import qualified Data.ByteString.Char8 as S
import Data.ByteString.Lazy
import Codec.Compression.GZip

--
-- this is a gzip compressed literal bytestring, storing a binary-encoded Data.Map
--
mytable =
    "\US\139\b\NUL\NUL\NUL\NUL\NUL\NUL\ETXEN\
    \\219\SO\194 \f\197\224\188\196\CAN\227\US\
    \\224\171~\NAKc\GS4ce\161`\178\191\215(\176\
    \\190\180\167\231\210\n\241\171\203\191\ti\
    \\157\217\149\249< \ENQ\214\&9>\202\162\179a\
    \\132X\233\ESC=\231\215\164\SYN\157\DC2D\226*\
    \\146\174o\t\167\DLE\209\"i_\240\193\129\199<W\
    \\250nC\CAN\212\CAN\162J\160\141C\178\133\216;\
    \\\@4\144-W\203\209x\205\140\166\RS\163\237]9f\
    \\170\143\ACK\163g\223\STX\184\&7\rH\222\FSW\
    \\130\&7D\197\NUL\164\&0U\193\186\t\186o\
    \\228\180~\NUL\a6\249\137#\SOH\NUL\NUL"

main = print =<< M.lookup "ghc" m
    where
        -- build the table from the bytestring:
        m :: M.Map String (Maybe String)
        m = decode . decompress . fromChunks . return $ mytable
like image 23
Don Stewart Avatar answered Sep 20 '22 23:09

Don Stewart


You can use a custom Makefile/Setup.hs hook and invoke windres (if you're on Windows) or objcopy/elfrc (if you're on Linux) to compile resources to COFF/ELF objects which you can then combine with your Haskell object files to form the final executable. You can then access resources using Haskell FFI like this (not tested):

-- We have an image resource called "_imgdata"
foreign import ccall "&" _imgdata :: CString 

-- Size of _imgdata is 405585 bytes.
imgdata :: CStringLen
imgdata = (_imgdata, 405585)

This solution will be more efficient than using file-embed (no CString -> ByteString conversions going on), but also more involved.

As an aside, I also ended up needing resource file support during my work on cabal-install, so it will probably be integrated into some future version of Cabal (if I'll implement it).

like image 30
Mikhail Glushenkov Avatar answered Sep 22 '22 23:09

Mikhail Glushenkov


I would strongly recommend to use Template Haskell here, to load the file and turn it into a Haskell Datatype. It will not only be compiled into the final binary, but also fully optimized.

like image 7
Lanbo Avatar answered Sep 22 '22 23:09

Lanbo