include-file-0.1.0.0: Development/IncludeFile.hs
-- | Inclusion of files in source code via Template Haskell.
--
-- When distributing executables, sometimes it is required to attach some other resources in
-- files. Using 'includeFileInSource' you avoid this problem by including those files inside
-- the executable at compile time.
--
-- = Example
--
-- A quick example. I want to include a small image (@foo.png@) in the executable. I would do:
--
-- > {-# LANGUAGE TemplateHaskell #-}
-- >
-- > import Development.IncludeFile
-- >
-- > $(includeFileInSource "foo.png" "myImage")
-- >
--
-- This defines the @myImage@ value with type 'S.ByteString' and with the content of the file @foo.png@.
--
-- = Using 'includeFileInSource'
--
-- The following entities must be in scope when calling 'includeFileInSource':
--
-- * A @ByteString@ type.
-- * A @Word8@ type, instance of the 'Num' class (at least with the 'fromInteger' method implemented).
-- * A @pack :: [Word8] -> ByteString@ function.
--
-- This module re-export these for convenience, but you can use your own types (for example, using
-- /lazy/ bytestrings instead of /strict/ bytestrings).
--
-- = Using lazy bytestrings
--
-- To use lazy bytestrings, instead of importing this full module, import it like this:
--
-- > import Data.ByteString.Lazy (ByteString,pack)
-- > import Development.IncludeFile (includeFileInSource,Word8)
--
-- Needless to say, if you have already imported any of those entities, you don't have to do it again.
--
-- = Performance impact
--
-- Benchmarks confirm that it is /much/ faster to use an included bytestring than reading it from a file.
--
-- > benchmarking include-file
-- > time 1.814 ns (1.799 ns .. 1.826 ns)
-- > 1.000 R² (0.999 R² .. 1.000 R²)
-- > mean 1.808 ns (1.797 ns .. 1.819 ns)
-- > std dev 37.48 ps (31.27 ps .. 46.78 ps)
-- >
-- > benchmarking read-file
-- > time 4.869 μs (4.798 μs .. 4.938 μs)
-- > 0.998 R² (0.998 R² .. 0.999 R²)
-- > mean 4.911 μs (4.857 μs .. 4.968 μs)
-- > std dev 178.8 ns (150.1 ns .. 212.5 ns)
--
-- = Large files
--
-- Do not use 'includeFileInSource' with large files. The limit between what /is/ and what is /not/ a large
-- file remains uncertain.
--
module Development.IncludeFile (
-- * Including files
includeFileInSource
-- * Convenient re-exports
, B.ByteString
, Word8
, B.pack
) where
import qualified Data.ByteString as B
import Data.Word (Word8)
import Language.Haskell.TH
-- | Define a value of type @ByteString@ (where @ByteString@ is whatever @ByteString@ type is in scope)
-- with the content of a file.
includeFileInSource :: FilePath -- ^ Path to file
-> String -- ^ Haskell value name
-> Q [Dec]
includeFileInSource fp n = do
b <- runIO $ B.readFile fp
let ws = B.unpack b
wtype = ConT $ mkName "Word8"
btype = ConT $ mkName "ByteString"
return
[ SigD (mkName n) btype
, FunD (mkName n) [Clause [] (NormalB $
AppE (SigE (VarE $ mkName "pack") $ ArrowT `AppT` (ListT `AppT` wtype) `AppT` btype)
$ ListE $ fmap (LitE . IntegerL . fromIntegral) ws
) []]
]