diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,10 @@
 
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 
+## 0.5.1.0 - 2026-01-06
+
+- Nix support up to 2.33
+
 ## 0.5.0.0 - 2025-07-18
 
 - `init` now installs synchronous signal handlers for you. You may remove your call to `installDefaultSigINTHandler`.
diff --git a/hercules-ci-cnix-expr.cabal b/hercules-ci-cnix-expr.cabal
--- a/hercules-ci-cnix-expr.cabal
+++ b/hercules-ci-cnix-expr.cabal
@@ -1,7 +1,7 @@
 cabal-version: 2.4
 
 name:           hercules-ci-cnix-expr
-version:        0.5.0.0
+version:        0.5.1.0
 synopsis:       Bindings for the Nix evaluator
 category:       Nix, CI, Testing, DevOps
 homepage:       https://docs.hercules-ci.com
@@ -25,10 +25,19 @@
   description: Whether to enable IDE workarounds. You shouldn't need this.
   default: False
 
+flag nix-2_31
+  description: nix-store >= 2.31
+  default: False
+  manual: False
+
 -- match the C++ language standard Nix is using
 common cxx-opts
-  cxx-options:
-    -std=c++2a
+  if flag(nix-2_31)
+    cxx-options:
+      -std=c++23
+  else
+    cxx-options:
+      -std=c++2a
 
   cxx-options:
     -Wall
@@ -41,12 +50,22 @@
   if impl(ghc >= 8.10)
     ghc-options:
       -optcxx-Wall
-      -optcxx-std=c++2a
+    if flag(nix-2_31)
+      ghc-options:
+        -optcxx-std=c++23
+    else
+      ghc-options:
+        -optcxx-std=c++2a
   else
     -- Remove soon
     ghc-options:
-      -optc-std=c++2a
       -optc-Wall
+    if flag(nix-2_31)
+      ghc-options:
+        -optc-std=c++23
+    else
+      ghc-options:
+        -optc-std=c++2a
     if os(darwin)
       ghc-options: -pgmc=clang++
 
@@ -97,11 +116,11 @@
     extra-libraries:
         boost_context
   pkgconfig-depends:
-      nix-store >= 2.28
-    , nix-expr >= 2.28
+      nix-store >= 2.28 && < 2.34
+    , nix-expr >= 2.28 && < 2.34
     -- TODO: replace nix-main stack overflow detection?
-    , nix-main >= 2.28
-    , nix-flake >= 2.28
+    , nix-main >= 2.28 && < 2.34
+    , nix-flake >= 2.28 && < 2.34
 
 
 test-suite hercules-ci-cnix-expr-unit-tests
diff --git a/include/hercules-ci-cnix/expr.hxx b/include/hercules-ci-cnix/expr.hxx
--- a/include/hercules-ci-cnix/expr.hxx
+++ b/include/hercules-ci-cnix/expr.hxx
@@ -1,9 +1,13 @@
 #pragma once
 
 #include <nix/store/derivations.hh>
+#include <nix/expr/value.hh>
 
 #include <hercules-ci-cnix/store.hxx>
 
 typedef nix::Strings::iterator StringsIterator;
 typedef nix::DerivationOutputs::iterator DerivationOutputsIterator;
 typedef nix::StringPairs::iterator StringPairsIterator;
