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-haskell.cabal b/hydra-haskell.cabal
--- a/hydra-haskell.cabal
+++ b/hydra-haskell.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-haskell
-version:        0.17.5
+version:        0.17.6
 synopsis:       Hydra's Haskell coder: emit Haskell 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". This package is Hydra's Haskell coder: it translates Hydra modules into Haskell source. The top-level entry point is moduleToHaskell (and moduleToHaskellModule for the structured AST). It builds on hydra-kernel.
 category:       Data
@@ -44,6 +44,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/Haskell/Environment.hs b/src/main/haskell/Hydra/Dsl/Haskell/Environment.hs
--- a/src/main/haskell/Hydra/Dsl/Haskell/Environment.hs
+++ b/src/main/haskell/Hydra/Dsl/Haskell/Environment.hs
@@ -9,10 +9,11 @@
 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 constructor for hydra.haskell.environment.HaskellModuleMetadata
-haskellModuleMetadata :: Typed.TypedTerm Bool -> Typed.TypedTerm Bool -> Typed.TypedTerm Bool -> Typed.TypedTerm Bool -> Typed.TypedTerm Environment.HaskellModuleMetadata
-haskellModuleMetadata usesByteString usesInt usesMap usesSet =
+haskellModuleMetadata :: Typed.TypedTerm Bool -> Typed.TypedTerm Bool -> Typed.TypedTerm Bool -> Typed.TypedTerm Bool -> Typed.TypedTerm Bool -> Typed.TypedTerm Environment.HaskellModuleMetadata
+haskellModuleMetadata usesByteString usesInt usesMap usesSet usesVoid =
     Typed.TypedTerm (Core.TermRecord (Core.Record {
       Core.recordTypeName = (Core.Name "hydra.haskell.environment.HaskellModuleMetadata"),
       Core.recordFields = [
@@ -27,7 +28,10 @@
           Core.fieldTerm = (Typed.unTypedTerm usesMap)},
         Core.Field {
           Core.fieldName = (Core.Name "usesSet"),
-          Core.fieldTerm = (Typed.unTypedTerm usesSet)}]}))
+          Core.fieldTerm = (Typed.unTypedTerm usesSet)},
+        Core.Field {
+          Core.fieldName = (Core.Name "usesVoid"),
+          Core.fieldTerm = (Typed.unTypedTerm usesVoid)}]}))
 
 -- | DSL name token for hydra.haskell.environment.HaskellModuleMetadata
 haskellModuleMetadataHaskellModuleMetadata :: Typed.TypedName Environment.HaskellModuleMetadata
@@ -69,6 +73,15 @@
         Core.projectionFieldName = (Core.Name "usesSet")})),
       Core.applicationArgument = (Typed.unTypedTerm x)}))
 
+-- | DSL accessor for the usesVoid field of hydra.haskell.environment.HaskellModuleMetadata
+haskellModuleMetadataUsesVoid :: Typed.TypedTerm Environment.HaskellModuleMetadata -> Typed.TypedTerm Bool
+haskellModuleMetadataUsesVoid x =
+    Typed.TypedTerm (Core.TermApplication (Core.Application {
+      Core.applicationFunction = (Core.TermProject (Core.Projection {
+        Core.projectionTypeName = (Core.Name "hydra.haskell.environment.HaskellModuleMetadata"),
+        Core.projectionFieldName = (Core.Name "usesVoid")})),
+      Core.applicationArgument = (Typed.unTypedTerm x)}))
+
 -- | DSL updater for the usesByteString field of hydra.haskell.environment.HaskellModuleMetadata
 haskellModuleMetadataWithUsesByteString :: Typed.TypedTerm Environment.HaskellModuleMetadata -> Typed.TypedTerm Bool -> Typed.TypedTerm Environment.HaskellModuleMetadata
 haskellModuleMetadataWithUsesByteString original newVal =
