diff --git a/HSmarty.cabal b/HSmarty.cabal
--- a/HSmarty.cabal
+++ b/HSmarty.cabal
@@ -1,5 +1,5 @@
 name:                HSmarty
-version:             0.4.1
+version:             0.4.2
 synopsis:            Small template engine
 description:         Haskell implementation of a subset of the PHP-Smarty template language
 Homepage:            https://github.com/agrafix/HSmarty
@@ -8,7 +8,7 @@
 license-file:        LICENSE
 author:              Alexander Thiemann <mail@athiemann.net>
 maintainer:          Alexander Thiemann <mail@athiemann.net>
-copyright:           (c) 2013 - 2020 by Alexander Thiemann
+copyright:           (c) 2013 - 2022 by Alexander Thiemann
 category:            Text
 build-type:          Simple
 cabal-version:       >=1.10
@@ -29,7 +29,7 @@
   default-language:  Haskell2010
   build-depends:
                      HTTP,
-                     aeson >=0.8,
+                     aeson >=2.0,
                      attoparsec >=0.11,
                      attoparsec-expr >=0.1.1,
                      base >= 4.8 && < 5,
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2013 - 2020, Alexander Thiemann <mail@thiemann.at>
+Copyright (c) 2013 - 2022, Alexander Thiemann <mail@thiemann.at>
 
 All rights reserved.
 
diff --git a/src/Text/HSmarty/Parser/Smarty.hs b/src/Text/HSmarty/Parser/Smarty.hs
--- a/src/Text/HSmarty/Parser/Smarty.hs
+++ b/src/Text/HSmarty/Parser/Smarty.hs
@@ -15,7 +15,7 @@
 
 parseSmarty :: MonadFail m => FilePath -> T.Text -> m Smarty
 parseSmarty fp t =
-    either fail mk $ parseOnly pRoot t
+    either (\err -> fail ("Failed to parse " <> fp <> ": " <> err)) mk $ parseOnly pRoot t
     where
       mk exprs =
           return $ Smarty fp exprs