+#if NIX_IS_AT_LEAST(2, 30, 0)
+typedef nix::ListView ListViewType;
+#endif
diff --git a/src/Hercules/CNix/Expr.hs b/src/Hercules/CNix/Expr.hs
--- a/src/Hercules/CNix/Expr.hs
+++ b/src/Hercules/CNix/Expr.hs
@@ -8,7 +8,6 @@
 {-# LANGUAGE QuasiQuotes #-}
 {-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE TypeFamilies #-}
-
 -- redundant-constraints: False positive in default signature for `toRawValue`
 {-# OPTIONS_GHC -Wno-redundant-constraints #-}
 
@@ -53,10 +52,10 @@
     getLocalFlake,
     getFlakeFromGit,
     getFlakeFromArchiveUrl,
-    ToRawValue(..),
-    ToValue(..),
-    FromValue(..),
-    ViaJSON(..),
+    ToRawValue (..),
+    ToValue (..),
+    FromValue (..),
+    ViaJSON (..),
 
     -- * Re-exports
     RawValue,
@@ -73,6 +72,8 @@
 
 import Conduit
 import qualified Data.Aeson as A
+import Data.Aeson.KeyMap (toMapText)
+import qualified Data.ByteString.Unsafe as BS
 import Data.Coerce (coerce)
 import qualified Data.HashMap.Lazy as H
 import qualified Data.Map as M
@@ -81,19 +82,17 @@
 import qualified Data.Vector as V
 import Foreign (nullPtr)
 import qualified Foreign.C.String
+import qualified Hercules.CNix as CNix
 import Hercules.CNix.Encapsulation (moveToForeignPtrWrapper)
 import Hercules.CNix.Expr.Context
 import Hercules.CNix.Expr.Raw
 import Hercules.CNix.Expr.Typed
 import Hercules.CNix.Store
 import Hercules.CNix.Store.Context
-import qualified Hercules.CNix as CNix
 import qualified Language.C.Inline.Cpp as C
 import qualified Language.C.Inline.Cpp.Exception as C
 import Protolude hiding (evalState)
 import System.Directory (makeAbsolute)
-import Data.Aeson.KeyMap (toMapText)
-import qualified Data.ByteString.Unsafe as BS
 
 C.context (Hercules.CNix.Store.Context.context <> Hercules.CNix.Expr.Context.evalContext)
 
@@ -200,12 +199,10 @@
         }|]
         pass
 
-
-{- | Configure the stack overflow handler to sleep before returning, allowing
-  other threads to continue for a bit.
-
-  No-op before Nix 2.12
--}
+-- | Configure the stack overflow handler to sleep before returning, allowing
+--  other threads to continue for a bit.
+--
+--  No-op before Nix 2.12
 setExtraStackOverflowHandlerToSleep :: IO ()
 setExtraStackOverflowHandlerToSleep =
   void
@@ -259,15 +256,16 @@
   }|]
 
 -- | (private) Make an EvalState and leak it.
-newEvalState :: MonadIO m => Store -> m (Ptr EvalState)
-newEvalState (Store store) = liftIO
-  [C.throwBlock| EvalState* {
-    nix::LookupPath emptyLookupPath;
-    return new EvalState(emptyLookupPath, *$(refStore* store), fetchSettings, evalSettings);
+newEvalState :: (MonadIO m) => Store -> m (Ptr EvalState)
+newEvalState (Store store) =
+  liftIO
+    [C.throwBlock| EvalState* {
+      nix::LookupPath emptyLookupPath;
+      return new EvalState(emptyLookupPath, *$(refStore* store), fetchSettings, evalSettings);
   } |]
 
 -- | (private) Don't leak it.
-deleteEvalState :: MonadIO m => (Ptr EvalState) -> m ()
+deleteEvalState :: (MonadIO m) => (Ptr EvalState) -> m ()
 deleteEvalState st = liftIO [C.throwBlock| void { delete $(EvalState* st); } |]
 
 withEvalState ::
@@ -277,20 +275,26 @@
 withEvalState store = bracket (newEvalState store) deleteEvalState
 
 withEvalStateConduit ::
-  MonadResource m =>
+  (MonadResource m) =>
   Store ->
   (Ptr EvalState -> ConduitT i o m r) ->
   ConduitT i o m r
 withEvalStateConduit store = bracketP (newEvalState store) deleteEvalState
 
 -- | Insert an allowed path. Only has an effect when in restricted or pure mode.
+{- ORMOLU_DISABLE -}
 addAllowedPath :: Ptr EvalState -> ByteString -> IO ()
 addAllowedPath evalState path =
   [C.throwBlock| void {
     std::string path = std::string($bs-ptr:path, $bs-len:path);
     EvalState &evalState = *$(EvalState *evalState);
+#if NIX_IS_AT_LEAST(2, 32, 0)
+    evalState.allowPathLegacy(path);
+#else
     evalState.allowPath(path);
+#endif
   }|]
+{- ORMOLU_ENABLE -}
 
 addInternalAllowedPaths :: Ptr EvalState -> IO ()
 addInternalAllowedPaths _evalState = do
@@ -348,6 +352,7 @@
           return $(EvalState *evalState)->isFunctor(*$(Value *v));
         }|]
 
