diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,168 @@
 
 ---
 
+## [0.17.6] - 2026-08-30
+
+Point release on the 0.17.x line, and the last backward-incompatible batch before the 1.0 surface
+freeze. Five kernel-surface changes land together as the "0.17 breaking batch":
+`null` becomes `isEmpty`, `read*`/`show*` become `parse*`/`print*`, `TypeScheme.constraints` and
+`TypeVariableConstraints.classes` change shape, and the constructor primitives are finalized.
+Alongside them, a genuine soundness gap in type inference is fixed — class constraints now propagate
+through function-domain positions — and the cold-seed path is rebuilt on the Java host so that
+breaking core-type changes are no longer structurally impossible to bootstrap.
+
+**Backward-incompatible.** All five surface changes below alter the published kernel. Code written
+against 0.17.5 must be updated:
+
+- **[#682]** `hydra.lib.{lists,sets,maps,strings}.null` -> `.isEmpty`.
+- **[#691]** the `read*`/`show*` family -> `parse*`/`print*`.
+- **[#683]** `TypeScheme.constraints`: `optional<map>` -> plain `map`.
+- **[#685]** `TypeVariableConstraints.classes`: `list` -> `set`.
+- **[#687]** constructor primitives finalized: `lists.pure` is removed; `optionals.given`,
+  `eithers.left`/`right` and `pairs.pair` are added.
+
+[#682]: https://github.com/CategoricalData/hydra/issues/682
+[#683]: https://github.com/CategoricalData/hydra/issues/683
+[#685]: https://github.com/CategoricalData/hydra/issues/685
+[#687]: https://github.com/CategoricalData/hydra/issues/687
+[#691]: https://github.com/CategoricalData/hydra/issues/691
+
+### Highlights
+
+- **Class constraints propagate through contravariant positions**
+  ([#702](https://github.com/CategoricalData/hydra/issues/702)): inference silently dropped a class
+  constraint when the constrained type variable was bound through the **domain** (input) position of a
+  higher-order function argument, while transferring it correctly in output positions — a real soundness
+  gap in the type checker. It had been papered over twice by freezing `encodeMap`/`encodeSet` to
+  monomorphic `Int`. Both freezes and the [#554](https://github.com/CategoricalData/hydra/issues/554)
+  `toPrimitive` stopgap are now removed, the definitions re-polymorphized, and a domain-position
+  regression test added. A `sets.filter` overlay lands across Java, Python, Scala, TypeScript and the
+  Lisp dialects as its first consumer.
+- **Cold-seed rebuilt on the Java host**
+  ([#703](https://github.com/CategoricalData/hydra/issues/703)): CI's cold-seed steps compiled HEAD's
+  kernel `Types` source against the *published* Hackage `hydra-kernel`, which is sound only for additive
+  changes and structurally impossible for any breaking core-type-shape change — the root of a recurring
+  red-CI class (#500, #608, #617, #417, #683). The Haskell cold-seeder (`ColdSeedMain`, `typesmods/`,
+  Check-2) is deleted and replaced by a data-driven Java-host schema-walking seed
+  (`seed-dist-haskell.sh`), which decodes `dist/json` and emits Haskell text without ever linking
+  HEAD types against a published kernel.
+- **`hydra.paths` completed** ([#716](https://github.com/CategoricalData/hydra/issues/716)):
+  the step unions are realigned with the `hydra.core` term and type grammars — missing variants
+  added (`annotatedAnnotation`, `eitherLeft`/`eitherRight`, `effect`), undirected steps given a
+  direction, and variants named after retired constructors renamed. `hydra.print.paths` is rewritten
+  as serialization-only (printers, parsers and a round-trip law), and the graph view moves to a new
+  `hydra.shredding` module (`shredGraph`/`shredTerm`/`shredSchema`), which the Graphviz coder now
+  consumes.
+- **`hydra.lib.ordering` is genuinely structural on every host**
+  ([#718](https://github.com/CategoricalData/hydra/issues/718)): the Java, Python and Scala overlay
+  implementations fell back to comparing *printed strings* for non-literal and mixed-variant terms,
+  contradicting the normative `ordering-and-equality.md` (records field-by-field in declaration
+  order, unions by declared-variant order then payload, wrappers by wrapped value, decimals by
+  numeric value then scale — with no print-based fallback anywhere). All three are now structural;
+  Java delegates to the generated `compareTo`.
+- **Bash driver-layer audit** ([#714](https://github.com/CategoricalData/hydra/issues/714)):
+  a prerequisite to the remaining #416 promotion. Six dead build scripts superseded by
+  `transform-json-to-target.sh` are deleted, and the #608 oil-and-water and #540 header-idempotency
+  regression harnesses are wired into `test-regressions.sh`.
+
+- **Backward-compatibility and deprecation policy**
+  ([#676](https://github.com/CategoricalData/hydra/issues/676)): adds the normative spec chapter defining
+  what "compatible" means after 1.0 — the additivity calculus, the deprecation/rename mechanism, and
+  enforcement. `LifecycleInfo.deprecatedSince` moves from defined-but-unused to populated on **32**
+  kernel primitives slated for 0.18 deprecation, via a new `deprecatedSince` DSL helper.
+- **Effect-sequencing contract specified and tested**
+  ([#675](https://github.com/CategoricalData/hydra/issues/675)): `hydra.lib.effects` carries a sequencing
+  contract — effects composed in a specified order, exactly once each, with `bind`'s continuation deferred
+  until the prior effect has run. On the lazy Haskell host this falls out of the evaluation model; on the
+  eight eager hosts it depended entirely on each hand-written interpreter. The obligations are now written
+  into `effects.md` and backed by ordering-observation tests for `bind`/`foldList`/`mapList`/`mapOptional`.
+- **Non-GC memory-discipline investigation**
+  ([#678](https://github.com/CategoricalData/hydra/issues/678)): Hydra's data model says nothing about
+  allocation, freeing, or ownership, which is what gates Swift, Rust, C++ and C as runtime hosts. This
+  investigation delivers per-language verdicts plus leak-free proof-of-concept implementations — Rust
+  (`Box` structural ownership), C++ (`unique_ptr` + arena) and C (arena), all ASan/LSan-clean on
+  construct-and-drop of a recursive `Term` — and an airtight analysis of Swift's class-forcing requirement.
+- **Build system promotion continues** ([#416](https://github.com/CategoricalData/hydra/issues/416)):
+  adds `hydra.build.Registry`, which models per-language build identity as data (name / coder package /
+  family) with per-script scope lists. `syncMatrix` and `langExpansion` are rewired onto it, and the
+  hardcoded language lists in the test and bench scripts are retired in favour of a generated
+  `languages.json`. Later in the cycle: `publishsets.reverseDepClosure` and
+  `publishsets.requiresCoders` promote reverse-dependency propagation and per-package coder-loading
+  into data, an `expected-libraries.json` artifact lets the Common Lisp and Emacs Lisp harnesses
+  fail fast on a missing `hydra.lib`, and a host-independent `apply-assembly-plan.sh` executor
+  (with a conformance test) takes over the Java `assemble-distribution` Step 0.
+- **Serialization specification chapter**
+  ([#674](https://github.com/CategoricalData/hydra/issues/674)): documents serializability, the
+  encode/decode contract, and the round-trip law; `isSerializable` now forbids `void`
+  ([#690](https://github.com/CategoricalData/hydra/issues/690)), and `hydra.lib.functions.absurd` is added
+  as the void eliminator ([#684](https://github.com/CategoricalData/hydra/issues/684)).
+
+### Bug fixes
+
+- **Hoisting silently produced terms with free variables**
+  ([#717](https://github.com/CategoricalData/hydra/issues/717)):
+  `augmentBindingsWithNewFreeVars` returned a binding *unchanged* when a captured variable had no
+  type in the context, yielding a top-level binding with free variables — a term valid before
+  hoisting and invalid after, produced with no error. It is now total and fails loudly, per the
+  kernel's fail-immediately rule.
+- **TypeScript `div`/`mod` used truncating semantics**
+  ([#677](https://github.com/CategoricalData/hydra/issues/677)): corrected to floor/Knuth division via
+  shared bigint helpers, with an `Optional` codomain; the dead `maybeDiv`/`maybeMod` are deleted and an
+  emitted-path test added.
+- **Overlay lag behind the `null` -> `isEmpty` rename**
+  ([#682](https://github.com/CategoricalData/hydra/issues/682)): the Java, Python, Scala, TypeScript and
+  Lisp overlays each kept a stale spelling (`isEmpty_`, a half-renamed Python `null` impl, and Lisp
+  camelCase where the coder emits snake_case), breaking generation until reconciled.
+- **`check-haskell-def-completeness` could not resolve qualified assembly references**
+  ([#683](https://github.com/CategoricalData/hydra/issues/683)), surfaced once the #703 cold-clone path
+  began reaching the check.
+- **Void-typed definitions leaked into DSL-ref generation**
+  ([#684](https://github.com/CategoricalData/hydra/issues/684)): `absurd` and friends are now excluded
+  across all hosts.
+- **Scheme bootstrap loader gaps** ([#641](https://github.com/CategoricalData/hydra/issues/641)):
+  missing `functions.scm`/`ordering.scm` in the native-lib load list, missing defaults in the def-module
+  load list, and a guile-only import allowlist.
+- **Bootstrap diff detail was swallowed** ([#671](https://github.com/CategoricalData/hydra/issues/671)):
+  now persisted to `<run>/<path>.diff`, with `compare_output`'s return value no longer discarded.
+  Separately, `compare_output` mis-derived the `src/main` vs `src/test` root per file
+  ([#706](https://github.com/CategoricalData/hydra/issues/706)).
+- **Nested duplicate output path** ([#664](https://github.com/CategoricalData/hydra/issues/664)) from
+  `hydra-kernel` being hardcoded into `defaultOutput`.
+- **Bootstrap codegen exceptions hid their stack traces**
+  ([#707](https://github.com/CategoricalData/hydra/issues/707)) in `bootstrap.ts`.
+
+### Release tooling
+
+- `bin/check-release-env.sh` ([#704](https://github.com/CategoricalData/hydra/issues/704)) adds a
+  publish-time toolchain preflight, after JDK- and twine-version problems cost real time at 0.17.5.
+- The TypeScript head's node floor is lowered to `^18.0.0 || >=20.0.0`, the true vitest constraint
+  ([#708](https://github.com/CategoricalData/hydra/issues/708)).
+- Hydra does not create GitHub Releases; the detached `.asc` signature stays in `release-artifacts/`
+  until [#441](https://github.com/CategoricalData/hydra/issues/441)
+  ([#508](https://github.com/CategoricalData/hydra/issues/508)).
+
+### Known issues
+
+- The `hostOverrides` shim is pinned to `{java: local, python: local}` for the duration of the breaking
+  batch, since the new kernel surface is by definition absent from published 0.17.5. It must be retired
+  once 0.17.6 is visible in Maven Central and PyPI
+  ([#508](https://github.com/CategoricalData/hydra/issues/508)).
+- **`hydra-ext` does not ship to Maven Central for Java**, as in 0.17.4 and 0.17.5: the Java coder's
+  visitor-pattern inner interface still collides with an enclosing type named `Visitor`
+  ([#643](https://github.com/CategoricalData/hydra/issues/643)), so the package does not compile for
+  that target. It publishes to Hackage and PyPI as usual.
+- The breaking batch leaves three deliberate 0.18 tails, each separately tracked:
+  `effects.pure` is retained provisionally pending a name decision
+  ([#688](https://github.com/CategoricalData/hydra/issues/688)); the `printable` type class that would
+  promote the print/parse convention into enforced dispatch awaits
+  [#497](https://github.com/CategoricalData/hydra/issues/497); and the literal conversion lattice is
+  gated on [#129](https://github.com/CategoricalData/hydra/issues/129) via
+  [#689](https://github.com/CategoricalData/hydra/issues/689). Anticipated `TypeClassConstraint`
+  variants remain open as [#686](https://github.com/CategoricalData/hydra/issues/686) — the `set`
+  element type must stay orderable as variants are added.
+
+---
+
 ## [0.17.5] - 2026-08-19
 
 Point release on the 0.17.x line. Two language-surface changes lead: sum-type eliminators unify on
diff --git a/hydra-python.cabal b/hydra-python.cabal
--- a/hydra-python.cabal
+++ b/hydra-python.cabal
@@ -1,11 +1,11 @@
 cabal-version: 1.12
 
--- This file has been generated from package.yaml by hpack version 0.38.1.
+-- This file has been generated from package.yaml by hpack version 0.39.1.
 --
 -- see: https://github.com/sol/hpack
 
 name:           hydra-python
-version:        0.17.5
+version:        0.17.6
 synopsis:       Hydra's Python coder: emit Python source from Hydra modules
 description:    Hydra is an implementation of the LambdaGraph data model, which takes advantage of an isomorphism between labeled hypergraphs and typed lambda calculus: in Hydra, "graphs are programs, and programs are graphs". Python support for Hydra
 category:       Data
@@ -47,6 +47,6 @@
   build-depends:
       base >=4.19.0 && <4.22
     , containers >=0.6.7 && <0.8
-    , hydra-kernel ==0.17.5
+    , hydra-kernel ==0.17.6
     , scientific >=0.3.7 && <0.4
   default-language: Haskell2010
diff --git a/src/main/haskell/Hydra/Dsl/Python/Environment.hs b/src/main/haskell/Hydra/Dsl/Python/Environment.hs
--- a/src/main/haskell/Hydra/Dsl/Python/Environment.hs
+++ b/src/main/haskell/Hydra/Dsl/Python/Environment.hs
@@ -15,6 +15,7 @@
 import qualified Hydra.Util as Util
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 import qualified Data.Map as M
 import qualified Data.Set as S
 
diff --git a/src/main/haskell/Hydra/Dsl/Python/Names.hs b/src/main/haskell/Hydra/Dsl/Python/Names.hs
--- a/src/main/haskell/Hydra/Dsl/Python/Names.hs
+++ b/src/main/haskell/Hydra/Dsl/Python/Names.hs
@@ -38,6 +38,7 @@
 import qualified Hydra.Variants as Variants
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 import qualified Data.Map as M
 import qualified Data.Set as S
 
diff --git a/src/main/haskell/Hydra/Dsl/Python/Syntax.hs b/src/main/haskell/Hydra/Dsl/Python/Syntax.hs
--- a/src/main/haskell/Hydra/Dsl/Python/Syntax.hs
+++ b/src/main/haskell/Hydra/Dsl/Python/Syntax.hs
@@ -9,6 +9,7 @@
 import qualified Hydra.Typed as Typed
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 
 -- | DSL name token for hydra.python.syntax.AnnotatedRhs
 annotatedRhsAnnotatedRhs :: Typed.TypedName Syntax.AnnotatedRhs
diff --git a/src/main/haskell/Hydra/Dsl/Python/Utils.hs b/src/main/haskell/Hydra/Dsl/Python/Utils.hs
--- a/src/main/haskell/Hydra/Dsl/Python/Utils.hs
+++ b/src/main/haskell/Hydra/Dsl/Python/Utils.hs
@@ -39,6 +39,7 @@
 import qualified Hydra.Variants as Variants
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 import qualified Data.Set as S
 
 -- | DSL reference to hydra.python.utils.annotatedExpression
diff --git a/src/main/haskell/Hydra/Python/Coder.hs b/src/main/haskell/Hydra/Python/Coder.hs
--- a/src/main/haskell/Hydra/Python/Coder.hs
+++ b/src/main/haskell/Hydra/Python/Coder.hs
@@ -67,6 +67,7 @@
 import qualified Hydra.Variants as Variants
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 import qualified Data.Map as M
 import qualified Data.Set as S
 
@@ -201,7 +202,7 @@
                         body = Core.lambdaBody v0
                     in (Optionals.match (Maps.lookup v countByName) (Maps.insert v 1 countByName, (Lists.cons field done)) (\count ->
                       let count2 = Math.add count 1
-                          v2 = Core.Name (Strings.concat2 (Core.unName v) (Literals.showInt32 count2))
+                          v2 = Core.Name (Strings.concat2 (Core.unName v) (Literals.printInt32 count2))
                           newBody = Reduction.alphaConvert v v2 body
                           newLam =
                                   Core.Lambda {
@@ -343,7 +344,7 @@
 
       let firstArg = Optionals.withDefault (Utils.pyNameToPyExpression (Syntax.Name "")) (Lists.head hargs)
           restArgs = Lists.drop 1 hargs
-          withRest = \e -> Logic.ifElse (Lists.null restArgs) e (Utils.functionCall (Utils.pyExpressionToPyPrimary e) restArgs)
+          withRest = \e -> Logic.ifElse (Lists.isEmpty restArgs) e (Utils.functionCall (Utils.pyExpressionToPyPrimary e) restArgs)
           defaultCase =
                   Eithers.bind (encodeTermInline cx env False fun) (\pfun -> Right (Utils.functionCall (Utils.pyExpressionToPyPrimary pfun) hargs, rargs))
       in case (Strip.deannotateAndDetypeTerm fun) of
@@ -355,7 +356,7 @@
         Core.TermUnwrap _ ->
           let valueExpr = Utils.projectFromExpression firstArg (Syntax.Name "value")
               allArgs = Lists.concat2 restArgs rargs
-          in (Logic.ifElse (Lists.null allArgs) (Right (valueExpr, [])) (Right (Utils.functionCall (Utils.pyExpressionToPyPrimary valueExpr) allArgs, [])))
+          in (Logic.ifElse (Lists.isEmpty allArgs) (Right (valueExpr, [])) (Right (Utils.functionCall (Utils.pyExpressionToPyPrimary valueExpr) allArgs, [])))
         Core.TermLambda _ -> Eithers.bind (encodeTermInline cx env False fun) (\pfun -> Right (Utils.functionCall (Utils.pyExpressionToPyPrimary pfun) hargs, rargs))
         Core.TermVariable v0 ->
           let g = pythonEnvironmentGetGraph env
@@ -366,7 +367,7 @@
                 consumeCount = Ordering.min elArity (Lists.length allArgs)
                 consumedArgs = Lists.take consumeCount allArgs
                 remainingArgs = Lists.drop consumeCount allArgs
-            in (Logic.ifElse (Lists.null consumedArgs) (Eithers.bind (encodeVariable cx env v0 []) (\expr -> Right (expr, rargs))) (Logic.ifElse (Sets.member v0 inlineVars) (Right (
+            in (Logic.ifElse (Lists.isEmpty consumedArgs) (Eithers.bind (encodeVariable cx env v0 []) (\expr -> Right (expr, rargs))) (Logic.ifElse (Sets.member v0 inlineVars) (Right (
               Utils.functionCall (Utils.pyExpressionToPyPrimary (lazyDotGet (PythonNames.termVariableReference env v0))) consumedArgs,
               remainingArgs)) (Right (
               Utils.functionCall (Utils.pyNameToPyPrimary (PythonNames.encodeName True Util.CaseConventionLowerSnake env v0)) consumedArgs,
@@ -461,7 +462,7 @@
                               Syntax.functionDefRawBlock = body}
                 in (Right (Syntax.StatementCompound (Syntax.CompoundStatementFunction (Syntax.FunctionDefinition {
                   Syntax.functionDefinitionDecorators = Nothing,
-                  Syntax.functionDefinitionRaw = funcDefRaw})))))))))))) (\csa -> Logic.ifElse (Lists.null lambdaParams) (
+                  Syntax.functionDefinitionRaw = funcDefRaw})))))))))))) (\csa -> Logic.ifElse (Lists.isEmpty lambdaParams) (
           let mcs = extractCaseElimination term1
           in (Optionals.match mcs (Eithers.bind (encodeTermMultiline cx env term1) (\stmts -> Optionals.match (Lists.head stmts) (Left (Errors.ErrorOther (Errors.OtherError "encodeTermMultiline returned no statements"))) (\x -> Right x))) (\cs ->
             let tname = Core.caseStatementTypeName cs
@@ -611,7 +612,7 @@
                     Optionals.match (Packaging.termDefinitionSignature v0) (Core.TypeScheme {
                       Core.typeSchemeVariables = [],
                       Core.typeSchemeBody = (Core.TypeVariable (Core.Name "hydra.core.Unit")),
-                      Core.typeSchemeConstraints = Nothing}) (\sig -> Scoping.termSignatureToTypeScheme sig)
+                      Core.typeSchemeConstraints = Maps.empty}) (\sig -> Scoping.termSignatureToTypeScheme sig)
         in (Eithers.bind (Annotations.getTermDescription cx (pythonEnvironmentGetGraph env) term) (\comment ->
           let normComment = Optionals.map Formatting.normalizeComment comment
           in (Eithers.bind (encodeTermAssignment cx env True name term typ normComment) (\stmt -> Right [
@@ -675,13 +676,13 @@
 encodeFloatValueEncodeFloat32 :: Float -> Either t0 Syntax.Expression
 encodeFloatValueEncodeFloat32 v =
 
-      let s = Literals.showFloat32 v
+      let s = Literals.printFloat32 v
       in (Logic.ifElse (Equality.equal s "NaN") (Right (encodeFloatValuePySpecialFloat "nan")) (Logic.ifElse (Equality.equal s "Infinity") (Right (encodeFloatValuePySpecialFloat "inf")) (Logic.ifElse (Equality.equal s "-Infinity") (Right (encodeFloatValuePySpecialFloat "-inf")) (Right (Utils.pyAtomToPyExpression (Syntax.AtomNumber (Syntax.NumberFloat (Literals.float32ToFloat64 v))))))))
 
 encodeFloatValueEncodeFloat64 :: Double -> Either t0 Syntax.Expression
 encodeFloatValueEncodeFloat64 v =
 
-      let s = Literals.showFloat64 v
+      let s = Literals.printFloat64 v
       in (Logic.ifElse (Equality.equal s "NaN") (Right (encodeFloatValuePySpecialFloat "nan")) (Logic.ifElse (Equality.equal s "Infinity") (Right (encodeFloatValuePySpecialFloat "inf")) (Logic.ifElse (Equality.equal s "-Infinity") (Right (encodeFloatValuePySpecialFloat "-inf")) (Logic.ifElse (Equality.equal s "-0.0") (Right (encodeFloatValuePySpecialFloat "-0.0")) (Right (Utils.pyAtomToPyExpression (Syntax.AtomNumber (Syntax.NumberFloat v))))))))
 
 encodeFloatValuePySpecialFloat :: String -> Syntax.Expression
@@ -797,7 +798,7 @@
           Utils.pyAtomToPyExpression (Syntax.AtomList (Utils.pyList (Lists.map (\byteVal -> Utils.pyAtomToPyExpression (Syntax.AtomNumber (Syntax.NumberInteger (Literals.int32ToBigint byteVal)))) byteValues)))]))
       Core.LiteralBoolean v0 -> Right (Utils.pyAtomToPyExpression (Logic.ifElse v0 Syntax.AtomTrue Syntax.AtomFalse))
       Core.LiteralDecimal v0 -> Right (Utils.functionCall (Utils.pyNameToPyPrimary (Syntax.Name "Decimal")) [
-        Utils.singleQuotedString (Literals.showDecimal v0)])
+        Utils.singleQuotedString (Literals.printDecimal v0)])
       Core.LiteralFloat v0 -> encodeFloatValue v0
       Core.LiteralInteger v0 -> encodeIntegerValue v0
       Core.LiteralString v0 -> Right (Utils.stringToPyExpression Syntax.QuoteStyleDouble v0)
@@ -872,7 +873,7 @@
                     Logic.ifElse (Logic.or isTypeMod (Logic.not useInlineTypeParams)) (PythonEnvironment.pythonModuleMetadataTypeVariables meta) Sets.empty
             tvarStmts = Lists.map (\tv -> tvarStatement (PythonNames.encodeTypeVariable tv)) (Sets.toList tvars)
             body =
-                    Lists.filter (\group -> Logic.not (Lists.null group)) (Lists.concat [
+                    Lists.filter (\group -> Logic.not (Lists.isEmpty group)) (Lists.concat [
                       [
                         commentStmts,
                         importStmts,
@@ -933,7 +934,7 @@
                     Core.bindingTypeScheme = (Just ts)}
           isComplex = Predicates.isComplexBinding tc binding
           isTrivial = Predicates.isTrivialTerm term
-      in (Logic.ifElse (Logic.and isComplex (Logic.not isTrivial)) (Logic.ifElse (Logic.and (Logic.not topLevel) (Lists.null params)) (Eithers.bind (encodeTermInline cx env2 False term) (\bodyExpr ->
+      in (Logic.ifElse (Logic.and isComplex (Logic.not isTrivial)) (Logic.ifElse (Logic.and (Logic.not topLevel) (Lists.isEmpty params)) (Eithers.bind (encodeTermInline cx env2 False term) (\bodyExpr ->
         let pyName = PythonNames.encodeName False Util.CaseConventionLowerSnake env2 name
             lazyExpr = makeLazy bodyExpr
         in (Right (Utils.annotatedStatement comment (Utils.assignmentStatement pyName lazyExpr))))) (Eithers.bind (Eithers.mapList (encodeBindingAs cx env2) bindings) (\bindingStmts -> functionDefinitionToExpr cx env2 name tparams params body doms mcod comment bindingStmts))) (Eithers.bind (encodeTermInline cx env2 False body) (\bodyExpr ->
@@ -978,7 +979,7 @@
                         PythonEnvironment.pythonEnvironmentOverlaySubs = (PythonEnvironment.pythonEnvironmentOverlaySubs innerEnv0)}
           in (Eithers.bind (encodeTermInline cx innerEnv False innerBody) (\pbody ->
             let pparams = Lists.map (PythonNames.encodeName False Util.CaseConventionLowerSnake innerEnv) params
-            in (Logic.ifElse (Lists.null bindings) (Right (makeUncurriedLambda pparams pbody)) (Eithers.bind (Eithers.mapList (encodeBindingAsAssignment cx False innerEnv) bindings) (\pbindingExprs ->
+            in (Logic.ifElse (Lists.isEmpty bindings) (Right (makeUncurriedLambda pparams pbody)) (Eithers.bind (Eithers.mapList (encodeBindingAsAssignment cx False innerEnv) bindings) (\pbindingExprs ->
               let pbindingStarExprs = Lists.map (\ne -> Syntax.StarNamedExpressionSimple ne) pbindingExprs
                   pbodyStarExpr = Utils.pyExpressionToPyStarNamedExpression pbody
                   tupleElements = Lists.concat2 pbindingStarExprs [
@@ -1039,7 +1040,7 @@
         Core.TermLet v0 ->
           let bindings = Core.letBindings v0
               body = Core.letBody v0
-          in (Logic.ifElse (Lists.null bindings) (encodeTermInline cx env False body) (withLetInline env v0 (\innerEnv -> Eithers.bind (Eithers.mapList (encodeBindingAsAssignment cx False innerEnv) bindings) (\pbindingExprs -> Eithers.bind (encodeTermInline cx innerEnv False body) (\pbody ->
+          in (Logic.ifElse (Lists.isEmpty bindings) (encodeTermInline cx env False body) (withLetInline env v0 (\innerEnv -> Eithers.bind (Eithers.mapList (encodeBindingAsAssignment cx False innerEnv) bindings) (\pbindingExprs -> Eithers.bind (encodeTermInline cx innerEnv False body) (\pbody ->
             let pbindingStarExprs = Lists.map (\ne -> Syntax.StarNamedExpressionSimple ne) pbindingExprs
                 pbodyStarExpr = Utils.pyExpressionToPyStarNamedExpression pbody
                 tupleElements = Lists.concat2 pbindingStarExprs [
@@ -1113,7 +1114,7 @@
                     bindings = Typing.functionStructureBindings fs
                     innerBody = Typing.functionStructureBody fs
                     env2 = Typing.functionStructureEnvironment fs
-                in (Logic.ifElse (Lists.null bindings) (Eithers.bind (encodeTermInline cx env False term) (\expr -> Right [
+                in (Logic.ifElse (Lists.isEmpty bindings) (Eithers.bind (encodeTermInline cx env False term) (\expr -> Right [
                   Utils.returnSingle expr])) (Eithers.bind (Eithers.mapList (encodeBindingAs cx env2) bindings) (\bindingStmts -> Eithers.bind (encodeTermMultiline cx env2 innerBody) (\bodyStmts -> Right (Lists.concat2 bindingStmts bodyStmts))))))
           gathered = Analysis.gatherApplications term
           args = Pairs.first gathered
@@ -1256,7 +1257,7 @@
 -- | Encode a type to a Python expression, quoting if the type has free variables
 encodeTypeQuoted :: PythonEnvironment.PythonEnvironment -> Core.Type -> Either t0 Syntax.Expression
 encodeTypeQuoted env typ =
-    Eithers.bind (encodeType env typ) (\pytype -> Right (Logic.ifElse (Sets.null (Variables.freeVariablesInType typ)) pytype (Utils.doubleQuotedString (Serialization.printExpr (Serde.expressionToExpr pytype)))))
+    Eithers.bind (encodeType env typ) (\pytype -> Right (Logic.ifElse (Sets.isEmpty (Variables.freeVariablesInType typ)) pytype (Utils.doubleQuotedString (Serialization.printExpr (Serde.expressionToExpr pytype)))))
 
 -- | Encode a union elimination as an inline conditional chain (isinstance-based ternary)
 encodeUnionEliminationInline :: Typing.InferenceContext -> PythonEnvironment.PythonEnvironment -> Core.CaseStatement -> Syntax.Expression -> Either Errors.Error Syntax.Expression
@@ -1342,7 +1343,7 @@
           tparamNames = findTypeParams env ftype
           tparams = Lists.map PythonNames.encodeTypeVariable tparamNames
           namePrim = Utils.pyNameToPyPrimary (PythonNames.variantName False env unionName fname)
-      in (Logic.ifElse (Lists.null tparams) namePrim (
+      in (Logic.ifElse (Lists.isEmpty tparams) namePrim (
         let tparamExprs = Lists.map Utils.pyNameToPyExpression tparams
         in (Utils.primaryWithExpressionSlices namePrim tparamExprs)))
 
@@ -1390,12 +1391,12 @@
           asFunctionCall =
                   Utils.functionCall (Utils.pyNameToPyPrimary (PythonNames.encodeName True Util.CaseConventionLowerSnake env name)) args
           asLazyCall = Utils.functionCall (Utils.pyExpressionToPyPrimary (lazyDotGet asVariable)) args
-      in (Logic.ifElse (Logic.not (Lists.null args)) (Logic.ifElse (Sets.member name inlineVars) (Right asLazyCall) (Optionals.match (Lexical.lookupPrimitive g name) (Right asFunctionCall) (\prim ->
+      in (Logic.ifElse (Logic.not (Lists.isEmpty args)) (Logic.ifElse (Sets.member name inlineVars) (Right asLazyCall) (Optionals.match (Lexical.lookupPrimitive g name) (Right asFunctionCall) (\prim ->
         let primArity = Arity.primitiveArity prim
         in (Logic.ifElse (Equality.equal primArity (Lists.length args)) (Right asFunctionCall) (
           let numRemaining = Math.sub primArity (Lists.length args)
               remainingParams =
-                      Lists.map (\i -> Syntax.Name (Strings.concat2 "x" (Literals.showInt32 i))) (Math.range 1 (Math.add numRemaining 1))
+                      Lists.map (\i -> Syntax.Name (Strings.concat2 "x" (Literals.printInt32 i))) (Math.range 1 (Math.add numRemaining 1))
               remainingExprs =
                       Lists.map (\n -> Syntax.ExpressionSimple (Syntax.Disjunction [
                         Syntax.Conjunction [
@@ -1425,31 +1426,31 @@
         let elTrivial1 = Predicates.isTrivialTerm (Core.bindingTerm el)
         in (Optionals.match (Core.bindingTypeScheme el) (Right asVariable) (\ts -> Logic.ifElse (Logic.and (Logic.and (Equality.equal (Arity.typeSchemeArity ts) 0) (Predicates.isComplexBinding tc el)) (Logic.not elTrivial1)) (Right asFunctionCall) (
           let asFunctionRef =
-                  Logic.ifElse (Logic.not (Lists.null (Core.typeSchemeVariables ts))) (makeSimpleLambda (Arity.typeArity (Core.typeSchemeBody ts)) asVariable) asVariable
+                  Logic.ifElse (Logic.not (Lists.isEmpty (Core.typeSchemeVariables ts))) (makeSimpleLambda (Arity.typeArity (Core.typeSchemeBody ts)) asVariable) asVariable
           in (Right asFunctionRef)))))) (\prim ->
         let primArity = Arity.primitiveArity prim
         in (Logic.ifElse (Equality.equal primArity 0) (Right asFunctionCall) (
           let ts = Scoping.termSignatureToTypeScheme (Packaging.primitiveDefinitionSignature (Graph.primitiveDefinition prim))
               asFunctionRef =
-                      Logic.ifElse (Logic.not (Lists.null (Core.typeSchemeVariables ts))) (makeSimpleLambda (Arity.typeArity (Core.typeSchemeBody ts)) asVariable) asVariable
+                      Logic.ifElse (Logic.not (Lists.isEmpty (Core.typeSchemeVariables ts))) (makeSimpleLambda (Arity.typeArity (Core.typeSchemeBody ts)) asVariable) asVariable
           in (Right asFunctionRef))))))) (\typ -> Logic.ifElse (Sets.member name tcLambdaVars) (Right asVariable) (Logic.ifElse (Sets.member name inlineVars) (
         let unwrapped = lazyDotGet asVariable
             asFunctionRef =
-                    Logic.ifElse (Logic.not (Sets.null (Variables.freeVariablesInType typ))) (makeSimpleLambda (Arity.typeArity typ) unwrapped) unwrapped
+                    Logic.ifElse (Logic.not (Sets.isEmpty (Variables.freeVariablesInType typ))) (makeSimpleLambda (Arity.typeArity typ) unwrapped) unwrapped
         in (Right asFunctionRef)) (Logic.ifElse (Logic.not (Maps.member name tcMetadata)) (Optionals.match (Lexical.lookupBinding g name) (
         let asFunctionRef =
-                Logic.ifElse (Logic.not (Sets.null (Variables.freeVariablesInType typ))) (makeSimpleLambda (Arity.typeArity typ) asVariable) asVariable
+                Logic.ifElse (Logic.not (Sets.isEmpty (Variables.freeVariablesInType typ))) (makeSimpleLambda (Arity.typeArity typ) asVariable) asVariable
         in (Right asFunctionRef)) (\el ->
         let elTrivial = Predicates.isTrivialTerm (Core.bindingTerm el)
         in (Optionals.match (Core.bindingTypeScheme el) (Logic.ifElse (Logic.and (Equality.equal (Arity.typeArity typ) 0) (Logic.not elTrivial)) (Right asFunctionCall) (
           let asFunctionRef =
-                  Logic.ifElse (Logic.not (Sets.null (Variables.freeVariablesInType typ))) (makeSimpleLambda (Arity.typeArity typ) asVariable) asVariable
+                  Logic.ifElse (Logic.not (Sets.isEmpty (Variables.freeVariablesInType typ))) (makeSimpleLambda (Arity.typeArity typ) asVariable) asVariable
           in (Right asFunctionRef))) (\ts -> Logic.ifElse (Logic.and (Logic.and (Equality.equal (Arity.typeArity typ) 0) (Predicates.isComplexBinding tc el)) (Logic.not elTrivial)) (Right asFunctionCall) (
           let asFunctionRef =
-                  Logic.ifElse (Logic.not (Sets.null (Variables.freeVariablesInType typ))) (makeSimpleLambda (Arity.typeArity typ) asVariable) asVariable
+                  Logic.ifElse (Logic.not (Sets.isEmpty (Variables.freeVariablesInType typ))) (makeSimpleLambda (Arity.typeArity typ) asVariable) asVariable
           in (Right asFunctionRef)))))) (Logic.ifElse (Logic.and (Equality.equal (Arity.typeArity typ) 0) (Predicates.isComplexVariable tc name)) (Right asFunctionCall) (
         let asFunctionRef =
-                Logic.ifElse (Logic.not (Sets.null (Variables.freeVariablesInType typ))) (makeSimpleLambda (Arity.typeArity typ) asVariable) asVariable
+                Logic.ifElse (Logic.not (Sets.isEmpty (Variables.freeVariablesInType typ))) (makeSimpleLambda (Arity.typeArity typ) asVariable) asVariable
         in (Right asFunctionRef))))))))
 
 -- | Encode a wrapped type (newtype) to a Python class definition
@@ -1564,7 +1565,7 @@
         Core.TypeLiteral v0 -> case v0 of
           Core.LiteralTypeDecimal -> setMetaUsesDecimal metaWithSubtypes True
           _ -> metaWithSubtypes
-        Core.TypeUnion v0 -> Logic.ifElse (Predicates.isEnumRowType v0) (setMetaUsesEnum metaWithSubtypes True) (Logic.ifElse (Logic.not (Lists.null v0)) (setMetaUsesNode metaWithSubtypes True) metaWithSubtypes)
+        Core.TypeUnion v0 -> Logic.ifElse (Predicates.isEnumRowType v0) (setMetaUsesEnum metaWithSubtypes True) (Logic.ifElse (Logic.not (Lists.isEmpty v0)) (setMetaUsesNode metaWithSubtypes True) metaWithSubtypes)
         Core.TypeForall v0 ->
           let body = Core.forallTypeBody v0
               metaForWrap = digForWrap isTermAnnot metaWithSubtypes body
@@ -1573,7 +1574,7 @@
             _ -> metaForWrap
         Core.TypeRecord v0 ->
           let hasAnnotated = Lists.foldl (\b -> \ft -> Logic.or b (Annotations.hasTypeDescription (Core.fieldTypeType ft))) False v0
-              meta1 = Logic.ifElse (Lists.null v0) metaWithSubtypes (setMetaUsesDataclass metaWithSubtypes True)
+              meta1 = Logic.ifElse (Lists.isEmpty v0) metaWithSubtypes (setMetaUsesDataclass metaWithSubtypes True)
           in (Logic.ifElse hasAnnotated (setMetaUsesAnnotated meta1 True) meta1)
         Core.TypeWrap _ -> Logic.ifElse isTermAnnot metaWithSubtypes (setMetaUsesNode metaWithSubtypes True)
         _ -> metaWithSubtypes
@@ -1624,7 +1625,7 @@
                 Syntax.paramNoDefaultParametersParamNoDefault = pyArgs,
                 Syntax.paramNoDefaultParametersParamWithDefault = [],
                 Syntax.paramNoDefaultParametersStarEtc = Nothing})
-          isTCO = Logic.and (Logic.not (Lists.null args)) (Analysis.isSelfTailRecursive name body)
+          isTCO = Logic.and (Logic.not (Lists.isEmpty args)) (Analysis.isSelfTailRecursive name body)
       in (Eithers.bind (Logic.ifElse isTCO (Eithers.bind (encodeTermMultilineTCO cx env name args body) (\tcoStmts ->
         let trueExpr = Syntax.NamedExpressionSimple (Utils.pyAtomToPyExpression Syntax.AtomTrue)
             whileBody = Utils.indentedBlock Nothing [
@@ -1640,7 +1641,7 @@
         Lists.concat2 prefixes stmts])))) (\block -> Eithers.bind (Optionals.match mcod (Right Nothing) (\cod -> Eithers.bind (encodeType env cod) (\pytyp -> Right (Just pytyp)))) (\mreturnType ->
         let pyTparams =
                 Logic.ifElse useInlineTypeParams (Lists.map (\arg_ -> Utils.pyNameToPyTypeParameter (PythonNames.encodeTypeVariable arg_)) tparams) []
-            isThunk = Lists.null args
+            isThunk = Lists.isEmpty args
             mDecorators = Logic.ifElse isThunk (Just (Syntax.Decorators [
                   lruCacheDecorator])) Nothing
             pyName = PythonNames.encodeName False Util.CaseConventionLowerSnake env name
@@ -1686,12 +1687,12 @@
           result = Lists.foldl addDef start defs
           tvars = PythonEnvironment.pythonModuleMetadataTypeVariables result
           result2 = setMetaUsesCast True (setMetaUsesLruCache True result)
-      in (setMetaUsesTypeVar result2 (Logic.not (Sets.null tvars)))
+      in (setMetaUsesTypeVar result2 (Logic.not (Sets.isEmpty tvars)))
 
 -- | Create Generic[...] argument expression for class definition
 genericArg :: [Core.Name] -> Maybe Syntax.Expression
 genericArg tparamList =
-    Logic.ifElse (Lists.null tparamList) Nothing (Just (Utils.pyPrimaryToPyExpression (Utils.primaryWithExpressionSlices (Syntax.PrimarySimple (Syntax.AtomName (Syntax.Name "Generic"))) (Lists.map (\n -> Syntax.ExpressionSimple (Syntax.Disjunction [
+    Logic.ifElse (Lists.isEmpty tparamList) Nothing (Just (Utils.pyPrimaryToPyExpression (Utils.primaryWithExpressionSlices (Syntax.PrimarySimple (Syntax.AtomName (Syntax.Name "Generic"))) (Lists.map (\n -> Syntax.ExpressionSimple (Syntax.Disjunction [
       Syntax.Conjunction [
         Syntax.InversionSimple (Syntax.Comparison {
           Syntax.comparisonLhs = Syntax.BitwiseOr {
@@ -1784,7 +1785,7 @@
 -- | Check whether a list of definitions contains any type definitions
 isTypeModuleCheck :: [Packaging.Definition] -> Bool
 isTypeModuleCheck defs =
-    Logic.not (Lists.null (Lists.filter (\d -> case d of
+    Logic.not (Lists.isEmpty (Lists.filter (\d -> case d of
       Packaging.DefinitionType _ -> True
       _ -> False) defs))
 
@@ -1846,7 +1847,7 @@
 makeSimpleLambda :: Int -> Syntax.Expression -> Syntax.Expression
 makeSimpleLambda arity lhs =
 
-      let args = Lists.map (\i -> Syntax.Name (Strings.concat2 "x" (Literals.showInt32 i))) (Math.range 1 (Math.add arity 1))
+      let args = Lists.map (\i -> Syntax.Name (Strings.concat2 "x" (Literals.printInt32 i))) (Math.range 1 (Math.add arity 1))
       in (Logic.ifElse (Equality.equal arity 0) lhs (Syntax.ExpressionLambda (Syntax.Lambda {
         Syntax.lambdaParams = Syntax.LambdaParameters {
           Syntax.lambdaParametersSlashNoDefault = Nothing,
@@ -1965,7 +1966,7 @@
                   Optionals.givens (Lists.map (\p ->
                     let modName = Pairs.first p
                         symbols = Optionals.givens (Pairs.second p)
-                    in (Logic.ifElse (Lists.null symbols) Nothing (Just (modName, symbols)))) pairs)
+                    in (Logic.ifElse (Lists.isEmpty symbols) Nothing (Just (modName, symbols)))) pairs)
       in (Lists.map (\p -> standardImportStatement (Pairs.first p) (Pairs.second p)) simplified)
 
 -- | Convert a Hydra module to Python source files
diff --git a/src/main/haskell/Hydra/Python/Environment.hs b/src/main/haskell/Hydra/Python/Environment.hs
--- a/src/main/haskell/Hydra/Python/Environment.hs
+++ b/src/main/haskell/Hydra/Python/Environment.hs
@@ -12,6 +12,7 @@
 import qualified Hydra.Util as Util
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 import qualified Data.Map as M
 import qualified Data.Set as S
 
diff --git a/src/main/haskell/Hydra/Python/Language.hs b/src/main/haskell/Hydra/Python/Language.hs
--- a/src/main/haskell/Hydra/Python/Language.hs
+++ b/src/main/haskell/Hydra/Python/Language.hs
@@ -33,6 +33,7 @@
 import qualified Hydra.Variants as Variants
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 import qualified Data.Set as S
 
 -- | Language constraints for Python 3
diff --git a/src/main/haskell/Hydra/Python/Names.hs b/src/main/haskell/Hydra/Python/Names.hs
--- a/src/main/haskell/Hydra/Python/Names.hs
+++ b/src/main/haskell/Hydra/Python/Names.hs
@@ -43,6 +43,7 @@
 import qualified Hydra.Variants as Variants
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 import qualified Data.Map as M
 import qualified Data.Set as S
 
diff --git a/src/main/haskell/Hydra/Python/Pyproject.hs b/src/main/haskell/Hydra/Python/Pyproject.hs
--- a/src/main/haskell/Hydra/Python/Pyproject.hs
+++ b/src/main/haskell/Hydra/Python/Pyproject.hs
@@ -8,6 +8,7 @@
 import qualified Hydra.Packaging as Packaging
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 
 data PyProjectBuildConfiguration =
   PyProjectBuildConfiguration {
diff --git a/src/main/haskell/Hydra/Python/Serde.hs b/src/main/haskell/Hydra/Python/Serde.hs
--- a/src/main/haskell/Hydra/Python/Serde.hs
+++ b/src/main/haskell/Hydra/Python/Serde.hs
@@ -41,6 +41,7 @@
 import qualified Hydra.Variants as Variants
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 
 -- | Serialize an annotated RHS
 annotatedRhsToExpr :: Syntax.AnnotatedRhs -> Ast.Expr
@@ -275,7 +276,7 @@
 
       let lhs = Syntax.comparisonLhs cmp
           rhs = Syntax.comparisonRhs cmp
-      in (Logic.ifElse (Lists.null rhs) (bitwiseOrToExpr lhs) (Serialization.spaceSep (Lists.cons (bitwiseOrToExpr lhs) (Lists.map compareOpBitwiseOrPairToExpr rhs))))
+      in (Logic.ifElse (Lists.isEmpty rhs) (bitwiseOrToExpr lhs) (Serialization.spaceSep (Lists.cons (bitwiseOrToExpr lhs) (Lists.map compareOpBitwiseOrPairToExpr rhs))))
 
 -- | Serialize a compound (multi-line) Python statement
 compoundStatementToExpr :: Syntax.CompoundStatement -> Ast.Expr
@@ -400,7 +401,7 @@
           block = Syntax.functionDefRawBlock fdr
           asyncKw = Logic.ifElse async_ (Just (Serialization.cst "async")) Nothing
           tparamPart =
-                  Logic.ifElse (Lists.null tparams) Nothing (Just (Serialization.bracketList Serialization.inlineStyle (Lists.map typeParameterToExpr tparams)))
+                  Logic.ifElse (Lists.isEmpty tparams) Nothing (Just (Serialization.bracketList Serialization.inlineStyle (Lists.map typeParameterToExpr tparams)))
           paramPart = Optionals.map parametersToExpr params
           retPart =
                   Optionals.map (\t -> Serialization.spaceSep [
@@ -641,9 +642,9 @@
 numberToExpr :: Syntax.Number -> Ast.Expr
 numberToExpr num =
     case num of
-      Syntax.NumberFloat v0 -> Serialization.cst (pythonFloatLiteralText (Literals.showFloat64 v0))
-      Syntax.NumberImaginary v0 -> Serialization.cst (Strings.concat2 (pythonFloatLiteralText (Literals.showFloat64 v0)) "j")
-      Syntax.NumberInteger v0 -> Serialization.cst (Literals.showBigint v0)
+      Syntax.NumberFloat v0 -> Serialization.cst (pythonFloatLiteralText (Literals.printFloat64 v0))
+      Syntax.NumberImaginary v0 -> Serialization.cst (Strings.concat2 (pythonFloatLiteralText (Literals.printFloat64 v0)) "j")
+      Syntax.NumberInteger v0 -> Serialization.cst (Literals.printBigint v0)
 
 -- | Serialize an or pattern
 orPatternToExpr :: Syntax.OrPattern -> Ast.Expr
@@ -1013,7 +1014,7 @@
           alias =
                   Serialization.noSep (Optionals.givens [
                     Just (nameToExpr name),
-                    (Logic.ifElse (Lists.null tparams) Nothing (Just (Serialization.bracketList Serialization.inlineStyle (Lists.map typeParameterToExpr tparams))))])
+                    (Logic.ifElse (Lists.isEmpty tparams) Nothing (Just (Serialization.bracketList Serialization.inlineStyle (Lists.map typeParameterToExpr tparams))))])
       in (Serialization.spaceSep [
         Serialization.cst "type",
         alias,
diff --git a/src/main/haskell/Hydra/Python/Syntax.hs b/src/main/haskell/Hydra/Python/Syntax.hs
--- a/src/main/haskell/Hydra/Python/Syntax.hs
+++ b/src/main/haskell/Hydra/Python/Syntax.hs
@@ -8,6 +8,7 @@
 import qualified Hydra.Core as Core
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 
 data AnnotatedStatement =
   AnnotatedStatement {
diff --git a/src/main/haskell/Hydra/Python/Testing.hs b/src/main/haskell/Hydra/Python/Testing.hs
--- a/src/main/haskell/Hydra/Python/Testing.hs
+++ b/src/main/haskell/Hydra/Python/Testing.hs
@@ -42,6 +42,7 @@
 import qualified Hydra.Variants as Variants
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 
 -- | Build the complete Python test module content
 buildPythonTestModule :: t0 -> Testing.TestGroup -> String -> String
@@ -74,7 +75,7 @@
         Testing.TestCaseUniversal v0 ->
           let actual_ = Testing.universalTestCaseActual v0 ()
               expected_ = Testing.universalTestCaseExpected v0 ()
-              fullName = Logic.ifElse (Lists.null groupPath) name_ (Strings.join "__" (Lists.concat2 groupPath [
+              fullName = Logic.ifElse (Lists.isEmpty groupPath) name_ (Strings.join "__" (Lists.concat2 groupPath [
                     name_]))
               formattedName = formatPythonTestName fullName
           in (Right [
diff --git a/src/main/haskell/Hydra/Python/Utils.hs b/src/main/haskell/Hydra/Python/Utils.hs
--- a/src/main/haskell/Hydra/Python/Utils.hs
+++ b/src/main/haskell/Hydra/Python/Utils.hs
@@ -42,6 +42,7 @@
 import qualified Hydra.Variants as Variants
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 import qualified Data.Set as S
 
 -- | Annotate an expression with an optional comment using Annotated[]
@@ -100,7 +101,7 @@
           sumRhs = Syntax.sumRhs shiftRhs
           termLhs = Syntax.termLhs sumRhs
           termRhs = Syntax.termRhs sumRhs
-      in (Logic.ifElse (Logic.not (Lists.null rhs)) Nothing (Logic.ifElse (Optionals.isGiven orLhs) Nothing (Logic.ifElse (Optionals.isGiven xorLhs) Nothing (Logic.ifElse (Optionals.isGiven andLhs) Nothing (Logic.ifElse (Optionals.isGiven shiftLhs) Nothing (Logic.ifElse (Optionals.isGiven sumLhs) Nothing (Logic.ifElse (Optionals.isGiven termLhs) Nothing (case termRhs of
+      in (Logic.ifElse (Logic.not (Lists.isEmpty rhs)) Nothing (Logic.ifElse (Optionals.isGiven orLhs) Nothing (Logic.ifElse (Optionals.isGiven xorLhs) Nothing (Logic.ifElse (Optionals.isGiven andLhs) Nothing (Logic.ifElse (Optionals.isGiven shiftLhs) Nothing (Logic.ifElse (Optionals.isGiven sumLhs) Nothing (Logic.ifElse (Optionals.isGiven termLhs) Nothing (case termRhs of
         Syntax.FactorSimple v0 -> decodePyPowerToPyPrimary v0
         _ -> Nothing))))))))
 
@@ -191,8 +192,8 @@
 
       let commentGroup = Optionals.match mcomment [] (\s -> [
             commentStatement s])
-          groups = Lists.filter (\g -> Logic.not (Lists.null g)) (Lists.cons commentGroup stmts)
-      in (Logic.ifElse (Lists.null groups) (Syntax.BlockIndented [
+          groups = Lists.filter (\g -> Logic.not (Lists.isEmpty g)) (Lists.cons commentGroup stmts)
+      in (Logic.ifElse (Lists.isEmpty groups) (Syntax.BlockIndented [
         [
           Syntax.StatementSimple [
             pyExpressionToPySimpleStatement (pyAtomToPyExpression Syntax.AtomEllipsis)]]]) (Syntax.BlockIndented groups))
@@ -215,7 +216,7 @@
       let build =
               \prev -> \ps -> Optionals.match (Lists.uncons ps) (Syntax.BitwiseOr {
                 Syntax.bitwiseOrLhs = prev,
-                Syntax.bitwiseOrRhs = (pyPrimaryToPyBitwiseXor (Syntax.PrimarySimple Syntax.AtomEllipsis))}) (\p -> Logic.ifElse (Lists.null (Pairs.second p)) (Syntax.BitwiseOr {
+                Syntax.bitwiseOrRhs = (pyPrimaryToPyBitwiseXor (Syntax.PrimarySimple Syntax.AtomEllipsis))}) (\p -> Logic.ifElse (Lists.isEmpty (Pairs.second p)) (Syntax.BitwiseOr {
                 Syntax.bitwiseOrLhs = prev,
                 Syntax.bitwiseOrRhs = (pyPrimaryToPyBitwiseXor (Pairs.first p))}) (build (Just (Syntax.BitwiseOr {
                 Syntax.bitwiseOrLhs = prev,
@@ -629,7 +630,7 @@
                         getItemMethod]])})
           docStmt = pyExpressionToPyStatement (tripleQuotedString docString)
           bodyGroups =
-                  Logic.ifElse (Lists.null extraStmts) (
+                  Logic.ifElse (Lists.isEmpty extraStmts) (
                     let passStmt = pySimpleStatementToPyStatement Syntax.SimpleStatementPass
                     in [
                       [