@@ -98,6 +111,13 @@
             Core.applicationFunction = (Core.TermProject (Core.Projection {
               Core.projectionTypeName = (Core.Name "hydra.haskell.environment.HaskellModuleMetadata"),
               Core.projectionFieldName = (Core.Name "usesSet")})),
+            Core.applicationArgument = (Typed.unTypedTerm original)}))},
+        Core.Field {
+          Core.fieldName = (Core.Name "usesVoid"),
+          Core.fieldTerm = (Core.TermApplication (Core.Application {
+            Core.applicationFunction = (Core.TermProject (Core.Projection {
+              Core.projectionTypeName = (Core.Name "hydra.haskell.environment.HaskellModuleMetadata"),
+              Core.projectionFieldName = (Core.Name "usesVoid")})),
             Core.applicationArgument = (Typed.unTypedTerm original)}))}]}))
 
 -- | DSL updater for the usesInt field of hydra.haskell.environment.HaskellModuleMetadata
@@ -129,6 +149,13 @@
             Core.applicationFunction = (Core.TermProject (Core.Projection {
               Core.projectionTypeName = (Core.Name "hydra.haskell.environment.HaskellModuleMetadata"),
               Core.projectionFieldName = (Core.Name "usesSet")})),
+            Core.applicationArgument = (Typed.unTypedTerm original)}))},
+        Core.Field {
+          Core.fieldName = (Core.Name "usesVoid"),
+          Core.fieldTerm = (Core.TermApplication (Core.Application {
+            Core.applicationFunction = (Core.TermProject (Core.Projection {
+              Core.projectionTypeName = (Core.Name "hydra.haskell.environment.HaskellModuleMetadata"),
+              Core.projectionFieldName = (Core.Name "usesVoid")})),
             Core.applicationArgument = (Typed.unTypedTerm original)}))}]}))
 
 -- | DSL updater for the usesMap field of hydra.haskell.environment.HaskellModuleMetadata
@@ -160,6 +187,13 @@
             Core.applicationFunction = (Core.TermProject (Core.Projection {
               Core.projectionTypeName = (Core.Name "hydra.haskell.environment.HaskellModuleMetadata"),
               Core.projectionFieldName = (Core.Name "usesSet")})),
+            Core.applicationArgument = (Typed.unTypedTerm original)}))},
+        Core.Field {
+          Core.fieldName = (Core.Name "usesVoid"),
+          Core.fieldTerm = (Core.TermApplication (Core.Application {
+            Core.applicationFunction = (Core.TermProject (Core.Projection {
+              Core.projectionTypeName = (Core.Name "hydra.haskell.environment.HaskellModuleMetadata"),
+              Core.projectionFieldName = (Core.Name "usesVoid")})),
             Core.applicationArgument = (Typed.unTypedTerm original)}))}]}))
 
 -- | DSL updater for the usesSet field of hydra.haskell.environment.HaskellModuleMetadata