+{- ORMOLU_DISABLE -}
 getRecurseForDerivations :: Ptr EvalState -> Value NixAttrs -> IO Bool
 getRecurseForDerivations evalState (Value (RawValue v)) =
   (0 /=)
@@ -355,6 +360,14 @@
           Value *v = $(Value *v);
           EvalState &evalState = *$(EvalState *evalState);
           auto attrs = v->attrs();
+#if NIX_IS_AT_LEAST(2, 32, 0)
+          auto iter = attrs->get(evalState.s.recurseForDerivations);
+          if (!iter) {
+            return 0;
+          } else {
+            return evalState.forceBool(*iter->value, iter->pos, "while evaluating whether to traverse into an attribute set to find more derivations");
+          }
+#else
           // Bindings::const_iterator iter; // 2.24
           auto iter = attrs->find(evalState.sRecurseForDerivations);
           if (iter == attrs->end()) {
@@ -366,8 +379,11 @@
             // nixpkgs master 67e2de195a4aa0a50ffb1e1ba0b4fb531dca67dc
             return evalState.forceBool(*iter->value, iter->pos, "while evaluating whether to traverse into an attribute set to find more derivations");
           }
+#endif
         } |]
+{- ORMOLU_ENABLE -}
 
+{- ORMOLU_DISABLE -}
 getAttr :: Ptr EvalState -> Value NixAttrs -> ByteString -> IO (Maybe RawValue)
 getAttr evalState (Value (RawValue v)) k =
   mkNullableRawValue
@@ -376,6 +392,14 @@
       EvalState &evalState = *$(EvalState *evalState);
       Symbol k = evalState.symbols.create($bs-cstr:k);
       auto attrs = v.attrs();
+#if NIX_IS_AT_LEAST(2, 32, 0)
+      auto iter = attrs->get(k);
+      if (!iter) {
+        return nullptr;
+      } else {
+        return iter->value;
+      }
+#else
       // Bindings::const_iterator iter; // 2.24
       auto iter = attrs->find(k);
       if (iter == attrs->end()) {
@@ -383,32 +407,64 @@
       } else {
         return iter->value;
       }
+#endif
     }|]
+{- ORMOLU_ENABLE -}
 
 -- | Converts 'nullPtr' to 'Nothing'; actual values to @Just (a :: 'RawValue')@
 mkNullableRawValue :: Ptr Value' -> IO (Maybe RawValue)
 mkNullableRawValue p | p == nullPtr = pure Nothing
 mkNullableRawValue p = Just <$> mkRawValue p
 
+{- ORMOLU_DISABLE -}
 getAttrs :: Ptr EvalState -> Value NixAttrs -> IO (Map ByteString RawValue)
 getAttrs evalState (Value (RawValue v)) = do
+#if NIX_IS_AT_LEAST(2, 32, 0)
+  -- In Nix 2.32+, we need to collect all attrs in C++ since iteration changed
+  size <- [C.exp| size_t { $(Value *v)->attrs()->size() }|]
+  let collect :: C.CSize -> Map ByteString RawValue -> IO (Map ByteString RawValue)
+      collect i acc | i >= size = pure acc
+      collect i acc = do
+        name <-
+          BS.unsafePackMallocCString
+            =<< [C.block| const char *{
+              EvalState &evalState = *$(EvalState *evalState);
+              auto iter = $(Value *v)->attrs()->begin();
+              for (size_t j = 0; j < $(size_t i); j++) ++iter;
+              SymbolStr str = evalState.symbols[iter->name];
+              return stringdup(static_cast<std::string>(str));
+            }|]
+        value <- mkRawValue =<< [C.block| Value *{
+              auto iter = $(Value *v)->attrs()->begin();
+              for (size_t j = 0; j < $(size_t i); j++) ++iter;
+              return new (NoGC) Value(*iter->value);
+            }|]
+        let acc' = M.insert name value acc
+        seq acc' pass
+        collect (i + 1) acc'
+  collect 0 mempty
+#else
   begin <- [C.exp| const Attr *{ $(Value *v)->attrs()->begin() }|]
   end <- [C.exp| const Attr *{ $(Value *v)->attrs()->end() }|]
   let gather :: Map ByteString RawValue -> Ptr Attr' -> IO (Map ByteString RawValue)
       gather acc i | i == end = pure acc
       gather acc i = do