diff --git a/src/Text/HSmarty/Render/Engine.hs b/src/Text/HSmarty/Render/Engine.hs
--- a/src/Text/HSmarty/Render/Engine.hs
+++ b/src/Text/HSmarty/Render/Engine.hs
@@ -1,48 +1,53 @@
+{-# LANGUAGE DoAndIfThenElse #-}
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE RankNTypes #-}
-{-# LANGUAGE DoAndIfThenElse #-}
+
 module Text.HSmarty.Render.Engine
-    ( ParamMap, mkParam
-    , SmartyCtx, SmartyError(..)
-    , prepareTemplate, prepareTemplates
-    , applyTemplate
-    , applyTemplateFromJson
-    )
+  ( ParamMap,
+    mkParam,
+    SmartyCtx,
+    SmartyError (..),
+    prepareTemplate,
+    prepareTemplates,
+    applyTemplate,
+    applyTemplateFromJson,
+  )
 where
 
 import Control.Monad
-import Text.HSmarty.Parser.Smarty
-import Text.HSmarty.Types
-import qualified Data.Text.Lazy as TL
-
 import Control.Monad.Except
 import Control.Monad.Identity
-import Data.Char (ord)
-import Data.Maybe
-import Data.Scientific
-import Data.Vector ((!?))
-import Network.HTTP.Base (urlEncode)
-import System.FilePath
-import System.FilePath.Glob
 import qualified Data.Aeson as A
+import qualified Data.Aeson.Key as AK
+import qualified Data.Aeson.KeyMap as KM
 import qualified Data.ByteString.Lazy as BSL
+import Data.Char (ord)
 import qualified Data.HashMap.Strict as HM
+import Data.Maybe
+import Data.Scientific
 import qualified Data.Text as T
 import qualified Data.Text.Encoding as T
 import qualified Data.Text.IO as T
+import qualified Data.Text.Lazy as TL
 import qualified Data.Text.Lazy.Builder as TLB
+import Data.Vector ((!?))
 import qualified Data.Vector as V
+import Network.HTTP.Base (urlEncode)
+import System.FilePath
+import System.FilePath.Glob
+import Text.HSmarty.Parser.Smarty
+import Text.HSmarty.Types
 
-data TemplateVar
-   = TemplateVar
-   { tv_value :: A.Value
-   , tv_props :: PropMap
-   }
-   deriving (Show, Eq)
+data TemplateVar = TemplateVar
+  { tv_value :: A.Value,
+    tv_props :: PropMap
+  }
+  deriving (Show, Eq)
 
 -- | Maps template variables to template params
 type ParamMap = HM.HashMap T.Text A.Value
+
 type PropMap = HM.HashMap T.Text A.Value
 
 mkParam :: A.ToJSON a => a -> A.Value
@@ -50,478 +55,556 @@
 
 type EvalM m a = ExceptT SmartyError m a
 
-newtype SmartyCtx
-    = SmartyCtx
-    { unSmartyCtx :: HM.HashMap FilePath Smarty
-    } deriving (Show, Eq)
+newtype SmartyCtx = SmartyCtx
+  { unSmartyCtx :: HM.HashMap FilePath Smarty
+  }
+  deriving (Show, Eq)
 
-data Env =
-    Env
-    { e_var :: HM.HashMap T.Text TemplateVar
-    , e_fun :: HM.HashMap T.Text ([SmartyStmt], [(T.Text, A.Value)])
-    , e_ctx :: SmartyCtx
-    } deriving (Show, Eq)
+data Env = Env
+  { e_var :: HM.HashMap T.Text TemplateVar,
+    e_fun :: HM.HashMap T.Text ([SmartyStmt], [(T.Text, A.Value)]),
+    e_ctx :: SmartyCtx
+  }
+  deriving (Show, Eq)
 
-newtype SmartyError
-    = SmartyError { unSmartyError :: T.Text }
-      deriving (Show, Eq)
+newtype SmartyError = SmartyError {unSmartyError :: T.Text}
+  deriving (Show, Eq)
 
 wrapSmartyCapture :: A.Value -> PropMap -> TemplateVar
-wrapSmartyCapture x = TemplateVar (A.Object $ HM.singleton "capture" x)
+wrapSmartyCapture x = TemplateVar (A.Object $ KM.singleton "capture" x)
 
 updateSmartyCapture :: T.Text -> A.Value -> HM.HashMap T.Text TemplateVar -> HM.HashMap T.Text TemplateVar
 updateSmartyCapture key value hm =
-    let updatedVal =
-            case HM.lookup "smarty" hm of
-                Nothing -> wrapSmartyCapture (A.Object $ HM.singleton key value) mempty
-                Just (TemplateVar existingCtx oldProps) -> 
-                    case existingCtx of
-                        A.Object oldContext -> 
-                            wrapSmartyCapture (A.Object $ HM.insert key value oldContext) oldProps
-                        _ -> error "Smarty capture context is always a map!"
-    in HM.insert "smarty" updatedVal hm
+  let updatedVal =
+        case HM.lookup "smarty" hm of
+          Nothing -> wrapSmartyCapture (A.Object $ KM.singleton (AK.fromText key) value) mempty
+          Just (TemplateVar existingCtx oldProps) ->
+            case existingCtx of
+              A.Object oldContext ->
+                wrapSmartyCapture (A.Object $ KM.insert (AK.fromText key) value oldContext) oldProps
+              _ -> error "Smarty capture context is always a map!"
+   in HM.insert "smarty" updatedVal hm
 
 rootMap :: HM.HashMap T.Text TemplateVar
 rootMap = HM.singleton "smarty" (wrapSmartyCapture (A.Object mempty) mempty)
 
 mkEnv :: ParamMap -> SmartyCtx -> Env
 mkEnv pm ctx =
-    Env
-    { e_var = rootMap <> HM.map (\init' -> TemplateVar init' HM.empty) pm
-    , e_fun = HM.empty
-    , e_ctx = ctx
+  Env
+    { e_var = rootMap <> HM.map (\init' -> TemplateVar init' HM.empty) pm,
+      e_fun = HM.empty,
+      e_ctx = ctx
     }
 
 -- | Parse and compile a template
 prepareTemplate :: FilePath -> IO SmartyCtx
 prepareTemplate fp =
-    do ct <- T.readFile fp
-       tpl <- HM.singleton fp <$> parseSmarty fp ct
-       pure $ SmartyCtx tpl
+  do
+    ct <- T.readFile fp
+    tpl <- HM.singleton fp <$> parseSmarty fp ct
+    pure $ SmartyCtx tpl
 
 -- | Parse and compiles templates matching a glob in a directiry
 prepareTemplates :: String -> FilePath -> IO SmartyCtx
 prepareTemplates pat dir =
-    do files <- globDir1 (compile pat) dir
-       let dirDropper =
-               makeRelative dir
-       ctx <-
-           foldM (\hm f ->
-                      do ct <- T.readFile f >>= parseSmarty f
-                         pure (HM.insert (dirDropper f) ct hm)
-                 ) mempty files
-       pure $ SmartyCtx ctx
+  do
+    files <- globDir1 (compile pat) dir
+    let dirDropper =
+          makeRelative dir
+    ctx <-
+      foldM
+        ( \hm f ->
+            do
+              ct <- T.readFile f >>= parseSmarty f
+              pure (HM.insert (dirDropper f) ct hm)
+        )
+        mempty
+        files
+    pure $ SmartyCtx ctx
 
 -- | Fill a template with values and print it as Text
 applyTemplateFromJson :: A.ToJSON a => FilePath -> SmartyCtx -> a -> Either SmartyError T.Text
 applyTemplateFromJson a b c =
   case A.toJSON c of
-    A.Object hm -> runIdentity $ runExceptT $ applyTemplate' a b hm
+    A.Object hm -> runIdentity $ runExceptT $ applyTemplate' a b $ HM.mapKeys AK.toText $ HM.fromList $ KM.toList hm
     x -> Left (SmartyError $ T.pack $ show x ++ " is not a json object, need an object at top level")
 
 -- | Fill a template with values and print it as Text
 applyTemplate :: FilePath -> SmartyCtx -> ParamMap -> Either SmartyError T.Text
 applyTemplate a b c =
-    runIdentity $ runExceptT $ applyTemplate' a b c
+  runIdentity $ runExceptT $ applyTemplate' a b c
 
 applyTemplate' :: Monad m => FilePath -> SmartyCtx -> ParamMap -> ExceptT SmartyError m T.Text
 applyTemplate' template ctx mp =
-    do getTpl <-
-           case HM.lookup template (unSmartyCtx ctx) of
-             Just ok -> pure ok
-             Nothing ->
-                 throwError (SmartyError $ T.pack $ "Template " ++ template ++ " not compiled")
-       evalTpl (mkEnv mp ctx) getTpl
+  do
+    getTpl <-
+      case HM.lookup template (unSmartyCtx ctx) of
+        Just ok -> pure ok
+        Nothing ->
+          throwError (SmartyError $ T.pack $ "Template " ++ template ++ " not compiled")
+    evalTpl (mkEnv mp ctx) getTpl
 
 txtPdHelper ::
-    Monad m => Env -> Expr -> (T.Text -> T.Text) -> ExceptT SmartyError m Expr
+  Monad m => Env -> Expr -> (T.Text -> T.Text) -> ExceptT SmartyError m Expr
 txtPdHelper env expr go =
-    do t <- exprToText env expr
-       return $ ExprLit $ A.String $ go t
+  do
+    t <- exprToText env expr
+    return $ ExprLit $ A.String $ go t
 
 applyPrintDirective :: Monad m => Env -> Expr -> PrintDirective -> EvalM m Expr
 applyPrintDirective env expr "json" =
-    do evaled <- evalExpr env expr
-       pure $ ExprLit $ A.String $ T.decodeUtf8 $ BSL.toStrict $ A.encode evaled
+  do
+    evaled <- evalExpr env expr
+    pure $ ExprLit $ A.String $ T.decodeUtf8 $ BSL.toStrict $ A.encode evaled
 applyPrintDirective env expr "urlencode" =
-    txtPdHelper env expr (T.pack . urlEncode . T.unpack)
+  txtPdHelper env expr (T.pack . urlEncode . T.unpack)
 applyPrintDirective env expr "nl2br" =
-    txtPdHelper env expr (T.replace "\n" "<br />")
+  txtPdHelper env expr (T.replace "\n" "<br />")
 applyPrintDirective env expr "escape" =
-    txtPdHelper env expr (T.pack . htmlEscape . T.unpack)
-    where
-      forbidden :: String
-      forbidden = "<&\">'/"
-      htmlEscape :: String -> String
-      htmlEscape [] = []
-      htmlEscape (x:xs) =
-          if x `elem` forbidden
-          then concat [ "&#" ++ show (ord x) ++ ";"
-                      , htmlEscape xs
-                      ]
-          else x : htmlEscape xs
+  txtPdHelper env expr (T.pack . htmlEscape . T.unpack)
+  where
+    forbidden :: String
+    forbidden = "<&\">'/"
+    htmlEscape :: String -> String
+    htmlEscape [] = []
+    htmlEscape (x : xs) =
+      if x `elem` forbidden
+        then
+          concat
+            [ "&#" ++ show (ord x) ++ ";",
+              htmlEscape xs
+            ]
+        else x : htmlEscape xs
 applyPrintDirective _ _ pd =
-    throwError $ SmartyError $
-    T.concat [ "Unknown print directive `"
-             , pd
-             , "`"
-             ]
+  throwError $
+    SmartyError $
+      T.concat
+        [ "Unknown print directive `",
+          pd,
+          "`"
+        ]
 
 evalTpl :: Monad m => Env -> Smarty -> EvalM m T.Text
 evalTpl env (Smarty _ tpl) =
-    snd <$> seqStmts env tpl
+  snd <$> seqStmts env tpl
 
 evalStmt :: Monad m => Env -> SmartyStmt -> EvalM m (Env, T.Text)
 evalStmt env (SmartyScope sc) =
-    do (_, body) <- seqStmts env (s_stmts sc)
-       pure (env, body)
+  do
+    (_, body) <- seqStmts env (s_stmts sc)
+    pure (env, body)
 evalStmt env (SmartyFun fd) =
-    do args <-
-           forM (fd_defArgs fd) $ \(n, expr) ->
-           do r <- evalExpr env expr
-              pure (n, r)
-       let fun =
-               HM.insert (fd_name fd) (fd_body fd, args) (e_fun env)
-       pure (env { e_fun = fun }, T.empty)
+  do
+    args <-
+      forM (fd_defArgs fd) $ \(n, expr) ->
+        do
+          r <- evalExpr env expr
+          pure (n, r)
+    let fun =
+          HM.insert (fd_name fd) (fd_body fd, args) (e_fun env)
+    pure (env {e_fun = fun}, T.empty)
 evalStmt env (SmartyCapture cap) =
-    do (_, body) <- seqStmts env (c_stmts cap)
-       let varBody = TemplateVar (A.String body) mempty
-           capture =
-               updateSmartyCapture (c_name cap) (A.String body)
-           assignment = 
-             case c_assign cap of
-                 Nothing -> mempty
-                 Just x -> HM.insert x varBody
-           eVars = capture . assignment $ e_var env
-       pure (env { e_var = eVars }, T.empty)
+  do
+    (_, body) <- seqStmts env (c_stmts cap)
+    let varBody = TemplateVar (A.String body) mempty
+        capture =
+          updateSmartyCapture (c_name cap) (A.String body)
+        assignment =
+          case c_assign cap of
+            Nothing -> mempty
+            Just x -> HM.insert x varBody
+        eVars = capture . assignment $ e_var env
+    pure (env {e_var = eVars}, T.empty)
 evalStmt env (SmartyLet l) =
-    do r <- evalExpr env (l_expr l)
-       let eVars =
-               HM.insert (l_name l) (TemplateVar r mempty) (e_var env)
-       pure (env { e_var = eVars }, T.empty)
+  do
+    r <- evalExpr env (l_expr l)
+    let eVars =
+          HM.insert (l_name l) (TemplateVar r mempty) (e_var env)
+    pure (env {e_var = eVars}, T.empty)
 evalStmt env (SmartyText t) = return (env, t)
 evalStmt env (SmartyComment _) = return (env, T.empty)
 evalStmt env (SmartyPrint expr directives) =
-    do e <- foldM (applyPrintDirective env) expr directives
-       (,) <$> pure env <*> exprToText env e
+  do
+    e <- foldM (applyPrintDirective env) expr directives
+    (,) <$> pure env <*> exprToText env e
 evalStmt env (SmartyIf (If cases elseBody)) =
-    do evaledCases <- mapM (\(cond, body) ->
-                                do r <- evalExpr env cond
-                                   (_, b) <- seqStmts env body
-                                   case r of
-                                     (A.Bool False) ->
-                                         return Nothing
-                                     A.Null -> return Nothing
-                                     (A.Array v) | null v -> return Nothing
-                                     _ ->
-                                         return $ Just b
-                           ) cases
-       case catMaybes evaledCases of
-         (x:_) ->
-             return (env, x)
-         _ ->
-             case elseBody of
-               Just elseB ->
-                   (,) <$> pure env <*> (snd <$> seqStmts env elseB)
-               Nothing ->
-                   return (env, T.empty)
+  do
+    evaledCases <-
+      mapM
+        ( \(cond, body) ->
+            do
+              r <- evalExpr env cond
+              (_, b) <- seqStmts env body
+              case r of
+                (A.Bool False) ->
+                  return Nothing
+                A.Null -> return Nothing
+                (A.Array v) | null v -> return Nothing
+                _ ->
+                  return $ Just b
+        )
+        cases
+    case catMaybes evaledCases of
+      (x : _) ->
+        return (env, x)
+      _ ->
+        case elseBody of
+          Just elseB ->
+            (,) <$> pure env <*> (snd <$> seqStmts env elseB)
+          Nothing ->
+            return (env, T.empty)
 evalStmt env (SmartyForeach (Foreach source mKey val body elseBody)) =
-    do evaledSource <- evalExpr env source
-       (preparedSource, size) <- mkForeachInput evaledSource
-       if size == 0
-       then case elseBody of
-              Just b -> (,) <$> pure env <*> (snd <$> seqStmts env b)
-              Nothing -> return (env, T.empty)
-       else do runs <- mapM (evalForeachBody env mKey val body) preparedSource
-               return (env, T.concat runs)
+  do
+    evaledSource <- evalExpr env source
+    (preparedSource, size) <- mkForeachInput evaledSource
+    if size == 0
+      then case elseBody of
+        Just b -> (,) <$> pure env <*> (snd <$> seqStmts env b)
+        Nothing -> return (env, T.empty)
+      else do
+        runs <- mapM (evalForeachBody env mKey val body) preparedSource
+        return (env, T.concat runs)
 
 seqStmts :: Monad m => Env -> [SmartyStmt] -> EvalM m (Env, T.Text)
 seqStmts env stmt =
-    do let go (ebase, tb) st =
-               do (e, t) <- evalStmt ebase st
-                  pure (e, tb <> TLB.fromText t)
-       (e', tb) <-
-           foldM go (env, mempty) stmt
-       return (e', TL.toStrict $ TLB.toLazyText tb)
+  do
+    let go (ebase, tb) st =
+          do
+            (e, t) <- evalStmt ebase st
+            pure (e, tb <> TLB.fromText t)
+    (e', tb) <-
+      foldM go (env, mempty) stmt
+    return (e', TL.toStrict $ TLB.toLazyText tb)
 
 exprToText :: Monad m => Env -> Expr -> EvalM m T.Text
 exprToText env expr =
-    do evaled <- evalExpr env expr
-       case evaled of
-         A.String t -> return t
-         A.Number n -> return $ T.pack $ show n
-         A.Null -> return "null"
-         A.Bool b ->
-             return (if b then "true" else "false")
-         A.Object o ->
-             return $ T.pack $ show o
-         A.Array a ->
-             return $ T.pack $ show a
+  do
+    evaled <- evalExpr env expr
+    case evaled of
+      A.String t -> return t
+      A.Number n -> return $ T.pack $ show n
+      A.Null -> return "null"
+      A.Bool b ->
+        return (if b then "true" else "false")
+      A.Object o ->
+        return $ T.pack $ show o
+      A.Array a ->
+        return $ T.pack $ show a
 
 evalForeachBody ::
-    Monad m
-    => Env -> Maybe T.Text -> T.Text -> [ SmartyStmt ]
-    -> ( A.Value, A.Value, PropMap )
-    -> EvalM m T.Text
+  Monad m =>
+  Env ->
+  Maybe T.Text ->
+  T.Text ->
+  [SmartyStmt] ->
+  (A.Value, A.Value, PropMap) ->
+  EvalM m T.Text
 evalForeachBody envFull mKey item body (keyVal, itemVal, props) =
-    let env = e_var envFull
-        env' = HM.insert item (TemplateVar itemVal props) env
-        env'' =
-            case mKey of
-              Just key -> HM.insert key (TemplateVar keyVal HM.empty) env'
-              Nothing -> env'
-    in snd <$> seqStmts (envFull { e_var = env'' }) body
-
+  let env = e_var envFull
+      env' = HM.insert item (TemplateVar itemVal props) env
+      env'' =
+        case mKey of
+          Just key -> HM.insert key (TemplateVar keyVal HM.empty) env'
+          Nothing -> env'
+   in snd <$> seqStmts (envFull {e_var = env''}) body
 
-mkForeachInput :: Monad m => A.Value -> EvalM m ( [ ( A.Value, A.Value, PropMap ) ], Int)
+mkForeachInput :: Monad m => A.Value -> EvalM m ([(A.Value, A.Value, PropMap)], Int)
 mkForeachInput (A.Array vec) =
-    return $ ( V.toList $ V.imap (\idx el ->
-                                      ( A.Number (fromIntegral idx)
-                                      , el
-                                      , mkForeachMap idx fSize
-                                      )
-                                 ) vec
-             , fSize
-             )
-    where
-      fSize = V.length vec
+  return $
+    ( V.toList $
+        V.imap
+          ( \idx el ->
+              ( A.Number (fromIntegral idx),
+                el,
+                mkForeachMap idx fSize
+              )
+          )
+          vec,
+      fSize
+    )
+  where
+    fSize = V.length vec
 mkForeachInput (A.Object hm) =
-    let (_, input) =
-            HM.foldlWithKey' (\(idx, out) key el ->
-                                  let newElem = ( A.String key
-                                                , el
-                                                , mkForeachMap idx hSize
-                                                )
-                                  in (idx+1, newElem : out)
-                             ) (0, []) hm
-    in return $ (reverse input, hSize)
-    where
-      hSize = HM.size hm
+  let (_, input) =
+        HM.foldlWithKey'
+          ( \(idx, out) key el ->
+              let newElem =
+                    ( A.String $ AK.toText key,
+                      el,
+                      mkForeachMap idx hSize
+                    )
+               in (idx + 1, newElem : out)
+          )
+          (0, [])
+          (HM.fromList $ KM.toList hm)
+   in return (reverse input, hSize)
+  where
+    hSize = KM.size hm
 mkForeachInput _ =
-    throwError $ SmartyError "Tried to iterate over non traversable type."
+  throwError $ SmartyError "Tried to iterate over non traversable type."
 
 mkForeachMap :: Int -> Int -> PropMap
 mkForeachMap idx' size' =
-    HM.fromList [ ("index", A.Number idx)
-                , ("iteration", A.Number $ 1 + idx)
-                , ("first", A.Bool $ idx == 0)
-                , ("last", A.Bool $ (idx+1) == size)
-                , ("total", A.Number size)
-                ]
-    where
-      size = fromIntegral size'
-      idx = fromIntegral idx'
+  HM.fromList
+    [ ("index", A.Number idx),
+      ("iteration", A.Number $ 1 + idx),
+      ("first", A.Bool $ idx == 0),
+      ("last", A.Bool $ (idx + 1) == size),
+      ("total", A.Number size)
+    ]
+  where
+    size = fromIntegral size'
+    idx = fromIntegral idx'
 
 str :: Monad m => T.Text -> A.Value -> EvalM m T.Text
 str _ (A.String x) = return x
-str desc _ = throwError $ SmartyError $ T.concat [ "`", desc, "` is not a string!" ]
+str desc _ = throwError $ SmartyError $ T.concat ["`", desc, "` is not a string!"]
 
 ifExists :: (Eq a, Show a, Monad m) => T.Text -> a -> [(a, A.Value)] -> (A.Value -> EvalM m b) -> EvalM m b
 ifExists msg key env fun =
-    case lookup key env of
-      Just x -> fun x
-      Nothing ->
-          throwError $ SmartyError $ T.concat [ "`", T.pack $ show key, "` is not given. ", msg]
+  case lookup key env of
+    Just x -> fun x
+    Nothing ->
+      throwError $ SmartyError $ T.concat ["`", T.pack $ show key, "` is not given. ", msg]
 
 lookupStr :: Monad m => T.Text -> T.Text -> [(T.Text, A.Value)] -> EvalM m T.Text
 lookupStr funName key env =
-    ifExists (T.concat ["Param for `", funName, "`"]) key env (str key)
+  ifExists (T.concat ["Param for `", funName, "`"]) key env (str key)
 
-evalFunCall :: Monad m => Env -> T.Text -> [ (T.Text, Expr) ] -> EvalM m A.Value
+evalFunCall :: Monad m => Env -> T.Text -> [(T.Text, Expr)] -> EvalM m A.Value
 evalFunCall env "include" args =
-    do evaledArgs <- mapM (\(k, expr) ->
-                               do val <- evalExpr env expr
-                                  return (k, val)
-                          ) args
-       filename <- lookupStr "include" "file" evaledArgs
-       let otherArgs = filter (\(k, _) ->
-                                   not $ k `elem` [ "include" ]
-                              ) evaledArgs
-           asTplParams = HM.fromList $ map (\(k, v) -> (k, v)) otherArgs
-       A.String <$> applyTemplate' (T.unpack filename) (e_ctx env) asTplParams
+  do
+    evaledArgs <-
+      mapM
+        ( \(k, expr) ->
+            do
+              val <- evalExpr env expr
+              return (k, val)
+        )
+        args
+    filename <- lookupStr "include" "file" evaledArgs
+    let otherArgs =
+          filter
+            ( \(k, _) ->
+                not $ k `elem` ["include"]
+            )
+            evaledArgs
+        asTplParams = HM.fromList $ map (\(k, v) -> (k, v)) otherArgs
+    A.String <$> applyTemplate' (T.unpack filename) (e_ctx env) asTplParams
 evalFunCall env fname args =
-    case HM.lookup fname (e_fun env) of
-      Just (fBody, fDefArgs) ->
-          do localArgs <-
-                 forM args $ \(n, expr) ->
-                 do r <- evalExpr env expr
-                    pure (n, r)
-             let myArgs =
-                     fmap (\v -> TemplateVar v mempty) $
-                     HM.fromList localArgs `HM.union` HM.fromList fDefArgs
-                 callEnv =
-                     myArgs `HM.union` e_var env
-             (_, res) <- seqStmts (env { e_var = callEnv }) fBody
-             return (A.String res)
-      Nothing ->
-          throwError $ SmartyError $
-          T.concat [ "Call to undefined function "
-                   , fname
-                   ]
-
+  case HM.lookup fname (e_fun env) of
+    Just (fBody, fDefArgs) ->
+      do
+        localArgs <-
+          forM args $ \(n, expr) ->
+            do
+              r <- evalExpr env expr
+              pure (n, r)
+        let myArgs =
+              fmap (\v -> TemplateVar v mempty) $
+                HM.fromList localArgs `HM.union` HM.fromList fDefArgs
+            callEnv =
+              myArgs `HM.union` e_var env
+        (_, res) <- seqStmts (env {e_var = callEnv}) fBody
+        return (A.String res)
+    Nothing ->
+      throwError $
+        SmartyError $
+          T.concat
+            [ "Call to undefined function ",
+              fname
+            ]
 
 evalExpr :: Monad m => Env -> Expr -> EvalM m A.Value
 evalExpr _ (ExprLit v) = return v
 evalExpr env (ExprBin op) =
-    evalBinOp env op
+  evalBinOp env op
 evalExpr env (ExprFun funCall) =
-    evalFunCall env (f_name funCall) (f_args funCall)
+  evalFunCall env (f_name funCall) (f_args funCall)
 evalExpr env (ExprVar v) =
-    case HM.lookup varName (e_var env) of
-      Just tplVar ->
-          case v of
-            (Variable { v_prop = Just propReq }) ->
-                case HM.lookup propReq (tv_props tplVar) of
-                  Just val -> return val
+  case HM.lookup varName (e_var env) of
+    Just tplVar ->
+      case v of
+        (Variable {v_prop = Just propReq}) ->
+          case HM.lookup propReq (tv_props tplVar) of
+            Just val -> return val
+            Nothing ->
+              throwError $
+                SmartyError $
+                  T.concat
+                    [ "Property `",
+                      propReq,
+                      "` is not defined for variable `",
+                      varName,
+                      "`"
+                    ]
+        (Variable {v_path = path, v_index = mIdx}) ->
+          let pathName =
+                T.concat
+                  [ varName,
+                    if (length path > 0) then "." else T.empty,
+                    T.intercalate "." path
+                  ]
+              idxWalk val =
+                case mIdx of
+                  Just eIdx ->
+                    do
+                      res <- evalExpr env eIdx
+                      walkIndex pathName res val
                   Nothing ->
-                      throwError $
-                      SmartyError $
-                      T.concat [ "Property `"
-                               , propReq
-                               , "` is not defined for variable `"
-                               , varName
-                               , "`"
-                               ]
-            (Variable { v_path = path, v_index = mIdx }) ->
-                let pathName = T.concat [ varName
-                                        , if (length path > 0) then "." else T.empty
-                                        , T.intercalate "." path
-                                        ]
-                    idxWalk val =
-                        case mIdx of
-                          Just eIdx ->
-                              do res <- evalExpr env eIdx
-                                 walkIndex pathName res val
-                          Nothing ->
-                              return val
-
-                in do pathRes <- walkPath varName path $ tv_value tplVar
-                      idxWalk pathRes
-
-
-      Nothing ->
-          throwError $
-          SmartyError $
-          T.concat [ "Variable `"
-                   , varName
-                   , "` is not defined"
-                   ]
-    where
-      varName = v_name v
+                    return val
+           in do
+                pathRes <- walkPath varName path $ tv_value tplVar
+                idxWalk pathRes
+    Nothing ->
+      throwError $
+        SmartyError $
+          T.concat
+            [ "Variable `",
+              varName,
+              "` is not defined"
+            ]
+  where
+    varName = v_name v
 
 walkIndex :: Monad m => T.Text -> A.Value -> A.Value -> EvalM m A.Value
 walkIndex vname (A.Number idx) (A.Array arr) =
-    case arr !? (fromJust $ toBoundedInteger idx) of
-      Just val -> return val
-      Nothing ->
-          throwError $
-          SmartyError $
-          T.concat [ "Out of bounds. `"
-                   , vname
-                   , "["
-                   , T.pack $ show idx
-                   , "]` not defined."
-                   ]
+  case arr !? (fromJust $ toBoundedInteger idx) of
+    Just val -> return val
+    Nothing ->
+      throwError $
+        SmartyError $
+          T.concat
+            [ "Out of bounds. `",
+              vname,
+              "[",
+              T.pack $ show idx,
+              "]` not defined."
+            ]
 walkIndex vname idx _ =
-    throwError $
+  throwError $
     SmartyError $
-    T.concat [ "Can't access `"
-             , T.pack $ show idx
-             , "` in `"
-             , vname
-             , "`. Index is not an integer or value not an array!"
-             ]
+      T.concat
+        [ "Can't access `",
+          T.pack $ show idx,
+          "` in `",
+          vname,
+          "`. Index is not an integer or value not an array!"
+        ]
 
 walkPath :: Monad m => T.Text -> [T.Text] -> A.Value -> EvalM m A.Value
 walkPath _ [] val = return val
-walkPath vname (path:xs) (A.Object obj) =
-    case HM.lookup path obj of
-      Just val -> walkPath (T.concat [vname, ".", path]) xs val
-      Nothing ->
-          throwError $ SmartyError $
-          T.concat [ "Variable `"
-                   , vname
-                   , "` doesn't have the key `"
-                   , path
-                   , "`"
-                   ]
-walkPath vname (path:_) _ =
-    throwError $ SmartyError $
-    T.concat [ "Variable `"
-             , vname
-             , "` is not a map! Can't lookup `"
-             , path
-             , "`"
-             ]
+walkPath vname (path : xs) (A.Object obj) =
+  case KM.lookup (AK.fromText path) obj of
+    Just val -> walkPath (T.concat [vname, ".", path]) xs val
+    Nothing ->
+      throwError $
+        SmartyError $
+          T.concat
+            [ "Variable `",
+              vname,
+              "` doesn't have the key `",
+              path,
+              "`"
+            ]
+walkPath vname (path : _) _ =
+  throwError $
+    SmartyError $
+      T.concat
+        [ "Variable `",
+          vname,
+          "` is not a map! Can't lookup `",
+          path,
+          "`"
+        ]
 
 evalBinOp :: Monad m => Env -> BinOp -> EvalM m A.Value
 evalBinOp env (BinEq a b) =
-    boolResOp (\x y -> return $ x == y) (a, b) env
+  boolResOp (\x y -> return $ x == y) (a, b) env
 evalBinOp env (BinNot e) =
-    do e' <- evalExpr env e
-       case e' of
-         A.Bool a ->
-             return (A.Bool $ not a)
-         _ ->
-             throwError $ SmartyError "Tried to evaluate a NOT on a non boolean value"
+  do
+    e' <- evalExpr env e
+    case e' of
+      A.Bool a ->
+        return (A.Bool $ not a)
+      _ ->
+        throwError $ SmartyError "Tried to evaluate a NOT on a non boolean value"
 evalBinOp env (BinOr x y) =
-    boolOp "Or" (||) (x, y) env
+  boolOp "Or" (||) (x, y) env
 evalBinOp env (BinAnd x y) =
-    boolOp "And" (&&) (x, y) env
+  boolOp "And" (&&) (x, y) env
 evalBinOp env (BinLarger x y) =
-    numOp "Larger" (>) (x, y) env
+  numOp "Larger" (>) (x, y) env
 evalBinOp env (BinLargerEq x y) =
-    numOp "LargerEq" (>=) (x, y) env
+  numOp "LargerEq" (>=) (x, y) env
 evalBinOp env (BinSmaller x y) =
-    numOp "Smaller" (<) (x, y) env
+  numOp "Smaller" (<) (x, y) env
 evalBinOp env (BinSmallerEq x y) =
-    numOp "SmallerEq" (<=) (x, y) env
+  numOp "SmallerEq" (<=) (x, y) env
 evalBinOp env (BinPlus x y) =
-    calcOp "Plus" (+) (x, y) env
+  calcOp "Plus" (+) (x, y) env
 evalBinOp env (BinMinus x y) =
-    calcOp "Minus" (-) (x, y) env
+  calcOp "Minus" (-) (x, y) env
 evalBinOp env (BinMul x y) =
-    calcOp "Mul" (*) (x, y) env
+  calcOp "Mul" (*) (x, y) env
 evalBinOp env (BinDiv x y) =
-    calcOp "Div" (/) (x, y) env
-
+  calcOp "Div" (/) (x, y) env
 
 boolOp :: Monad m => T.Text -> (Bool -> Bool -> Bool) -> (Expr, Expr) -> Env -> EvalM m A.Value
 boolOp d op exprs env =
-    boolResOp bOp exprs env
-    where
-      bOp (A.Bool a) (A.Bool b) =
-          return $ a `op` b
-      bOp _ _ = throwError $ SmartyError $ T.concat [ "Tried ", d, "Op and on two non boolean values" ]
+  boolResOp bOp exprs env
+  where
+    bOp (A.Bool a) (A.Bool b) =
+      return $ a `op` b
+    bOp _ _ = throwError $ SmartyError $ T.concat ["Tried ", d, "Op and on two non boolean values"]
 
 numOp :: Monad m => T.Text -> (Scientific -> Scientific -> Bool) -> (Expr, Expr) -> Env -> EvalM m A.Value
 numOp =
-    numGenOp boolResOp
+  numGenOp boolResOp
 
 calcOp :: Monad m => T.Text -> (Scientific -> Scientific -> Scientific) -> (Expr, Expr) -> Env -> EvalM m A.Value
 calcOp =
-    numGenOp numResOp
+  numGenOp numResOp
 
-numGenOp :: Monad m => ((A.Value -> A.Value -> EvalM m a)
-                 -> (Expr, Expr) -> Env -> EvalM m A.Value)
-         -> T.Text -> (Scientific -> Scientific -> a) -> (Expr, Expr) -> Env -> EvalM m A.Value
+numGenOp ::
+  Monad m =>
+  ( (A.Value -> A.Value -> EvalM m a) ->
+    (Expr, Expr) ->
+    Env ->
+    EvalM m A.Value
+  ) ->
+  T.Text ->
+  (Scientific -> Scientific -> a) ->
+  (Expr, Expr) ->
+  Env ->
+  EvalM m A.Value
 numGenOp fun d op exprs env =
-    fun nOp exprs env
-    where
-      nOp (A.Number a) (A.Number b) =
-          return $ a `op` b
-      nOp _ _ = throwError $ SmartyError $ T.concat [ "Tried ", d, "Op and on two non numeric values" ]
+  fun nOp exprs env
+  where
+    nOp (A.Number a) (A.Number b) =
+      return $ a `op` b
+    nOp _ _ = throwError $ SmartyError $ T.concat ["Tried ", d, "Op and on two non numeric values"]
 
-numResOp :: Monad m => (A.Value -> A.Value -> EvalM m Scientific)
-       -> (Expr, Expr) -> Env -> EvalM m A.Value
+numResOp ::
+  Monad m =>
+  (A.Value -> A.Value -> EvalM m Scientific) ->
+  (Expr, Expr) ->
+  Env ->
+  EvalM m A.Value
 numResOp fun (a, b) env =
-    do a' <- evalExpr env a
-       b' <- evalExpr env b
-       A.Number <$> fun a' b'
+  do
+    a' <- evalExpr env a
+    b' <- evalExpr env b
+    A.Number <$> fun a' b'
 
-boolResOp :: Monad m => (A.Value -> A.Value -> EvalM m Bool)
-       -> (Expr, Expr) -> Env -> EvalM m A.Value
+boolResOp ::
+  Monad m =>
+  (A.Value -> A.Value -> EvalM m Bool) ->
+  (Expr, Expr) ->
+  Env ->
+  EvalM m A.Value
 boolResOp fun (a, b) env =
-    do a' <- evalExpr env a
-       b' <- evalExpr env b
-       A.Bool <$> fun a' b'
+  do
+    a' <- evalExpr env a
+    b' <- evalExpr env b
+    A.Bool <$> fun a' b'