@@ -191,4 +225,49 @@
             Core.applicationArgument = (Typed.unTypedTerm original)}))},
         Core.Field {
           Core.fieldName = (Core.Name "usesSet"),
+          Core.fieldTerm = (Typed.unTypedTerm newVal)},
+        Core.Field {
+          Core.fieldName = (Core.Name "usesVoid"),
+          Core.fieldTerm = (Core.TermApplication (Core.Application {
+            Core.applicationFunction = (Core.TermProject (Core.Projection {
+              Core.projectionTypeName = (Core.Name "hydra.haskell.environment.HaskellModuleMetadata"),
+              Core.projectionFieldName = (Core.Name "usesVoid")})),
+            Core.applicationArgument = (Typed.unTypedTerm original)}))}]}))
+
+-- | DSL updater for the usesVoid field of hydra.haskell.environment.HaskellModuleMetadata
+haskellModuleMetadataWithUsesVoid :: Typed.TypedTerm Environment.HaskellModuleMetadata -> Typed.TypedTerm Bool -> Typed.TypedTerm Environment.HaskellModuleMetadata
+haskellModuleMetadataWithUsesVoid original newVal =
+    Typed.TypedTerm (Core.TermRecord (Core.Record {
+      Core.recordTypeName = (Core.Name "hydra.haskell.environment.HaskellModuleMetadata"),
+      Core.recordFields = [
+        Core.Field {
+          Core.fieldName = (Core.Name "usesByteString"),
+          Core.fieldTerm = (Core.TermApplication (Core.Application {
+            Core.applicationFunction = (Core.TermProject (Core.Projection {
+              Core.projectionTypeName = (Core.Name "hydra.haskell.environment.HaskellModuleMetadata"),
+              Core.projectionFieldName = (Core.Name "usesByteString")})),
+            Core.applicationArgument = (Typed.unTypedTerm original)}))},
+        Core.Field {
+          Core.fieldName = (Core.Name "usesInt"),
+          Core.fieldTerm = (Core.TermApplication (Core.Application {
+            Core.applicationFunction = (Core.TermProject (Core.Projection {
+              Core.projectionTypeName = (Core.Name "hydra.haskell.environment.HaskellModuleMetadata"),
+              Core.projectionFieldName = (Core.Name "usesInt")})),
+            Core.applicationArgument = (Typed.unTypedTerm original)}))},
+        Core.Field {
+          Core.fieldName = (Core.Name "usesMap"),
+          Core.fieldTerm = (Core.TermApplication (Core.Application {
+            Core.applicationFunction = (Core.TermProject (Core.Projection {
+              Core.projectionTypeName = (Core.Name "hydra.haskell.environment.HaskellModuleMetadata"),
+              Core.projectionFieldName = (Core.Name "usesMap")})),
+            Core.applicationArgument = (Typed.unTypedTerm original)}))},
+        Core.Field {
+          Core.fieldName = (Core.Name "usesSet"),
+          Core.fieldTerm = (Core.TermApplication (Core.Application {
+            Core.applicationFunction = (Core.TermProject (Core.Projection {
+              Core.projectionTypeName = (Core.Name "hydra.haskell.environment.HaskellModuleMetadata"),
+              Core.projectionFieldName = (Core.Name "usesSet")})),
+            Core.applicationArgument = (Typed.unTypedTerm original)}))},
+        Core.Field {
+          Core.fieldName = (Core.Name "usesVoid"),
           Core.fieldTerm = (Typed.unTypedTerm newVal)}]}))
diff --git a/src/main/haskell/Hydra/Dsl/Haskell/Syntax.hs b/src/main/haskell/Hydra/Dsl/Haskell/Syntax.hs
--- a/src/main/haskell/Hydra/Dsl/Haskell/Syntax.hs
+++ b/src/main/haskell/Hydra/Dsl/Haskell/Syntax.hs
@@ -10,6 +10,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 constructor for hydra.haskell.syntax.Alternative
 alternative :: Typed.TypedTerm Syntax.Pattern -> Typed.TypedTerm Syntax.CaseRhs -> Typed.TypedTerm (Maybe Syntax.LocalBindings) -> Typed.TypedTerm Syntax.Alternative
diff --git a/src/main/haskell/Hydra/Haskell/Coder.hs b/src/main/haskell/Hydra/Haskell/Coder.hs
--- a/src/main/haskell/Hydra/Haskell/Coder.hs
+++ b/src/main/haskell/Hydra/Haskell/Coder.hs
@@ -69,6 +69,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
 
@@ -141,7 +142,7 @@
                                   malias = Pairs.second (Pairs.first triple)
                                   hidden = Pairs.second triple
                                   spec =