-        name <- BS.unsafePackMallocCString =<< [C.block| const char *{
-          EvalState &evalState = *$(EvalState *evalState);
-          SymbolStr str = evalState.symbols[$(Attr *i)->name];
-          return stringdup(static_cast<std::string>(str));
-        }|]
+        name <-
+          BS.unsafePackMallocCString
+            =<< [C.block| const char *{
+              EvalState &evalState = *$(EvalState *evalState);
+              SymbolStr str = evalState.symbols[$(Attr *i)->name];
+              return stringdup(static_cast<std::string>(str));
+            }|]
         value <- mkRawValue =<< [C.exp| Value *{ new (NoGC) Value(*$(Attr *i)->value) } |]
         let acc' = M.insert name value acc
         seq acc' pass
         gather acc' =<< [C.exp| Attr *{ &$(Attr *i)[1] }|]
   gather mempty begin
+#endif
+{- ORMOLU_ENABLE -}
 
-getDrvFile :: MonadIO m => Ptr EvalState -> RawValue -> m StorePath
+getDrvFile :: (MonadIO m) => Ptr EvalState -> RawValue -> m StorePath
 getDrvFile evalState (RawValue v) = liftIO do
   moveToForeignPtrWrapper
     =<< [C.throwBlock| nix::StorePath *{
@@ -438,12 +494,26 @@
       Right _ -> do
         pure $ Right Nothing
 
+#if NIX_IS_AT_LEAST(2, 30, 0)
 getList :: Value NixList -> IO [RawValue]
 getList (Value (RawValue nixList)) = do
   len <- [C.exp| int { $(Value *nixList)->listSize() }|]
+  bracket
+    [C.exp| ListViewType * { new ListViewType($(Value *nixList)->listView()) }|]
+    (\listView -> [C.exp| void { delete $(ListViewType *listView) }|])
+    (\listView -> do
+      let getElem i = mkRawValue =<< [C.exp| Value * { (*$(ListViewType *listView))[$(int i)] }|]
+      for [0 .. (len - 1)] \i -> do
+        getElem i
+    )
+#else
+getList :: Value NixList -> IO [RawValue]
+getList (Value (RawValue nixList)) = do
+  len <- [C.exp| int { $(Value *nixList)->listSize() }|]
   let getElem i = mkRawValue =<< [C.exp| Value * { $(Value *nixList)->listElems()[$(int i)] }|]
   for [0 .. (len - 1)] \i -> do
     getElem i
+#endif
 
 getAttrList :: Ptr EvalState -> Value NixAttrs -> ByteString -> IO (Either SomeException (Maybe [RawValue]))
 getAttrList evalState attrset attrName = do
@@ -498,18 +568,24 @@
       return r;
     }|]
 
+{- ORMOLU_DISABLE -}
 mkPath :: Ptr EvalState -> ByteString -> IO (Value NixPath)
 mkPath evalState path =
   Value
     <$> ( mkRawValue
             =<< [C.throwBlock| Value *{
-      Value *r = new (NoGC) Value();
-      std::string s($bs-ptr:path, $bs-len:path);
-      EvalState & state = *$(EvalState *evalState);
-      r->mkPath(state.rootPath(CanonPath(s)));
-      return r;
-  }|]
+              Value *r = new (NoGC) Value();
+              std::string s($bs-ptr:path, $bs-len:path);
+              EvalState & state = *$(EvalState *evalState);
+#if NIX_IS_AT_LEAST(2, 33, 0)
+              r->mkPath(state.rootPath(CanonPath(s)), state.mem);
+#else
+              r->mkPath(state.rootPath(CanonPath(s)));
+#endif
+              return r;
+          }|]
         )
