diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) 2017 Gabriel Gonzalez
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+    * Neither the name of Gabriel Gonzalez nor the names of other contributors
+      may be used to endorse or promote products derived from this software
+      without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/dhall-bash.cabal b/dhall-bash.cabal
new file mode 100644
--- /dev/null
+++ b/dhall-bash.cabal
@@ -0,0 +1,54 @@
+Name: dhall-bash
+Version: 1.0.0
+Cabal-Version: >=1.8.0.2
+Build-Type: Simple
+Tested-With: GHC == 7.10.2, GHC == 8.0.1
+License: BSD3
+License-File: LICENSE
+Copyright: 2017 Gabriel Gonzalez
+Author: Gabriel Gonzalez
+Maintainer: Gabriel439@gmail.com
+Bug-Reports: https://github.com/Gabriel439/Haskell-Dhall-Bash-Library/issues
+Synopsis: Compile Dhall to Bash
+Description:
+    Use this package if you want to compile Dhall expressions to Bash.
+    You can use this package as a library or an executable:
+    .
+    * See the "Dhall.Bash" module if you want to use this package as a library
+    .
+    * Use the @dhall-to-bash@ if you want an executable
+    .
+    The "Dhall.Bash" module also contains instructions for how to use this
+    package
+Category: Compiler
+Source-Repository head
+    Type: git
+    Location: https://github.com/Gabriel439/Haskell-Dhall-Bash-Library
+
+Library
+    Hs-Source-Dirs: src
+    Build-Depends:
+        base               >= 4.8.0.0 && < 5   ,
+        bytestring                       < 0.11,
+        containers                       < 0.6 ,
+        dhall              >= 1.0.1   && < 1.3 ,
+        neat-interpolation               < 0.4 ,
+        shell-escape                     < 0.3 ,
+        text-format                      < 0.4 ,
+        text               >= 0.2     && < 1.3 ,
+        vector             >= 0.3     && < 0.13
+    Exposed-Modules: Dhall.Bash
+    GHC-Options: -Wall
+
+Executable dhall-to-bash
+    Hs-Source-Dirs: exec
+    Main-Is: Main.hs
+    Build-Depends:
+        base             >= 4.8.0.0  && < 5   ,
+        bytestring                      < 0.11,
+        dhall            >= 1.0.1    && < 1.3 ,
+        dhall-bash                            ,
+        optparse-generic >= 1.1.1    && < 1.2 ,
+        trifecta         >= 1.6      && < 1.7 ,
+        text             >= 0.11.1.0 && < 1.3
+    GHC-Options: -Wall
diff --git a/exec/Main.hs b/exec/Main.hs
new file mode 100644
--- /dev/null
+++ b/exec/Main.hs
@@ -0,0 +1,74 @@
+{-# LANGUAGE DataKinds         #-}
+{-# LANGUAGE DeriveAnyClass    #-}
+{-# LANGUAGE DeriveGeneric     #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards   #-}
+{-# LANGUAGE TypeOperators     #-}
+
+module Main where
+
+import Control.Exception (SomeException)
+import Data.ByteString (ByteString)
+import Options.Generic (Generic, ParseRecord, type (<?>)(..))
+import System.Exit (ExitCode(..))
+import Text.Trifecta.Delta (Delta(..))
+
+import qualified Control.Exception
+import qualified Data.ByteString
+import qualified Data.Text.Lazy.IO
+import qualified Dhall
+import qualified Dhall.Bash
+import qualified Dhall.Import
+import qualified Dhall.Parser
+import qualified Dhall.TypeCheck
+import qualified GHC.IO.Encoding
+import qualified Options.Generic
+import qualified System.Exit
+import qualified System.IO
+
+data Options = Options
+    { explain :: Bool
+        <?> "Explain error messages in detail"
+    , declare :: Maybe ByteString
+        <?> "Declare the given variable as a statement instead of an expression"
+    } deriving (Generic, ParseRecord)
+
+main :: IO ()
+main = handle (do
+    GHC.IO.Encoding.setLocaleEncoding GHC.IO.Encoding.utf8
+    Options {..} <- Options.Generic.getRecord "Compile Dhall to Bash"
+
+    (if unHelpful explain then Dhall.detailed else id) (do
+        inText <- Data.Text.Lazy.IO.getContents
+
+        expr <- case Dhall.Parser.exprFromText (Directed "(stdin)" 0 0 0 0) inText of
+            Left  err  -> Control.Exception.throwIO err
+            Right expr -> return expr
+
+        expr' <- Dhall.Import.load expr
+        case Dhall.TypeCheck.typeOf expr' of
+            Left  err -> Control.Exception.throwIO err
+            Right _   -> return ()
+
+        bytes <- case unHelpful declare of
+            Nothing  -> do
+                case Dhall.Bash.dhallToExpression expr' of
+                    Left  err   -> Control.Exception.throwIO err
+                    Right bytes -> return bytes
+            Just var -> do
+                case Dhall.Bash.dhallToStatement expr' var of
+                    Left  err   -> Control.Exception.throwIO err
+                    Right bytes -> return bytes
+        Data.ByteString.putStr bytes ))
+
+handle :: IO a -> IO a
+handle = Control.Exception.handle handler
+  where
+    handler :: SomeException -> IO a
+    handler e = case Control.Exception.fromException e of
+        Just ExitSuccess -> do
+            Control.Exception.throwIO e
+        _ -> do
+            System.IO.hPutStrLn System.IO.stderr ""
+            System.IO.hPrint    System.IO.stderr e
+            System.Exit.exitFailure
diff --git a/src/Dhall/Bash.hs b/src/Dhall/Bash.hs
new file mode 100644
--- /dev/null
+++ b/src/Dhall/Bash.hs
@@ -0,0 +1,302 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE OverloadedStrings  #-}
+{-# LANGUAGE QuasiQuotes        #-}
+
+{-| This library exports two utilities for compiling Dhall expressions to Bash:
+
+    * `dhallToExpression`, which emits a Bash expression (i.e. a valid
+      right-hand side for an assignment)
+
+    * `dhallToStatement`, which emits a Bash @declare@ or @unset@ statement
+      suitable for use with `eval`
+
+    `dhallToExpression` only supports the conversion of primitive values, such
+    as:
+
+    * @Bool@ - which translates to a string that is either @"true"@ or @"false"@
+    * @Natural@ - which translates to a Bash integer
+    * @Integer@ - which translates to a Bash integer
+    * @Text@ - which translates to a Bash string (properly escaped if necessary)
+
+    The @dhall-to-bash@ executable by default tries to compile Dhall expressions
+    to Bash expressions using the `dhallToExpression` function.  For example:
+
+> $ dhall-to-bash <<< 'True'
+> true
+> $ dhall-to-bash <<< 'False'
+> false
+> $ dhall-to-bash <<< '1'
+> 1
+> $ dhall-to-bash <<< '+1'
+> 1
+> $ dhall-to-bash <<< '"ABC"'
+> ABC
+> $ dhall-to-bash <<< '" X "'
+> $' X '
+> $ dhall-to-bash <<< 'Natural/even +100'
+> true
+
+    The output of `dhallToExpression` is a valid Bash expression that can be
+    embedded anywhere Bash expressions are valid, such as the right-hand side of
+    an assignment statement:
+
+> $ FOO=$(dhall-to-bash <<< 'List/length Integer [1, 2, 3]')
+> $ echo "${FOO}"
+> 3
+
+    `dhallToStatement` supports a wider range of expressions by also adding
+    support for:
+
+    * @Optional@ - which translates to a variable which is either set or unset
+    * @List@ - which translates to a Bash array
+    * records - which translate to Bash associative arrays
+
+    The @dhall-to-bash@ executable can emit a statement instead of an expression
+    if you add the @--declare@ flag specifying which variable to set or unset.
+    For example:
+
+> $ dhall-to-bash --declare FOO <<< '[] : Optional Integer'
+> unset FOO
+> $ dhall-to-bash --declare FOO <<< '[1] : Optional Integer'
+> declare -r -i FOO=1
+> $ dhall-to-bash --declare FOO <<< '[[1] : Optional Integer] : Optional (Optional Integer)'
+> declare -r -i FOO=1
+> $ dhall-to-bash --declare FOO <<< '[[] : Optional Integer] : Optional (Optional Integer)'
+> unset FOO
+> $ dhall-to-bash --declare FOO <<< '[1, 2, 3]'
+> declare -r -a FOO=(1 2 3)
+> $ dhall-to-bash --declare FOO <<< '{ bar = 1, baz = True }'
+> declare -r -A FOO=([bar]=1 [baz]=true)
+
+    The output of `dhallToExpression` is either a @declare@ or @unset@ Bash
+    statement that you can pass to @eval@:
+
+> $ eval $(dhall-to-bash --declare FOO <<< '{ bar = 1, baz = True }')
+> $ echo "${FOO[bar]}"
+> 1
+> $ echo "${FOO[baz]}"
+> true
+
+    @dhall-to-bash@ declares variables read-only (i.e. @-r@) to prevent you from
+    accidentally overwriting, deleting or mutating variables:
+
+> $ eval $(dist/build/dhall-to-bash/dhall-to-bash --declare BAR <<< '1')
+> $ echo "${BAR"}
+> 1
+> $ unset BAR
+> bash: unset: BAR: cannot unset: readonly variable
+> $ eval $(dist/build/dhall-to-bash/dhall-to-bash --declare BAR <<< '2')
+> bash: declare: BAR: readonly variable
+
+-}
+
+module Dhall.Bash (
+    -- * Dhall to Bash
+      dhallToExpression
+    , dhallToStatement
+
+    -- * Exceptions
+    , ExpressionError(..)
+    , StatementError(..)
+    ) where
+
+import Control.Exception (Exception)
+import Data.Bifunctor (first)
+import Data.ByteString
+import Data.Monoid ((<>))
+import Data.Typeable (Typeable)
+import Dhall.Core (Expr(..))
+import Dhall.TypeCheck
+
+import qualified Data.Foldable
+import qualified Data.Map
+import qualified Data.Text
+import qualified Data.Text.Buildable
+import qualified Data.Text.Encoding
+import qualified Data.Text.Lazy
+import qualified Data.Text.Lazy.Builder
+import qualified Data.Vector
+import qualified Dhall.Core
+import qualified NeatInterpolation
+import qualified Text.ShellEscape
+
+_ERROR :: Data.Text.Text
+_ERROR = "\ESC[1;31mError\ESC[0m"
+
+{-| This is the exception type for errors that might arise when translating
+    Dhall expressions to Bash statements
+
+    Because the majority of Dhall language features do not easily translate to
+    Bash this just returns the expression that failed
+-}
+data StatementError
+    = UnsupportedStatement (Expr X X)
+    | UnsupportedSubexpression (Expr X X)
+    deriving (Typeable)
+
+instance Show StatementError where
+    show (UnsupportedStatement e) =
+        Data.Text.unpack [NeatInterpolation.text|
+$_ERROR: Cannot translate to a Bash statement
+
+Explanation: Only primitive values, records, ❰List❱s, and ❰Optional❱ values can
+be translated from Dhall to a Bash statement
+
+The following Dhall expression could not be translated to a Bash statement:
+
+↳ $txt
+|]
+      where
+        txt = Data.Text.Lazy.toStrict (Dhall.Core.pretty e)
+    show (UnsupportedSubexpression e) =
+        -- Carefully note: No tip suggesting `--declare` since it won't work
+        -- here (and the user is already using `--declare`)
+        Data.Text.unpack [NeatInterpolation.text|
+$_ERROR: Cannot translate to a Bash expression
+
+Explanation: Only primitive values can be translated from Dhall to a Bash
+expression
+
+The following Dhall expression could not be translated to a Bash expression:
+
+↳ $txt
+|]
+      where
+        txt = Data.Text.Lazy.toStrict (Dhall.Core.pretty e)
+
+instance Exception StatementError
+
+{-| This is the exception type for errors that might arise when translating
+    Dhall expressions to Bash expressions
+
+    Because the majority of Dhall language features do not easily translate to
+    Bash this just returns the expression that failed
+-}
+data ExpressionError = UnsupportedExpression (Expr X X) deriving (Typeable)
+
+instance Show ExpressionError where
+    show (UnsupportedExpression e) =
+        Data.Text.unpack [NeatInterpolation.text|
+$_ERROR: Cannot translate to a Bash expression
+
+Explanation: Only primitive values can be translated from Dhall to a Bash
+expression
+
+The following Dhall expression could not be translated to a Bash expression:
+
+↳ $txt$tip
+|]
+      where
+        txt = Data.Text.Lazy.toStrict (Dhall.Core.pretty e)
+
+        tip = case e of
+            OptionalLit _ _ -> "\n\n" <> [NeatInterpolation.text|
+Tip: You can convert an ❰Optional❱ value to a Bash statement using the --declare
+flag
+|]
+            ListLit _ _ -> "\n\n" <> [NeatInterpolation.text|
+Tip: You can convert a ❰List❱ to a Bash statement using the --declare flag
+|]
+            RecordLit _ -> "\n\n" <> [NeatInterpolation.text|
+Tip: You can convert a record to a Bash statement using the --declare flag
+|]
+            _ -> ""
+
+instance Exception ExpressionError
+
+{-| Compile a Dhall expression to a Bash statement that @declare@s or @unset@s a
+    a variable of your choice
+
+    This only supports:
+
+    * @Bool@s
+    * @Natural@s
+    * @Integer@s
+    * @Text@s
+    * @Optional@s
+    * @List@s
+    * records
+ -}
+dhallToStatement
+    :: Expr s X
+    -- ^ Dhall expression to compile
+    -> ByteString
+    -- ^ Variable to @declare@ or @unset@
+    -> Either StatementError ByteString
+    -- ^ Bash statement or compile failure
+dhallToStatement expr0 var0 = go (Dhall.Core.normalize expr0)
+  where
+    var = Text.ShellEscape.bytes (Text.ShellEscape.bash var0)
+
+    adapt (UnsupportedExpression e) = UnsupportedSubexpression e
+
+    go (BoolLit a) = do
+        go (TextLit (if a then "true" else "false"))
+    go (NaturalLit a) = do
+        go (IntegerLit (fromIntegral a))
+    go (IntegerLit a) = do
+        e <- first adapt (dhallToExpression (IntegerLit a))
+        let bytes = "declare -r -i " <> var <> "=" <> e
+        return bytes
+    go (TextLit a) = do
+        e <- first adapt (dhallToExpression (TextLit a))
+        let bytes = "declare -r " <> var <> "=" <> e
+        return bytes
+    go (ListLit _ bs) = do
+        bs' <- first adapt (mapM dhallToExpression bs)
+        let bytes
+                =   "declare -r -a "
+                <>  var
+                <>  "=("
+                <>  Data.ByteString.intercalate " " (Data.Foldable.toList bs')
+                <>  ")"
+        return bytes
+    go (OptionalLit _ bs) = do
+        if Data.Vector.null bs
+            then do
+                let bytes = "unset " <> var
+                return bytes
+            else go (Data.Vector.head bs)
+    go (RecordLit a) = do
+        let process (k, v) = do
+                v' <- dhallToExpression v
+                let bytes = Data.Text.Encoding.encodeUtf8 (Data.Text.Lazy.toStrict k)
+                let k'    = Text.ShellEscape.bytes (Text.ShellEscape.bash bytes)
+                return ("[" <> k' <> "]=" <> v')
+        kvs' <- first adapt (traverse process (Data.Map.toList a))
+        let bytes
+                =   "declare -r -A "
+                <>  var
+                <>  "=("
+                <>  Data.ByteString.intercalate " " kvs'
+                <>  ")"
+        return bytes
+    go e = Left (UnsupportedStatement e)
+
+{-| Compile a Dhall expression to a Bash expression
+
+    This only supports:
+
+    * @Bool@s
+    * @Natural@s
+    * @Integer@s
+    * @Text@s
+ -}
+dhallToExpression
+    :: Expr s X
+    -- ^ Dhall expression to compile
+    -> Either ExpressionError ByteString
+    -- ^ Bash expression or compile failure
+dhallToExpression expr0 = go (Dhall.Core.normalize expr0)
+  where
+    go (BoolLit a) = do
+        go (TextLit (if a then "true" else "false"))
+    go (NaturalLit a) = do
+        go (IntegerLit (fromIntegral a))
+    go (IntegerLit a) = do
+        go (TextLit (Data.Text.Buildable.build a))
+    go (TextLit a) = do
+        let text  = Data.Text.Lazy.Builder.toLazyText a
+        let bytes = Data.Text.Encoding.encodeUtf8 (Data.Text.Lazy.toStrict text)
+        return (Text.ShellEscape.bytes (Text.ShellEscape.bash bytes))
+    go e = Left (UnsupportedExpression e)