-                                          Logic.ifElse (Lists.null hidden) Nothing (Just (Syntax.ImportSpecHiding (Lists.map (\n -> Syntax.NamedImportExport {
+                                          Logic.ifElse (Lists.isEmpty hidden) Nothing (Just (Syntax.ImportSpecHiding (Lists.map (\n -> Syntax.NamedImportExport {
                                             Syntax.namedImportExportModifier = Nothing,
                                             Syntax.namedImportExportName = (Utils.simpleName n),
                                             Syntax.namedImportExportSubspec = Nothing}) hidden)))
@@ -166,7 +167,8 @@
                             "sum",
                             "unlines"])],
                       [
-                        (("Data.Scientific", (Just "Sci")), [])],
+                        (("Data.Scientific", (Just "Sci")), []),
+                        (("Data.Void", Nothing), [])],
                       (condImport (Environment.haskellModuleMetadataUsesByteString meta) (("Data.ByteString", (Just "B")), [])),
                       (condImport (Environment.haskellModuleMetadataUsesInt meta) (("Data.Int", (Just "I")), [])),
                       (condImport (Environment.haskellModuleMetadataUsesMap meta) (("Data.Map", (Just "M")), [])),
@@ -191,7 +193,8 @@
       Environment.haskellModuleMetadataUsesByteString = False,
       Environment.haskellModuleMetadataUsesInt = False,
       Environment.haskellModuleMetadataUsesMap = False,
-      Environment.haskellModuleMetadataUsesSet = False}
+      Environment.haskellModuleMetadataUsesSet = False,
+      Environment.haskellModuleMetadataUsesVoid = False}
 
 -- | Encode a Hydra case statement as a Haskell case expression with a given scrutinee
 encodeCaseExpression :: Int -> Util.ModuleNames Syntax.ModuleName -> Core.CaseStatement -> Syntax.Expression -> t0 -> Graph.Graph -> Either Errors.Error Syntax.Expression
@@ -204,7 +207,7 @@
                   \fieldMap -> \field ->
                     let fn = Core.caseAlternativeName field
                         fun_ = Core.caseAlternativeHandler field
-                        v0 = Strings.concat2 "v" (Literals.showInt32 depth)
+                        v0 = Strings.concat2 "v" (Literals.printInt32 depth)
                         raw =
                                 Core.TermApplication (Core.Application {
                                   Core.applicationFunction = fun_,
@@ -256,7 +259,7 @@
     case l of
       Core.LiteralBinary v0 -> Right (Utils.hsapp (Utils.hsvar "Literals.base64ToBinary") (Utils.hslit (Syntax.LiteralString (Literals.binaryToBase64 v0))))
       Core.LiteralBoolean v0 -> Right (Utils.hsvar (Logic.ifElse v0 "True" "False"))
-      Core.LiteralDecimal v0 -> Right (Utils.hsapp (Utils.hsvar "Literals.stringToDecimal") (Utils.hslit (Syntax.LiteralString (Literals.showDecimal v0))))
+      Core.LiteralDecimal v0 -> Right (Utils.hsapp (Utils.hsvar "Literals.stringToDecimal") (Utils.hslit (Syntax.LiteralString (Literals.printDecimal v0))))
       Core.LiteralFloat v0 -> case v0 of
         Core.FloatValueFloat32 v1 -> Right (Utils.hslit (Syntax.LiteralFloat v1))
         Core.FloatValueFloat64 v1 -> Right (Utils.hslit (Syntax.LiteralDouble v1))
@@ -345,7 +348,7 @@
             Syntax.letExpressionInner = hinner})))))
         Core.TermList v0 -> Eithers.bind (Eithers.mapList encode v0) (\helems -> Right (Syntax.ExpressionList helems))
         Core.TermLiteral v0 -> encodeLiteral v0 cx