+{- ORMOLU_ENABLE -}
 
 getFlakeFromFlakeRef :: Ptr EvalState -> ByteString -> IO RawValue
 getFlakeFromFlakeRef evalState flakeRef = do
@@ -542,35 +618,34 @@
   absPath <- encodeUtf8 . toS <$> makeAbsolute (toS path)
   mkRawValue
     =<< [C.throwBlock| Value *{
-    EvalState &evalState = *$(EvalState *evalState);
-    Value *r = new (NoGC) Value();
-    std::string path($bs-ptr:absPath, $bs-len:absPath);
-    auto flakeRef = nix::parseFlakeRef(
-      fetchSettings,
-      path,
-      {},
-      true);
-    nix::flake::callFlake(evalState,
-      nix::flake::lockFlake(
-        flakeSettings,
-        evalState,
-        flakeRef,
-        nix::flake::LockFlags {
-          .updateLockFile = false,
-          .useRegistries = false,
-          .allowUnlocked = false,
-        }),
-      *r);
-    return r;
-  }|]
+      EvalState &evalState = *$(EvalState *evalState);
+      Value *r = new (NoGC) Value();
+      std::string path($bs-ptr:absPath, $bs-len:absPath);
+      auto flakeRef = nix::parseFlakeRef(
+        fetchSettings,
+        path,
+        {},
+        true);
+      nix::flake::callFlake(evalState,
+        nix::flake::lockFlake(
+          flakeSettings,
+          evalState,
+          flakeRef,
+          nix::flake::LockFlags {
+            .updateLockFile = false,
+            .useRegistries = false,
+            .allowUnlocked = false,
+          }),
+        *r);
+      return r;
+    }|]
 
 getFlakeFromGit :: Ptr EvalState -> Text -> Text -> Text -> IO RawValue
 getFlakeFromGit evalState url ref rev =
-  let
-    urlb = encodeUtf8 url
-    refb = encodeUtf8 ref
-    revb = encodeUtf8 rev
-  in [C.throwBlock| Value *{
+  let urlb = encodeUtf8 url
+      refb = encodeUtf8 ref
+      revb = encodeUtf8 rev
+   in [C.throwBlock| Value *{
     EvalState &evalState = *$(EvalState *evalState);
     Value *r = new (NoGC) Value();
     std::string url($bs-ptr:urlb, $bs-len:urlb);
@@ -599,7 +674,7 @@
       *r);
     return r;
   }|]
-    >>= mkRawValue
+        >>= mkRawValue
 
 getFlakeFromArchiveUrl :: Ptr EvalState -> Text -> IO RawValue
 getFlakeFromArchiveUrl evalState url = do
@@ -612,15 +687,15 @@
   p' <- getStringIgnoreContext p
   getFlakeFromFlakeRef evalState p'
 
-traverseWithKey_ :: Applicative f => (k -> a -> f ()) -> Map k a -> f ()
+traverseWithKey_ :: (Applicative f) => (k -> a -> f ()) -> Map k a -> f ()
 traverseWithKey_ f = M.foldrWithKey (\k a more -> f k a *> more) (pure ())
 
 class ToRawValue a where
   toRawValue :: Ptr EvalState -> a -> IO RawValue
-  default toRawValue :: ToValue a => Ptr EvalState -> a -> IO RawValue
+  default toRawValue :: (ToValue a) => Ptr EvalState -> a -> IO RawValue
   toRawValue evalState a = rtValue <$> toValue evalState a
 
-class ToRawValue a => ToValue a where
+class (ToRawValue a) => ToValue a where
   type NixTypeFor a :: Type
   toValue :: Ptr EvalState -> a -> IO (Value (NixTypeFor a))
 
@@ -660,10 +735,10 @@
   toValue _ b =
     coerce
       <$> [C.block| Value *{
-      Value *r = new (NoGC) Value();
-      r->mkBool($(bool b));
-      return r;
-    }|]
+        Value *r = new (NoGC) Value();
+        r->mkBool($(bool b));
+        return r;
+      }|]
 
 instance ToRawValue Bool
 
@@ -681,10 +756,10 @@
   toValue _ i =
     coerce
       <$> [C.block| Value *{
-    Value *r = new (NoGC) Value();
-    r->mkInt($(int64_t i));
-    return r;
-  }|]
+        Value *r = new (NoGC) Value();
+        r->mkInt($(int64_t i));
+        return r;
+      }|]
 
 instance ToRawValue Int
 
@@ -699,10 +774,10 @@
   toValue _ f =
     coerce
       <$> [C.block| Value *{
-        Value *r = new (NoGC) Value();
-        r->mkFloat($(double f));
-        return r;
-      }|]
+          Value *r = new (NoGC) Value();
+          r->mkFloat($(double f));
+          return r;
+        }|]
 
 instance ToRawValue Double
 
@@ -710,24 +785,36 @@
   type NixTypeFor Double = NixFloat
   toValue es f = toValue es (fromRational (toRational f) :: C.CDouble)
 
+{- ORMOLU_DISABLE -}
 -- | Nix String
 instance ToValue ByteString where
   type NixTypeFor ByteString = NixString
-  toValue _ s =
+  toValue evalState s =
     -- TODO simplify when r->mkString(string_view) is safe in all supported Nix versions
     coerce
       <$> [C.block| Value *{
-    Value *r = new (NoGC) Value();
-    std::string_view s($bs-ptr:s, $bs-len:s);
-    // If empty, the pointer may be invalid; don't use it.
-    if (s.size() == 0) {
-      r->mkString("");
-    }
-    else {
-      r->mkString(GC_STRNDUP(s.data(), s.size()));
-    }
-    return r;
-  }|]
+        Value *r = new (NoGC) Value();
+        std::string_view s($bs-ptr:s, $bs-len:s);
+#if NIX_IS_AT_LEAST(2, 33, 0)
+        EvalState &evalState = *$(EvalState *evalState);
+        if (s.size() == 0) {
+          r->mkString(std::string_view(""), evalState.mem);
+        }
+        else {
+          r->mkString(std::string_view(GC_STRNDUP(s.data(), s.size()), s.size()), evalState.mem);
+        }
+#else
+        // If empty, the pointer may be invalid; don't use it.
+        if (s.size() == 0) {
+          r->mkString("");
+        }
+        else {
+          r->mkString(GC_STRNDUP(s.data(), s.size()));
+        }
+#endif
+        return r;
+      }|]
+{- ORMOLU_ENABLE -}
 
 -- | Nix String
 instance ToRawValue ByteString
@@ -740,47 +827,53 @@
   type NixTypeFor Text = NixString
   toValue es s = toValue es (encodeUtf8 s)
 
-instance ToRawValue a => ToRawValue (Map ByteString a)
+instance (ToRawValue a) => ToRawValue (Map ByteString a)
 
-withBindingsBuilder :: Integral n => Ptr EvalState -> n -> (Ptr BindingsBuilder' -> IO ()) -> IO (Value NixAttrs)
+withBindingsBuilder :: (Integral n) => Ptr EvalState -> n -> (Ptr BindingsBuilder' -> IO ()) -> IO (Value NixAttrs)
 withBindingsBuilder evalState n f = do
   withBindingsBuilder' evalState n \bb -> do
     f bb
-    v <- [C.block| Value* {
-      auto v = new (NoGC) Value();
-      v->mkAttrs(*$(BindingsBuilder *bb));
-      return v;
-    }|]
+    v <-
+      [C.block| Value* {
+        auto v = new (NoGC) Value();
+        v->mkAttrs(*$(BindingsBuilder *bb));
+        return v;
+      }|]
     Value <$> mkRawValue v
 
-withBindingsBuilder' :: Integral n => Ptr EvalState -> n -> (Ptr BindingsBuilder' -> IO a) -> IO a
+{- ORMOLU_DISABLE -}
+withBindingsBuilder' :: (Integral n) => Ptr EvalState -> n -> (Ptr BindingsBuilder' -> IO a) -> IO a
 withBindingsBuilder' evalState n =
   let l :: C.CInt
       l = fromIntegral n