-        Core.TermMap v0 -> Logic.ifElse (Maps.null v0) (Right (Utils.hsvar "M.empty")) (nonemptyMap v0)
+        Core.TermMap v0 -> Logic.ifElse (Maps.isEmpty v0) (Right (Utils.hsvar "M.empty")) (nonemptyMap v0)
         Core.TermOptional v0 -> Optionals.match v0 (Right (Utils.hsvar "Nothing")) (\t -> Eithers.bind (encode t) (\ht -> Right (Utils.hsapp (Utils.hsvar "Just") ht)))
         Core.TermPair v0 -> Eithers.bind (encode (Pairs.first v0)) (\f -> Eithers.bind (encode (Pairs.second v0)) (\s -> Right (Syntax.ExpressionTuple [
           f,
@@ -365,7 +368,7 @@
           in (Eithers.bind (Eithers.mapList toFieldUpdate fields) (\updates -> Right (Syntax.ExpressionConstructRecord (Syntax.RecordExpression {
             Syntax.recordExpressionName = typeName,
             Syntax.recordExpressionFields = updates}))))
-        Core.TermSet v0 -> Logic.ifElse (Sets.null v0) (Right (Utils.hsvar "S.empty")) (nonemptySet v0)
+        Core.TermSet v0 -> Logic.ifElse (Sets.isEmpty v0) (Right (Utils.hsvar "S.empty")) (nonemptySet v0)
         Core.TermTypeLambda v0 ->
           let term1 = Core.typeLambdaBody v0
           in (encode term1)
@@ -500,7 +503,7 @@
                         clsSet = Pairs.second mapEntry
                         toPair = \c -> (name, c)
                     in (Lists.map toPair (Sets.toList clsSet))
-      in (Eithers.bind (adaptTypeToHaskellAndEncode namespaces typ cx g) (\htyp -> Logic.ifElse (Lists.null assertPairs) (Right htyp) (
+      in (Eithers.bind (adaptTypeToHaskellAndEncode namespaces typ cx g) (\htyp -> Logic.ifElse (Lists.isEmpty assertPairs) (Right htyp) (
         let encoded = Lists.map encodeAssertion assertPairs
             hassert =
                     Logic.ifElse (Equality.equal (Lists.length encoded) 1) (Optionals.withDefault (Syntax.ConstraintTuple encoded) (Lists.head encoded)) (Syntax.ConstraintTuple encoded)
@@ -535,6 +538,7 @@
         _ -> meta
       Core.TypeMap _ -> setMetaUsesMap True meta
       Core.TypeSet _ -> setMetaUsesSet True meta
+      Core.TypeVoid -> setMetaUsesVoid True meta
       _ -> meta
 
 -- | Find type variables that require an Ord constraint (used in maps or sets)
@@ -630,7 +634,8 @@
       Environment.haskellModuleMetadataUsesByteString = b,
       Environment.haskellModuleMetadataUsesInt = (Environment.haskellModuleMetadataUsesInt m),
       Environment.haskellModuleMetadataUsesMap = (Environment.haskellModuleMetadataUsesMap m),
-      Environment.haskellModuleMetadataUsesSet = (Environment.haskellModuleMetadataUsesSet m)}
+      Environment.haskellModuleMetadataUsesSet = (Environment.haskellModuleMetadataUsesSet m),
+      Environment.haskellModuleMetadataUsesVoid = (Environment.haskellModuleMetadataUsesVoid m)}
 
 -- | Set the usesInt flag in Haskell module metadata
 setMetaUsesInt :: Bool -> Environment.HaskellModuleMetadata -> Environment.HaskellModuleMetadata
@@ -639,7 +644,8 @@
       Environment.haskellModuleMetadataUsesByteString = (Environment.haskellModuleMetadataUsesByteString m),
       Environment.haskellModuleMetadataUsesInt = b,
       Environment.haskellModuleMetadataUsesMap = (Environment.haskellModuleMetadataUsesMap m),
-      Environment.haskellModuleMetadataUsesSet = (Environment.haskellModuleMetadataUsesSet m)}
+      Environment.haskellModuleMetadataUsesSet = (Environment.haskellModuleMetadataUsesSet m),
+      Environment.haskellModuleMetadataUsesVoid = (Environment.haskellModuleMetadataUsesVoid m)}
 
 -- | Set the usesMap flag in Haskell module metadata
 setMetaUsesMap :: Bool -> Environment.HaskellModuleMetadata -> Environment.HaskellModuleMetadata
@@ -648,7 +654,8 @@
       Environment.haskellModuleMetadataUsesByteString = (Environment.haskellModuleMetadataUsesByteString m),
       Environment.haskellModuleMetadataUsesInt = (Environment.haskellModuleMetadataUsesInt m),
       Environment.haskellModuleMetadataUsesMap = b,
-      Environment.haskellModuleMetadataUsesSet = (Environment.haskellModuleMetadataUsesSet m)}
+      Environment.haskellModuleMetadataUsesSet = (Environment.haskellModuleMetadataUsesSet m),
+      Environment.haskellModuleMetadataUsesVoid = (Environment.haskellModuleMetadataUsesVoid m)}
 
 -- | Set the usesSet flag in Haskell module metadata
 setMetaUsesSet :: Bool -> Environment.HaskellModuleMetadata -> Environment.HaskellModuleMetadata
@@ -657,8 +664,19 @@
       Environment.haskellModuleMetadataUsesByteString = (Environment.haskellModuleMetadataUsesByteString m),
       Environment.haskellModuleMetadataUsesInt = (Environment.haskellModuleMetadataUsesInt m),
       Environment.haskellModuleMetadataUsesMap = (Environment.haskellModuleMetadataUsesMap m),
-      Environment.haskellModuleMetadataUsesSet = b}
+      Environment.haskellModuleMetadataUsesSet = b,
+      Environment.haskellModuleMetadataUsesVoid = (Environment.haskellModuleMetadataUsesVoid m)}
 
+-- | Set the usesVoid flag in Haskell module metadata
+setMetaUsesVoid :: Bool -> Environment.HaskellModuleMetadata -> Environment.HaskellModuleMetadata
+setMetaUsesVoid b m =
+    Environment.HaskellModuleMetadata {
+      Environment.haskellModuleMetadataUsesByteString = (Environment.haskellModuleMetadataUsesByteString m),
+      Environment.haskellModuleMetadataUsesInt = (Environment.haskellModuleMetadataUsesInt m),
+      Environment.haskellModuleMetadataUsesMap = (Environment.haskellModuleMetadataUsesMap m),
+      Environment.haskellModuleMetadataUsesSet = (Environment.haskellModuleMetadataUsesSet m),
+      Environment.haskellModuleMetadataUsesVoid = b}
+
 -- | Convert a Hydra term definition to a Haskell declaration with comments
 toDataDeclaration :: Util.ModuleNames Syntax.ModuleName -> Packaging.TermDefinition -> t0 -> Graph.Graph -> Either Errors.Error Syntax.Declaration
 toDataDeclaration namespaces def cx g =
@@ -706,7 +724,7 @@
                         in (toDecl comments hname_ env (Just (Syntax.LocalBindings allBindings)))))
                     _ -> Eithers.bind (encodeTerm 0 namespaces term_ cx g) (\hterm ->
                       let vb = Utils.simpleValueBinding hname_ hterm bindings
-                          schemeConstraints = Optionals.match typ Nothing (\ts -> Core.typeSchemeConstraints ts)
+                          schemeConstraints = Optionals.match typ Maps.empty (\ts -> Core.typeSchemeConstraints ts)
                           schemeClasses = typeSchemeConstraintsToClassMap schemeConstraints
                       in (Eithers.bind (Annotations.getTypeClasses cx g (Strip.removeTypesFromTerm term)) (\explicitClasses ->
                         let combinedClasses = Maps.union schemeClasses explicitClasses
@@ -890,13 +908,13 @@
         in (Right decl)))
 
 -- | Project type scheme constraints to a map of type variables to typeclass names
-typeSchemeConstraintsToClassMap :: Ord t0 => (Maybe (M.Map t0 Core.TypeVariableConstraints) -> M.Map t0 (S.Set Core.Name))
-typeSchemeConstraintsToClassMap maybeConstraints =
+typeSchemeConstraintsToClassMap :: Ord t0 => (M.Map t0 Core.TypeVariableConstraints -> M.Map t0 (S.Set Core.Name))
+typeSchemeConstraintsToClassMap constraints =
 
       let constraintToName =
               \tcc -> case tcc of
                 Core.TypeClassConstraintSimple v0 -> Just v0
-      in (Optionals.match maybeConstraints Maps.empty (\constraints -> Maps.map (\meta -> Sets.fromList (Optionals.givens (Lists.map constraintToName (Core.typeVariableConstraintsClasses meta)))) constraints))
+      in (Maps.map (\meta -> Sets.fromList (Optionals.givens (Lists.map constraintToName (Sets.toList (Core.typeVariableConstraintsClasses meta))))) constraints)
 
 -- | Whether to use the Hydra core import in generated modules
 useCoreImport :: Bool
diff --git a/src/main/haskell/Hydra/Haskell/Environment.hs b/src/main/haskell/Hydra/Haskell/Environment.hs
--- a/src/main/haskell/Hydra/Haskell/Environment.hs
+++ b/src/main/haskell/Hydra/Haskell/Environment.hs
@@ -7,6 +7,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
 
 -- | Metadata used to determine which standard imports are needed in a generated Haskell module
 data HaskellModuleMetadata =
@@ -18,7 +19,9 @@
     -- | Whether the module uses Data.Map (M.Map, M.fromList, M.empty)
     haskellModuleMetadataUsesMap :: Bool,
     -- | Whether the module uses Data.Set (S.Set, S.fromList, S.empty)
-    haskellModuleMetadataUsesSet :: Bool}
+    haskellModuleMetadataUsesSet :: Bool,
+    -- | Whether the module uses Data.Void (Void)
+    haskellModuleMetadataUsesVoid :: Bool}
   deriving (Eq, Ord, Read, Show)
 
 _HaskellModuleMetadata = Core.Name "hydra.haskell.environment.HaskellModuleMetadata"