-  in
-    bracket
-      [C.block| BindingsBuilder* {
-        auto &evalState = *$(EvalState *evalState);
-        return new BindingsBuilder(evalState, evalState.allocBindings($(int l)));
-      }|]
-      \bb -> [C.block| void { delete $(BindingsBuilder *bb); }|]
+   in bracket
+        [C.block| BindingsBuilder* {
+          auto &evalState = *$(EvalState *evalState);
+#if NIX_IS_AT_LEAST(2, 32, 0)
+          return new BindingsBuilder(evalState.buildBindings($(int l)));
+#else
+          return new BindingsBuilder(evalState, evalState.allocBindings($(int l)));
+#endif
+        }|]
+        \bb -> [C.block| void { delete $(BindingsBuilder *bb); }|]
+{- ORMOLU_ENABLE -}
 
-instance ToRawValue a => ToValue (Map ByteString a) where
+instance (ToRawValue a) => ToValue (Map ByteString a) where
   type NixTypeFor (Map ByteString a) = NixAttrs
 
   toValue evalState attrs = withBindingsBuilder evalState (length attrs) \bb -> do
     attrs & traverseWithKey_ \k a -> do
       RawValue aRaw <- toRawValue evalState a
       [C.block| void {
-          EvalState &evalState = *$(EvalState *evalState);
-          std::string k($bs-ptr:k, $bs-len:k);
-          Value &a = *$(Value *aRaw);
-          $(BindingsBuilder *bb)->alloc(evalState.symbols.create(k)) = a;
-        }|]
+        EvalState &evalState = *$(EvalState *evalState);
+        std::string k($bs-ptr:k, $bs-len:k);
+        Value &a = *$(Value *aRaw);
+        $(BindingsBuilder *bb)->alloc(evalState.symbols.create(k)) = a;
+      }|]
 
-instance ToRawValue a => ToRawValue (Map Text a)
+instance (ToRawValue a) => ToRawValue (Map Text a)
 
-instance ToRawValue a => ToValue (Map Text a) where
+instance (ToRawValue a) => ToValue (Map Text a) where
   type NixTypeFor (Map Text a) = NixAttrs
   toValue evalState attrs = toValue evalState (M.mapKeys encodeUtf8 attrs)
 
@@ -806,15 +899,15 @@
 newtype ViaJSON a = ViaJSON {fromViaJSON :: a}
   deriving newtype (Eq, Ord, Read, Show)
 
-instance A.ToJSON a => ToRawValue (ViaJSON a) where
+instance (A.ToJSON a) => ToRawValue (ViaJSON a) where
   toRawValue es (ViaJSON a) = toRawValue es (A.toJSON a)
 
-hmTraverseWithKey_ :: Applicative f => (k -> a -> f ()) -> H.HashMap k a -> f ()
+hmTraverseWithKey_ :: (Applicative f) => (k -> a -> f ()) -> H.HashMap k a -> f ()
 hmTraverseWithKey_ f = H.foldrWithKey (\k a more -> f k a *> more) (pure ())
 
-instance ToRawValue a => ToRawValue (H.HashMap Text a)
+instance (ToRawValue a) => ToRawValue (H.HashMap Text a)
 
-instance ToRawValue a => ToValue (H.HashMap Text a) where
+instance (ToRawValue a) => ToValue (H.HashMap Text a) where
   type NixTypeFor (H.HashMap Text a) = NixAttrs
 
   toValue evalState attrs = withBindingsBuilder evalState (length attrs) \bb -> do
@@ -822,15 +915,16 @@
       RawValue aRaw <- toRawValue evalState a
       let k = encodeUtf8 k'
       [C.block| void {
-          EvalState &evalState = *$(EvalState *evalState);
-          std::string k($bs-ptr:k, $bs-len:k);
-          Value &a = *$(Value *aRaw);
-          $(BindingsBuilder *bb)->alloc(evalState.symbols.create(k)) = a;
-        }|]
+        EvalState &evalState = *$(EvalState *evalState);
+        std::string k($bs-ptr:k, $bs-len:k);
+        Value &a = *$(Value *aRaw);
+        $(BindingsBuilder *bb)->alloc(evalState.symbols.create(k)) = a;
+      }|]
 