@@ -30,3 +33,5 @@
 _HaskellModuleMetadata_usesMap = Core.Name "usesMap"
 
 _HaskellModuleMetadata_usesSet = Core.Name "usesSet"
+
+_HaskellModuleMetadata_usesVoid = Core.Name "usesVoid"
diff --git a/src/main/haskell/Hydra/Haskell/Language.hs b/src/main/haskell/Hydra/Haskell/Language.hs
--- a/src/main/haskell/Hydra/Haskell/Language.hs
+++ b/src/main/haskell/Hydra/Haskell/Language.hs
@@ -37,6 +37,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 Haskell
diff --git a/src/main/haskell/Hydra/Haskell/Operators.hs b/src/main/haskell/Hydra/Haskell/Operators.hs
--- a/src/main/haskell/Hydra/Haskell/Operators.hs
+++ b/src/main/haskell/Hydra/Haskell/Operators.hs
@@ -37,6 +37,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
 
 -- | Logical AND operator (&&)
 andOp :: Ast.Op
diff --git a/src/main/haskell/Hydra/Haskell/Serde.hs b/src/main/haskell/Hydra/Haskell/Serde.hs
--- a/src/main/haskell/Hydra/Haskell/Serde.hs
+++ b/src/main/haskell/Hydra/Haskell/Serde.hs
@@ -53,6 +53,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
 
 -- | Convert a pattern-matching alternative to an AST expression
 alternativeToExpr :: Syntax.Alternative -> Ast.Expr