-instance ToRawValue a => ToRawValue (Vector a)
+instance (ToRawValue a) => ToRawValue (Vector a)
 
-instance ToRawValue a => ToValue (Vector a) where
+{- ORMOLU_DISABLE -}
+instance (ToRawValue a) => ToValue (Vector a) where
   type NixTypeFor (Vector a) = NixList
   toValue evalState vec =
     coerce <$> do
@@ -838,8 +932,15 @@
           l = fromIntegral (length vec)
       b <-
         [C.block| ListBuilder* {
+#if NIX_IS_AT_LEAST(2, 33, 0)
           EvalState &evalState = *$(EvalState *evalState);
+          return new (NoGC) ListBuilder(evalState.mem, $(int l));
+#elif NIX_IS_AT_LEAST(2, 32, 0)
+          return new (NoGC) ListBuilder($(int l));
+#else
+          EvalState &evalState = *$(EvalState *evalState);
           return new (NoGC) ListBuilder(evalState, $(int l));
+#endif
         }|]
       vec & V.imapM_ \i a -> do
         RawValue aRaw <- toRawValue evalState a
@@ -855,9 +956,10 @@
           return v;
         }|]
       Value <$> mkRawValue v
+{- ORMOLU_ENABLE -}
 
-instance ToRawValue a => ToRawValue [a]
+instance (ToRawValue a) => ToRawValue [a]
 
-instance ToRawValue a => ToValue [a] where
+instance (ToRawValue a) => ToValue [a] where
   type NixTypeFor [a] = NixList
   toValue es l = toValue es (V.fromList l)
diff --git a/src/Hercules/CNix/Expr/Context.hs b/src/Hercules/CNix/Expr/Context.hs
--- a/src/Hercules/CNix/Expr/Context.hs
+++ b/src/Hercules/CNix/Expr/Context.hs
@@ -33,6 +33,8 @@
 
 data ListBuilder'
 
+data ListViewType'
+
 context :: C.Context
 context =
   C.cppCtx
@@ -57,4 +59,6 @@
           =: [t|BindingsBuilder'|]
           <> C.TypeName "ListBuilder"
           =: [t|ListBuilder'|]
+          <> C.TypeName "ListViewType"
+          =: [t|ListViewType'|]
     }
diff --git a/src/Hercules/CNix/Expr/Typed.hs b/src/Hercules/CNix/Expr/Typed.hs
--- a/src/Hercules/CNix/Expr/Typed.hs
+++ b/src/Hercules/CNix/Expr/Typed.hs
@@ -1,7 +1,7 @@
+{-# LANGUAGE CPP #-}
 {-# LANGUAGE EmptyDataDecls #-}
 {-# LANGUAGE QuasiQuotes #-}
 {-# LANGUAGE TemplateHaskell #-}
-{-# LANGUAGE CPP #-}
 
 module Hercules.CNix.Expr.Typed
   ( Value (..),
@@ -137,8 +137,8 @@
 
 getBool :: Value Bool -> IO Bool
 getBool (Value (RawValue v)) =
-  (0 /=) <$>
-    [C.exp| int { $(Value *v)->boolean() ? 1 : 0 }|]
+  (0 /=)
+    <$> [C.exp| int { $(Value *v)->boolean() ? 1 : 0 }|]
 
 getInt :: Value NixInt -> IO Int64
 getInt (Value (RawValue v)) =
@@ -154,8 +154,8 @@
 
 hasContext :: Value NixString -> IO Bool
 hasContext (Value (RawValue v)) =
-  (0 /=) <$>
-    [C.exp| int { $(Value *v)->context() ? 1 : 0 }|]
+  (0 /=)
+    <$> [C.exp| int { $(Value *v)->context() ? 1 : 0 }|]
 
 class CheckType a where
   checkType :: Ptr EvalState -> RawValue -> IO (Maybe (Value a))