@@ -178,7 +179,7 @@
                           derivCat = Lists.concat (Lists.map Syntax.unDerivingClause deriv)
                           constructors = Serialization.orSep Serialization.halfBlockStyle (Lists.map constructorToExpr cons)
                           derivingClause =
-                                  Logic.ifElse (Lists.null derivCat) [] [
+                                  Logic.ifElse (Lists.isEmpty derivCat) [] [
                                     Serialization.spaceSep (Lists.cons (Serialization.cst "deriving") [
                                       Serialization.parenList False (Lists.map nameToExpr derivCat)])]
                           mainParts =
@@ -323,11 +324,11 @@
                     let raw = showFn v
                     in (Logic.ifElse (Equality.equal raw "NaN") "(0/0)" (Logic.ifElse (Equality.equal raw "Infinity") "(1/0)" (Logic.ifElse (Equality.equal raw "-Infinity") "(-(1/0))" (parensIfNeg (Equality.equal (Optionals.withDefault 0 (Strings.charAt 0 raw)) 45) raw))))
       in (Serialization.cst (case lit of
-        Syntax.LiteralChar v0 -> Literals.printString (Literals.showUint16 v0)
-        Syntax.LiteralDouble v0 -> showFloat (\v -> Literals.showFloat64 v) v0
-        Syntax.LiteralFloat v0 -> showFloat (\v -> Literals.showFloat32 v) v0
-        Syntax.LiteralInt v0 -> parensIfNeg (Ordering.lt v0 0) (Literals.showInt32 v0)
-        Syntax.LiteralInteger v0 -> parensIfNeg (Ordering.lt v0 0) (Literals.showBigint v0)
+        Syntax.LiteralChar v0 -> Literals.printString (Literals.printUint16 v0)
+        Syntax.LiteralDouble v0 -> showFloat (\v -> Literals.printFloat64 v) v0
+        Syntax.LiteralFloat v0 -> showFloat (\v -> Literals.printFloat32 v) v0
+        Syntax.LiteralInt v0 -> parensIfNeg (Ordering.lt v0 0) (Literals.printInt32 v0)
+        Syntax.LiteralInteger v0 -> parensIfNeg (Ordering.lt v0 0) (Literals.printBigint v0)
         Syntax.LiteralString v0 -> Literals.printString v0))
 
 -- | Convert a local binding to an AST expression
@@ -362,7 +363,7 @@
           headerLine = Optionals.match mh [] (\h -> [
                 moduleHeadToExpr h])
           declLines = Lists.map declarationToExpr decls
-          importLines = Logic.ifElse (Lists.null imports) [] [
+          importLines = Logic.ifElse (Lists.isEmpty imports) [] [
                 Serialization.newlineSep (Lists.map importToExpr imports)]
       in (Serialization.doubleNewlineSep (Lists.concat [
         warning,
diff --git a/src/main/haskell/Hydra/Haskell/Syntax.hs b/src/main/haskell/Hydra/Haskell/Syntax.hs
--- a/src/main/haskell/Hydra/Haskell/Syntax.hs
+++ b/src/main/haskell/Hydra/Haskell/Syntax.hs
@@ -7,6 +7,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
 
 -- | A pattern-matching alternative
 data Alternative =
diff --git a/src/main/haskell/Hydra/Haskell/Testing.hs b/src/main/haskell/Hydra/Haskell/Testing.hs
--- a/src/main/haskell/Hydra/Haskell/Testing.hs
+++ b/src/main/haskell/Hydra/Haskell/Testing.hs
@@ -59,6 +59,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
 
 -- | Add namespaces from a set of names to existing namespaces
diff --git a/src/main/haskell/Hydra/Haskell/Utils.hs b/src/main/haskell/Hydra/Haskell/Utils.hs
--- a/src/main/haskell/Hydra/Haskell/Utils.hs
+++ b/src/main/haskell/Hydra/Haskell/Utils.hs
@@ -52,6 +52,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
 
 -- | Create an application pattern from a name and argument patterns
@@ -234,7 +235,7 @@
                 Syntax.qualifiedNameQualifiers = [],
                 Syntax.qualifiedNameUnqualified = (Syntax.NamePart "")}))
           app =
-                  \l -> Optionals.withDefault dummyType (Optionals.map (\p -> Logic.ifElse (Lists.null (Pairs.second p)) (Pairs.first p) (Syntax.TypeApplication (Syntax.ApplicationType {
+                  \l -> Optionals.withDefault dummyType (Optionals.map (\p -> Logic.ifElse (Lists.isEmpty (Pairs.second p)) (Pairs.first p) (Syntax.TypeApplication (Syntax.ApplicationType {
                     Syntax.applicationTypeContext = (app (Pairs.second p)),
                     Syntax.applicationTypeArgument = (Pairs.first p)}))) (Lists.uncons l))
       in (app (Lists.reverse types))
