diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,131 @@
 
 ---
 
+## [0.17.5] - 2026-08-19
+
+Point release on the 0.17.x line. Two language-surface changes lead: sum-type eliminators unify on
+`match` (scrutinee-first), and the primitive set is tentatively finalized — `strings.lines`/`unlines`
+are removed in favour of `hydra.formatting` term helpers. A symlink-aware filesystem family lands
+across all ten hosts, and Hydra's own build system continues its migration into Hydra.
+
+**Backward-incompatible.** The `cases` -> `match` rename and the `strings.lines`/`unlines` removal
+both change the published kernel surface. Code written against 0.17.4 that uses `Optionals.cases`
+or `strings.lines`/`strings.unlines` must be updated.
+
+### Highlights
+
+- **Sum-type eliminators unify on `match`** ([#615](https://github.com/CategoricalData/hydra/issues/615)):
+  the `cases`/`match` helper pair is flipped to a single scrutinee-first `match` convention across the
+  Haskell, Java, Python, and Scala DSLs, the `optionals.cases` -> `optionals.match` kernel primitive,
+  and the corresponding implementations in TypeScript and all four Lisp dialects.
+- **Primitive set tentatively finalized** ([#417](https://github.com/CategoricalData/hydra/issues/417)):
+  `strings.lines` and `strings.unlines` are removed as primitives; their semantics are preserved as
+  `hydra.formatting.lines`/`unlines` term helpers, with overlay implementations dropped across all ten
+  hosts and callers repointed.
+- **Symlink-aware filesystem primitives** ([#666](https://github.com/CategoricalData/hydra/issues/666)):
+  `status(followLinks)`, `createSymlink`, and `readSymlink` land with native implementations for every
+  host — Haskell, Java, Python, Scala, TypeScript, Clojure, Common Lisp, Scheme, and Emacs Lisp.
+- **Build system promotion continues** ([#416](https://github.com/CategoricalData/hydra/issues/416)):
+  adds `hydra.build.walk` extension-glob helpers (`extensionOf`/`filterByExtension`/`matchesExtension`)
+  and `hydra.build.comparereportlogic`, a set of pure snapshot-compare decision helpers.
+
+### Bug fixes
+
+- **`math.range` was inclusive of both bounds** ([#647](https://github.com/CategoricalData/hydra/issues/647)),
+  silently fabricating an extra element in index arithmetic.
+- **Java coder TCO-`cases` codegen** dropped enclosing-method type parameters from union-variant casts
+  (found via [#666](https://github.com/CategoricalData/hydra/issues/666)).
+- **`ShaclRdf` promoted to a DSL module** ([#652](https://github.com/CategoricalData/hydra/issues/652))
+  so the SHACL pipeline works outside Haskell.
+- **Cold-seed shim portability**: the R22 bootstrap shim used GNU-only `\+` and `\b`, which fail under
+  BSD `sed` on macOS ([#417](https://github.com/CategoricalData/hydra/issues/417)).
+- **Bootstrap Haskell target missing `unix`**: the demo's static `package.yaml` was not updated when
+  `System.Posix.Files` entered the kernel overlay, breaking every `*-to-haskell` bootstrap cell
+  ([#670](https://github.com/CategoricalData/hydra/issues/670)).
+- **Bootstrap comparison scored Scala and TypeScript cells as passing without comparing anything**:
+  `compare_output`'s target switch was missing both languages, so the baseline path was malformed,
+  every file counted as "no baseline found", and a zero-file comparison reported `pass`. A Scala or
+  TypeScript *target* cell could never fail ([#671](https://github.com/CategoricalData/hydra/issues/671)).
+
+### Known issues
+
+- A pre-#630 post-generation text pass rewrites `hydra.lib.<sub>` -> `hydra.overlay.<lang>.lib.<sub>`
+  in generated **doc comments** as well as code, because its quote-prefix guard does not recognize
+  mid-sentence prose references. Affects two kernel doc strings in `src/main`
+  (`hydra.error.system`, `hydra.error.packaging`) plus ~20 generated docstrings in the test tree;
+  cosmetic only, no behavioral impact. Tracked as part of
+  [#633](https://github.com/CategoricalData/hydra/issues/633).
+- Bootstrap comparison diff detail is not persisted to the run directory
+  ([#671](https://github.com/CategoricalData/hydra/issues/671)).
+
+---
+
+## [0.17.4] - 2026-08-09
+
+Point release on the 0.17.x line. Fixes an inference bug that mis-typed generated Java and Python for
+`readFile`, adds the `divide` primitive, and restores `hydra-ext` to the published package set. Release
+validation also surfaced a family of pre-existing cross-host code-generation defects — none of which the
+per-host CI jobs can catch, because only the bootstrap exercises one host generating *another*.
+
+### Highlights
+
+- **Output-only nominal generalization fixed** ([#637](https://github.com/CategoricalData/hydra/issues/637),
+  with [#638](https://github.com/CategoricalData/hydra/issues/638) /
+  [#639](https://github.com/CategoricalData/hydra/issues/639) /
+  [#640](https://github.com/CategoricalData/hydra/issues/640)): a nominal type appearing only in the
+  *output* position of a primitive's signature was generalized to a free type variable instead of resolving
+  as nominal, mis-typing the generated Java and Python for `readFile`. Also widens universe closure and adds
+  a `kernelTypeUniverse` helper, an effect DSL builder, and `effects`/`files`/`text`/`system` lib wrappers.
+- **`divide` primitive** ([#317](https://github.com/CategoricalData/hydra/issues/317)): now present in the
+  published kernel. This also clears the cold-clone dangle, since a cold seed against published 0.17.4
+  resolves `DefMath.divide`.
+- **`hydra-ext` republished** ([#636](https://github.com/CategoricalData/hydra/issues/636)): back in the
+  published set after its absence from 0.17.1–0.17.3, shipping to Hackage and PyPI.
+
+### Bug fixes
+
+- **Overlay-lib redirection skipped for the Haskell target**
+  ([#630](https://github.com/CategoricalData/hydra/issues/630)):
+  the Java, Python, Scheme, Clojure and Common Lisp heads resolved the overlay lib directory using an
+  all-lowercase path and did not lower-case the names they scanned. Haskell's overlay tree is capitalized
+  (`Hydra/Overlay/Haskell/Lib/`), so every `hydra.lib.*` redirect silently failed and the generated Haskell
+  imported `Hydra.Lib.*` instead of `Hydra.Overlay.Haskell.Lib.*`, producing hundreds of type errors. The
+  defect was invisible on case-sensitive filesystems, where the failed directory check falls back to a
+  hard-coded list that happens to be correct. The Python head carried *two* independent copies of this scan;
+  only the one in `generation.py` feeds the Haskell coder.
+- **Lisp-as-host bootstrap** ([#520](https://github.com/CategoricalData/hydra/issues/520),
+  [#630](https://github.com/CategoricalData/hydra/issues/630)): the four Lisp head decode drivers called
+  `from_json` with a stale 4-argument signature, and the Lisp bootstrap-driver coders were not pre-bound with
+  `overlaySubs`.
+- **Python-as-host dropped the `Graph` argument** ([#630](https://github.com/CategoricalData/hydra/issues/630)):
+  `overlaySubs` was not threaded into the Python head's `write_*` wrappers.
+- **Clojure `resolve-coder` and `decimal-to-float32`**
+  ([#630](https://github.com/CategoricalData/hydra/issues/630)): a stray parenthesis closed a `case` early,
+  making an unsupported-default block run unconditionally; and `(float (.doubleValue ...))` threw on
+  `Float.MAX_VALUE`, now saturating via `(.floatValue (bigdec x))`.
+- **Cold-seeder failed on macOS** ([#630](https://github.com/CategoricalData/hydra/issues/630)): the #376
+  cold seeder used a bare `sed -i -e`, which is GNU-only and aborts immediately under BSD `sed`.
+
+### Release engineering
+
+- **Review checkpoint before Maven Central publishes**
+  ([#591](https://github.com/CategoricalData/hydra/issues/591)): `HYDRA_JAVA_PUBLISH_HOLD=1` uploads the
+  aggregated Java deployment as `USER_MANAGED`, so it validates and then holds for review instead of
+  publishing automatically. This mirrors `HYDRA_SCALA_PUBLISH_HOLD` and makes an upload reversible — a held
+  deployment can be dropped, whereas an automatic one cannot be undone.
+
+### Known issues
+
+- **`hydra-ext` is not published to Maven Central**
+  ([#643](https://github.com/CategoricalData/hydra/issues/643)): it does not compile for the Java target,
+  because the visitor-pattern inner interface the Java coder emits collides with the enclosing `Visitor`
+  type in `hydra.cpp.syntax`. Only Java is affected; the Python coder emits sibling classes rather than
+  nested ones. ext ships to Hackage and PyPI at 0.17.4 and is expected to rejoin the Java set once fixed.
+- **TypeScript no longer self-hosts**
+  ([#642](https://github.com/CategoricalData/hydra/issues/642)): the TypeScript host's JSON decoder fails to
+  decode most of the kernel, so `typescript-as-host` code generation is broken. No published artifact is
+  affected — `dist/typescript` is generated by the Haskell host and passes its test suite.
+
 ## [0.17.3] - 2026-08-01
 
 Point release on the 0.17.x line, focused on **release-artifact integrity**. The 0.17.2 release shipped
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.39.1.
+-- This file has been generated from package.yaml by hpack version 0.38.1.
 --
 -- see: https://github.com/sol/hpack
 
 name:           hydra-python
-version:        0.17.4
+version:        0.17.5
 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.4
+    , hydra-kernel ==0.17.5
     , 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
@@ -13,7 +13,7 @@
 import qualified Hydra.Typed as Typed
 import qualified Hydra.Typing as Typing
 import qualified Hydra.Util as Util
-import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, map, pure, sum)
+import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
 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
@@ -36,7 +36,7 @@
 import qualified Hydra.Util as Util
 import qualified Hydra.Validation as Validation
 import qualified Hydra.Variants as Variants
-import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, map, pure, sum)
+import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
 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
@@ -7,7 +7,7 @@
 import qualified Hydra.Core as Core
 import qualified Hydra.Python.Syntax as Syntax
 import qualified Hydra.Typed as Typed
-import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, map, pure, sum)
+import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
 
 -- | DSL name token for hydra.python.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
@@ -37,7 +37,7 @@
 import qualified Hydra.Util as Util
 import qualified Hydra.Validation as Validation
 import qualified Hydra.Variants as Variants
-import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, map, pure, sum)
+import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
 import qualified Data.Set as S
 
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
@@ -65,7 +65,7 @@
 import qualified Hydra.Validation as Validation
 import qualified Hydra.Variables as Variables
 import qualified Hydra.Variants as Variants
-import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, map, pure, sum)
+import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
 import qualified Data.Map as M
 import qualified Data.Set as S
@@ -199,7 +199,7 @@
                     let v = Core.lambdaParameter v0
                         mdom = Core.lambdaDomain v0
                         body = Core.lambdaBody v0
-                    in (Optionals.cases (Maps.lookup v countByName) (Maps.insert v 1 countByName, (Lists.cons field done)) (\count ->
+                    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))
                           newBody = Reduction.alphaConvert v v2 body
@@ -361,7 +361,7 @@
           let g = pythonEnvironmentGetGraph env
               allArgs = Lists.concat2 hargs rargs
               inlineVars = PythonEnvironment.pythonEnvironmentInlineVariables env
-          in (Optionals.cases (Maps.lookup v0 (Graph.graphPrimitives g)) (Optionals.cases (Lexical.lookupBinding g v0) (Eithers.bind (encodeVariable cx env v0 hargs) (\expr -> Right (expr, rargs))) (\el -> Optionals.cases (Core.bindingTypeScheme el) (Eithers.bind (encodeVariable cx env v0 hargs) (\expr -> Right (expr, rargs))) (\ts ->
+          in (Optionals.match (Maps.lookup v0 (Graph.graphPrimitives g)) (Optionals.match (Lexical.lookupBinding g v0) (Eithers.bind (encodeVariable cx env v0 hargs) (\expr -> Right (expr, rargs))) (\el -> Optionals.match (Core.bindingTypeScheme el) (Eithers.bind (encodeVariable cx env v0 hargs) (\expr -> Right (expr, rargs))) (\ts ->
             let elArity = Arity.typeSchemeArity ts
                 consumeCount = Ordering.min elArity (Lists.length allArgs)
                 consumedArgs = Lists.take consumeCount allArgs
@@ -412,14 +412,14 @@
           term1 = Core.bindingTerm binding
           mts = Core.bindingTypeScheme binding
           fname = PythonNames.encodeName True Util.CaseConventionLowerSnake env name1
-      in (Optionals.cases mts (
+      in (Optionals.match mts (
         let gathered = gatherLambdas term1
             lambdaParams = Pairs.first gathered
             innerBody = Pairs.second gathered
             mcsa = isCaseStatementApplication innerBody
-        in (Optionals.cases mcsa (
+        in (Optionals.match mcsa (
           let mcs = extractCaseElimination term1
-          in (Optionals.cases mcs (Eithers.bind (encodeTermMultiline cx env term1) (\stmts -> Optionals.cases (Lists.head stmts) (Left (Errors.ErrorOther (Errors.OtherError "encodeTermMultiline returned no statements"))) (\x -> Right x))) (\cs ->
+          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
                 dflt = Core.caseStatementDefault cs
                 cases_ = Core.caseStatementCases cs
@@ -463,7 +463,7 @@
                   Syntax.functionDefinitionDecorators = Nothing,
                   Syntax.functionDefinitionRaw = funcDefRaw})))))))))))) (\csa -> Logic.ifElse (Lists.null lambdaParams) (
           let mcs = extractCaseElimination term1
-          in (Optionals.cases mcs (Eithers.bind (encodeTermMultiline cx env term1) (\stmts -> Optionals.cases (Lists.head stmts) (Left (Errors.ErrorOther (Errors.OtherError "encodeTermMultiline returned no statements"))) (\x -> Right x))) (\cs ->
+          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
                 dflt = Core.caseStatementDefault cs
                 cases_ = Core.caseStatementCases cs
@@ -576,7 +576,7 @@
             termIsComplex = Predicates.isComplexTerm tc term
             isTrivial = Predicates.isTrivialTerm term
             needsThunk =
-                    Logic.ifElse isTrivial False (Optionals.cases mts (Logic.and allowThunking (Logic.or isComplexVar termIsComplex)) (\ts -> Logic.and allowThunking (Logic.and (Equality.equal (Arity.typeSchemeArity ts) 0) (Logic.or isComplexVar termIsComplex))))
+                    Logic.ifElse isTrivial False (Optionals.match mts (Logic.and allowThunking (Logic.or isComplexVar termIsComplex)) (\ts -> Logic.and allowThunking (Logic.and (Equality.equal (Arity.typeSchemeArity ts) 0) (Logic.or isComplexVar termIsComplex))))
             pterm = makeLazy pbody
         in (Right (Syntax.NamedExpressionAssignment (Syntax.AssignmentExpression {
           Syntax.assignmentExpressionName = pyName,
@@ -589,7 +589,7 @@
 -- | Encode the default (wildcard) case block for a match statement
 encodeDefaultCaseBlock :: (t0 -> Either t1 Syntax.Expression) -> Bool -> Maybe t0 -> Core.Name -> Either t1 [Syntax.CaseBlock]
 encodeDefaultCaseBlock termToExpr isFull mdflt tname =
-    Eithers.bind (Optionals.cases mdflt (Right (Logic.ifElse isFull (Utils.raiseAssertionError "Unreachable: all variants handled") (Utils.raiseTypeError (Strings.concat2 "Unsupported " (Names.localNameOf tname))))) (\d -> Eithers.bind (termToExpr d) (\pyexpr -> Right (Utils.returnSingle pyexpr)))) (\stmt ->
+    Eithers.bind (Optionals.match mdflt (Right (Logic.ifElse isFull (Utils.raiseAssertionError "Unreachable: all variants handled") (Utils.raiseTypeError (Strings.concat2 "Unsupported " (Names.localNameOf tname))))) (\d -> Eithers.bind (termToExpr d) (\pyexpr -> Right (Utils.returnSingle pyexpr)))) (\stmt ->
       let patterns = Utils.pyClosedPatternToPyPatterns Syntax.ClosedPatternWildcard
           body = Utils.indentedBlock Nothing [
                 [
@@ -608,7 +608,7 @@
         let name = Packaging.termDefinitionName v0
             term = Packaging.termDefinitionBody v0
             typ =
-                    Optionals.cases (Packaging.termDefinitionSignature v0) (Core.TypeScheme {
+                    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)
@@ -637,7 +637,7 @@
                     Utils.functionCall (Utils.pyNameToPyPrimary (PythonNames.encodeName True Util.CaseConventionPascal env (Core.Name "hydra.core.Name"))) [
                       Utils.doubleQuotedString fnameStr]
             assignStmt = Utils.assignmentStatement pyName pyValue
-        in (Right (Optionals.cases mcomment [
+        in (Right (Optionals.match mcomment [
           assignStmt] (\c -> [
           assignStmt,
           (Utils.pyExpressionToPyStatement (Utils.tripleQuotedString c))])))))
@@ -865,7 +865,7 @@
                     Logic.ifElse (Logic.and isTypeMod (Equality.equal targetPythonVersion PythonEnvironment.PythonVersionPython310)) (setMetaUsesTypeAlias meta2 True) meta2
             namespaces = PythonEnvironment.pythonModuleMetadataNamespaces meta0
             commentStmts =
-                    Optionals.cases (Optionals.map Formatting.normalizeComment (Optionals.bind (Packaging.moduleMetadata mod) (\em -> Packaging.entityMetadataDescription em))) [] (\c -> [
+                    Optionals.match (Optionals.map Formatting.normalizeComment (Optionals.bind (Packaging.moduleMetadata mod) (\em -> Packaging.entityMetadataDescription em))) [] (\c -> [
                       Utils.commentStatement c])
             importStmts = moduleImports namespaces meta
             tvars =
@@ -889,7 +889,7 @@
           boundVars = PythonEnvironment.pythonEnvironmentBoundTypeVariables env
           tparamList = Pairs.first boundVars
           mGenericArg = genericArg tparamList
-          args = Optionals.cases mGenericArg Nothing (\a -> Just (Utils.pyExpressionsToPyArgs [
+          args = Optionals.match mGenericArg Nothing (\a -> Just (Utils.pyExpressionsToPyArgs [
                 a]))
           decs = Just (Syntax.Decorators [
                 dataclassDecorator])
@@ -1063,7 +1063,7 @@
           let pmapOfEntries =
                   Utils.projectFromExpression (Utils.pyNameToPyExpression (Syntax.Name "PersistentMap")) (Syntax.Name "of_entries")
           in (Right (Utils.functionCall (Utils.pyExpressionToPyPrimary pmapOfEntries) tuplePairs)))
-        Core.TermOptional v0 -> Optionals.cases v0 (Right (Utils.functionCall (Utils.pyNameToPyPrimary (Syntax.Name "None_")) [])) (\t1 -> Eithers.bind (encode t1) (\pyexp -> withCast (Utils.functionCall (Utils.pyNameToPyPrimary (Syntax.Name "Given")) [
+        Core.TermOptional v0 -> Optionals.match v0 (Right (Utils.functionCall (Utils.pyNameToPyPrimary (Syntax.Name "None_")) [])) (\t1 -> Eithers.bind (encode t1) (\pyexp -> withCast (Utils.functionCall (Utils.pyNameToPyPrimary (Syntax.Name "Given")) [
           pyexp])))
         Core.TermPair v0 ->
           let t1 = Pairs.first v0
@@ -1090,7 +1090,7 @@
           in (Eithers.bind (Resolution.requireUnionType cx (pythonEnvironmentGetGraph env) tname) (\rt -> Logic.ifElse (Predicates.isEnumRowType rt) (Right (Utils.projectFromExpression (Utils.pyNameToPyExpression (PythonNames.encodeNameQualified env tname)) (PythonNames.encodeEnumValue env (Core.fieldName field)))) (
             let fname = Core.fieldName field
                 isUnitVariant =
-                        Optionals.cases (Lists.find (\ft -> Equality.equal (Core.unName (Core.fieldTypeName ft)) (Core.unName fname)) rt) False (\ft -> Predicates.isUnitType (Strip.deannotateType (Core.fieldTypeType ft)))
+                        Optionals.match (Lists.find (\ft -> Equality.equal (Core.unName (Core.fieldTypeName ft)) (Core.unName fname)) rt) False (\ft -> Predicates.isUnitType (Strip.deannotateType (Core.fieldTypeType ft)))
             in (Eithers.bind (Logic.ifElse (Logic.or (Predicates.isUnitTerm (Core.fieldTerm field)) isUnitVariant) (Right []) (Eithers.bind (encode (Core.fieldTerm field)) (\parg -> Right [
               parg]))) (\args ->
               let deconflictedName = deconflictVariantName True env tname fname (PythonEnvironment.pythonEnvironmentGraph env)
@@ -1269,7 +1269,7 @@
         let isEnum = Predicates.isEnumRowType rt
             valueExpr = Utils.projectFromExpression pyArg (Syntax.Name "value")
             isinstancePrimary = Utils.pyNameToPyPrimary (Syntax.Name "isinstance")
-        in (Eithers.bind (Optionals.cases mdefault (Right (unsupportedExpression "no matching case in inline union elimination")) (\dflt -> encodeTermInline cx env False dflt)) (\pyDefault ->
+        in (Eithers.bind (Optionals.match mdefault (Right (unsupportedExpression "no matching case in inline union elimination")) (\dflt -> encodeTermInline cx env False dflt)) (\pyDefault ->
           let encodeBranch =
                   \field ->
                     let fname = Core.caseAlternativeName field
@@ -1390,11 +1390,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.cases (Lexical.lookupPrimitive g name) (Right asFunctionCall) (\prim ->
+      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 ->
         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 numRemaining)
+              remainingParams =
+                      Lists.map (\i -> Syntax.Name (Strings.concat2 "x" (Literals.showInt32 i))) (Math.range 1 (Math.add numRemaining 1))
               remainingExprs =
                       Lists.map (\n -> Syntax.ExpressionSimple (Syntax.Disjunction [
                         Syntax.Conjunction [
@@ -1420,9 +1421,9 @@
               allArgs = Lists.concat2 args remainingExprs
               fullCall =
                       Utils.functionCall (Utils.pyNameToPyPrimary (PythonNames.encodeName True Util.CaseConventionLowerSnake env name)) allArgs
-          in (Right (makeUncurriedLambda remainingParams fullCall))))))) (Optionals.cases mTyp (Logic.ifElse (Sets.member name tcLambdaVars) (Right asVariable) (Logic.ifElse (Sets.member name inlineVars) (Right (lazyDotGet asVariable)) (Optionals.cases (Lexical.lookupPrimitive g name) (Optionals.cases (Lexical.lookupBinding g name) (Optionals.cases (Maps.lookup name tcMetadata) (Left (Errors.ErrorOther (Errors.OtherError (Strings.concat2 "Unknown variable: " (Core.unName name))))) (\_ -> Right asFunctionCall)) (\el ->
+          in (Right (makeUncurriedLambda remainingParams fullCall))))))) (Optionals.match mTyp (Logic.ifElse (Sets.member name tcLambdaVars) (Right asVariable) (Logic.ifElse (Sets.member name inlineVars) (Right (lazyDotGet asVariable)) (Optionals.match (Lexical.lookupPrimitive g name) (Optionals.match (Lexical.lookupBinding g name) (Optionals.match (Maps.lookup name tcMetadata) (Left (Errors.ErrorOther (Errors.OtherError (Strings.concat2 "Unknown variable: " (Core.unName name))))) (\_ -> Right asFunctionCall)) (\el ->
         let elTrivial1 = Predicates.isTrivialTerm (Core.bindingTerm el)
-        in (Optionals.cases (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) (
+        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
           in (Right asFunctionRef)))))) (\prim ->
@@ -1435,12 +1436,12 @@
         let unwrapped = lazyDotGet asVariable
             asFunctionRef =
                     Logic.ifElse (Logic.not (Sets.null (Variables.freeVariablesInType typ))) (makeSimpleLambda (Arity.typeArity typ) unwrapped) unwrapped
-        in (Right asFunctionRef)) (Logic.ifElse (Logic.not (Maps.member name tcMetadata)) (Optionals.cases (Lexical.lookupBinding g name) (
+        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
         in (Right asFunctionRef)) (\el ->
         let elTrivial = Predicates.isTrivialTerm (Core.bindingTerm el)
-        in (Optionals.cases (Core.bindingTypeScheme el) (Logic.ifElse (Logic.and (Equality.equal (Arity.typeArity typ) 0) (Logic.not elTrivial)) (Right asFunctionCall) (
+        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
           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) (
@@ -1525,12 +1526,12 @@
                 Core.TermEither v0 ->
                   let metaWithCast = setMetaUsesCast True meta
                   in (Eithers.either (\_ -> setMetaUsesLeft metaWithCast True) (\_ -> setMetaUsesRight metaWithCast True) v0)
-                Core.TermLambda v0 -> Optionals.cases (Core.lambdaDomain v0) meta (\dom -> Logic.ifElse topLevel (extendMetaForType True False dom meta) meta)
+                Core.TermLambda v0 -> Optionals.match (Core.lambdaDomain v0) meta (\dom -> Logic.ifElse topLevel (extendMetaForType True False dom meta) meta)
                 Core.TermLet v0 ->
                   let bindings = Core.letBindings v0
                   in (Lists.foldl (
                     let forBinding =
-                            \m -> \b -> Optionals.cases (Core.bindingTypeScheme b) m (\ts ->
+                            \m -> \b -> Optionals.match (Core.bindingTypeScheme b) m (\ts ->
                               let term1 = Core.bindingTerm b
                               in (Logic.ifElse (Analysis.isSimpleAssignment term1) m (extendMetaForType True True (Core.typeSchemeBody ts) m)))
                     in forBinding) meta bindings)
@@ -1540,7 +1541,7 @@
                 Core.TermList _ -> setMetaUsesFrozenList meta True
                 Core.TermMap _ -> setMetaUsesFrozenDict meta True
                 Core.TermSet _ -> setMetaUsesFrozenSet meta True
-                Core.TermOptional v0 -> Optionals.cases v0 (setMetaUsesNothing meta True) (\_ -> setMetaUsesJust meta True)
+                Core.TermOptional v0 -> Optionals.match v0 (setMetaUsesNothing meta True) (\_ -> setMetaUsesJust meta True)
                 Core.TermInject _ -> setMetaUsesCast True meta
                 _ -> meta
       in (Rewriting.foldOverTerm Coders.TraversalOrderPre step meta0 term)
@@ -1636,7 +1637,7 @@
         in (Right (Utils.indentedBlock comment [
           [
             whileStmt]])))) (Eithers.bind (encodeTermMultiline cx env body) (\stmts -> Right (Utils.indentedBlock comment [
-        Lists.concat2 prefixes stmts])))) (\block -> Eithers.bind (Optionals.cases mcod (Right Nothing) (\cod -> Eithers.bind (encodeType env cod) (\pytyp -> Right (Just pytyp)))) (\mreturnType ->
+        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
@@ -1675,7 +1676,7 @@
                     Packaging.DefinitionTerm v0 ->
                       let term = Packaging.termDefinitionBody v0
                           typ =
-                                  Optionals.cases (Packaging.termDefinitionSignature v0) (Core.TypeVariable (Core.Name "hydra.core.Unit")) (\sig -> Core.typeSchemeBody (Scoping.termSignatureToTypeScheme sig))
+                                  Optionals.match (Packaging.termDefinitionSignature v0) (Core.TypeVariable (Core.Name "hydra.core.Unit")) (\sig -> Core.typeSchemeBody (Scoping.termSignatureToTypeScheme sig))
                           meta2 = extendMetaForType True True typ meta
                       in (extendMetaForTerm True meta2 term)
                     Packaging.DefinitionType v0 ->
@@ -1805,7 +1806,7 @@
 -- | Per-parameter isLazy flags of a primitive (by name), or empty if not a primitive. Single source of truth for which arguments coders thunk; replaces hard-coded name tables (issue #391).
 lazyFlagsForPrimitive :: Graph.Graph -> Core.Name -> [Bool]
 lazyFlagsForPrimitive g name =
-    Optionals.cases (Maps.lookup name (Graph.graphPrimitives g)) [] (\prim ->
+    Optionals.match (Maps.lookup name (Graph.graphPrimitives g)) [] (\prim ->
       let def0 = Graph.primitiveDefinition prim
           sig = Packaging.primitiveDefinitionSignature def0
       in (Lists.map (\p -> Typing.parameterIsLazy p) (Typing.termSignatureParameters sig)))
@@ -1845,7 +1846,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 arity)
+      let args = Lists.map (\i -> Syntax.Name (Strings.concat2 "x" (Literals.showInt32 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,
@@ -2825,7 +2826,7 @@
       Core.TermProject _ -> 1
       Core.TermUnwrap _ -> 1
       Core.TermCases _ -> 1
-      Core.TermVariable v0 -> Optionals.cases (Lexical.lookupBinding graph v0) 0 (\el -> Optionals.cases (Core.bindingTypeScheme el) (Arity.termArity (Core.bindingTerm el)) (\ts -> Arity.typeSchemeArity ts))
+      Core.TermVariable v0 -> Optionals.match (Lexical.lookupBinding graph v0) 0 (\el -> Optionals.match (Core.bindingTypeScheme el) (Arity.termArity (Core.bindingTerm el)) (\ts -> Arity.typeSchemeArity ts))
       _ -> 0
 
 -- | Create a TypeVar assignment statement for a type variable name
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
@@ -10,7 +10,7 @@
 import qualified Hydra.Python.Syntax as Syntax
 import qualified Hydra.Typing as Typing
 import qualified Hydra.Util as Util
-import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, map, pure, sum)
+import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
 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
@@ -31,7 +31,7 @@
 import qualified Hydra.Util as Util
 import qualified Hydra.Validation as Validation
 import qualified Hydra.Variants as Variants
-import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, map, pure, sum)
+import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
 import qualified Data.Set as S
 
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
@@ -41,7 +41,7 @@
 import qualified Hydra.Util as Util
 import qualified Hydra.Validation as Validation
 import qualified Hydra.Variants as Variants
-import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, map, pure, sum)
+import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
 import qualified Data.Map as M
 import qualified Data.Set as S
@@ -76,7 +76,7 @@
           local = Util.qualifiedNameLocal qualName
           pyLocal = sanitizePythonName (Formatting.convertCase Util.CaseConventionCamel conv local)
           pyNs = \nsVal -> encodeNamespaceStringWithOverrides (Environment.pythonEnvironmentOverlaySubs env) nsVal
-      in (Logic.ifElse isQualified (Optionals.cases (Maps.lookup name boundVars) (Logic.ifElse (Equality.equal mns (Just focusNs)) (Syntax.Name (Logic.ifElse useFutureAnnotations pyLocal (Serde.escapePythonString True pyLocal))) (Optionals.cases mns (Syntax.Name pyLocal) (\nsVal -> Syntax.Name (Strings.concat2 (pyNs nsVal) (Strings.concat2 "." pyLocal))))) (\n -> n)) (Syntax.Name pyLocal))
+      in (Logic.ifElse isQualified (Optionals.match (Maps.lookup name boundVars) (Logic.ifElse (Equality.equal mns (Just focusNs)) (Syntax.Name (Logic.ifElse useFutureAnnotations pyLocal (Serde.escapePythonString True pyLocal))) (Optionals.match mns (Syntax.Name pyLocal) (\nsVal -> Syntax.Name (Strings.concat2 (pyNs nsVal) (Strings.concat2 "." pyLocal))))) (\n -> n)) (Syntax.Name pyLocal))
 
 -- | Encode a name as a fully qualified Python name
 encodeNameQualified :: Environment.PythonEnvironment -> Core.Name -> Syntax.Name
@@ -90,7 +90,7 @@
           mns = Util.qualifiedNameModuleName qualName
           local = Util.qualifiedNameLocal qualName
           pyNs = \nsVal -> encodeNamespaceStringWithOverrides (Environment.pythonEnvironmentOverlaySubs env) nsVal
-      in (Optionals.cases (Maps.lookup name boundVars) (Logic.ifElse (Equality.equal mns (Just focusNs)) (Syntax.Name (Logic.ifElse useFutureAnnotations local (Serde.escapePythonString True local))) (Optionals.cases mns (Syntax.Name (sanitizePythonName local)) (\nsVal -> Syntax.Name (Strings.concat2 (pyNs nsVal) (Strings.concat2 "." (sanitizePythonName local)))))) (\n -> n))
+      in (Optionals.match (Maps.lookup name boundVars) (Logic.ifElse (Equality.equal mns (Just focusNs)) (Syntax.Name (Logic.ifElse useFutureAnnotations local (Serde.escapePythonString True local))) (Optionals.match mns (Syntax.Name (sanitizePythonName local)) (\nsVal -> Syntax.Name (Strings.concat2 (pyNs nsVal) (Strings.concat2 "." (sanitizePythonName local)))))) (\n -> n))
 
 -- | Encode a namespace as a Python dotted name
 encodeNamespace :: Packaging.ModuleName -> Syntax.DottedName
@@ -100,7 +100,7 @@
 -- | Convert a ModuleName to its Python dotted import string, routing overlay modules to hydra.overlay.python.*
 encodeNamespaceStringWithOverrides :: S.Set String -> Packaging.ModuleName -> String
 encodeNamespaceStringWithOverrides overlaySubs nsVal =
-    Optionals.cases (Logic.ifElse (Logic.and (Logic.and (Equality.equal (Lists.length (Strings.splitOn "." (Packaging.unModuleName nsVal))) 3) (Equality.equal (Lists.take 2 (Strings.splitOn "." (Packaging.unModuleName nsVal))) [
+    Optionals.match (Logic.ifElse (Logic.and (Logic.and (Equality.equal (Lists.length (Strings.splitOn "." (Packaging.unModuleName nsVal))) 3) (Equality.equal (Lists.take 2 (Strings.splitOn "." (Packaging.unModuleName nsVal))) [
       "hydra",
       "lib"])) (Sets.member (Strings.join "." (Lists.drop 2 (Strings.splitOn "." (Packaging.unModuleName nsVal)))) overlaySubs)) (Just (Strings.concat2 "hydra.overlay.python.lib." (Strings.join "." (Lists.drop 2 (Strings.splitOn "." (Packaging.unModuleName nsVal)))))) Nothing) (Optionals.withDefault (Strings.join "." (Lists.map (Formatting.convertCase Util.CaseConventionCamel Util.CaseConventionLowerSnake) (Strings.splitOn "." (Packaging.unModuleName nsVal)))) (Maps.lookup nsVal overlayPythonModuleAliases)) (\redirected -> redirected)
 
@@ -166,7 +166,7 @@
           focusPair = Util.moduleNamesFocus namespaces
           focusNs = Pairs.first focusPair
           mns = Names.moduleNameOf name
-          sameNamespace = Optionals.cases mns False (\ns -> Equality.equal ns focusNs)
+          sameNamespace = Optionals.match mns False (\ns -> Equality.equal ns focusNs)
       in (Logic.ifElse (Logic.and quoted sameNamespace) (Syntax.ExpressionSimple (Syntax.Disjunction [
         Syntax.Conjunction [
           Syntax.InversionSimple (Syntax.Comparison {
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
@@ -6,7 +6,7 @@
 
 import qualified Hydra.Core as Core
 import qualified Hydra.Packaging as Packaging
-import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, map, pure, sum)
+import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
 
 data 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
@@ -13,6 +13,7 @@
 import qualified Hydra.Error.Core as ErrorCore
 import qualified Hydra.Error.Packaging as ErrorPackaging
 import qualified Hydra.Errors as Errors
+import qualified Hydra.Formatting as Formatting
 import qualified Hydra.Graph as Graph
 import qualified Hydra.Json.Model as Model
 import qualified Hydra.Overlay.Haskell.Lib.Equality as Equality
@@ -38,7 +39,7 @@
 import qualified Hydra.Util as Util
 import qualified Hydra.Validation as Validation
 import qualified Hydra.Variants as Variants
-import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, map, pure, sum)
+import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
 
 -- | Serialize an annotated RHS
@@ -450,7 +451,7 @@
 
       let name = Syntax.importFromAsNameName ifan
           alias = Syntax.importFromAsNameAs ifan
-      in (Optionals.cases alias (nameToExpr name) (\a -> Serialization.spaceSep [
+      in (Optionals.match alias (nameToExpr name) (\a -> Serialization.spaceSep [
         nameToExpr name,
         (Serialization.cst "as"),
         (nameToExpr a)]))
@@ -930,7 +931,7 @@
 stringToExpr s =
 
       let content = Syntax.stringValue s
-          prefix = Optionals.cases (Syntax.stringPrefix s) "" stringPrefixToText
+          prefix = Optionals.match (Syntax.stringPrefix s) "" stringPrefixToText
           style = Syntax.stringQuoteStyle s
       in case style of
         Syntax.QuoteStyleSingle -> Serialization.cst (Strings.concat2 prefix (escapePythonString False content))
@@ -991,7 +992,7 @@
 -- | Convert a doc string to Python comment format. Empty source lines emit `#` (no trailing space) so blank comment lines don't carry trailing whitespace into the generated file.
 toPythonComments :: String -> String
 toPythonComments doc_ =
-    Logic.ifElse (Equality.equal doc_ "") "" (Strings.join "\n" (Lists.map (\line -> Logic.ifElse (Equality.equal line "") "#" (Strings.concat2 "# " line)) (Strings.lines (Docs.renderDocStringWith pythonDocEntityRef doc_))))
+    Logic.ifElse (Equality.equal doc_ "") "" (Strings.join "\n" (Lists.map (\line -> Logic.ifElse (Equality.equal line "") "#" (Strings.concat2 "# " line)) (Formatting.lines (Docs.renderDocStringWith pythonDocEntityRef doc_))))
 
 -- | Serialize a Python tuple
 tupleToExpr :: Syntax.Tuple -> Ast.Expr
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
@@ -6,7 +6,7 @@
 module Hydra.Python.Syntax where
 
 import qualified Hydra.Core as Core
-import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, map, pure, sum)
+import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
 
 data 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
@@ -40,7 +40,7 @@
 import qualified Hydra.Util as Util
 import qualified Hydra.Validation as Validation
 import qualified Hydra.Variants as Variants
-import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, map, pure, sum)
+import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
 
 -- | Build the complete Python test module content
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
@@ -40,21 +40,21 @@
 import qualified Hydra.Util as Util
 import qualified Hydra.Validation as Validation
 import qualified Hydra.Variants as Variants
-import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, map, pure, sum)
+import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
 import qualified Data.Set as S
 
 -- | Annotate an expression with an optional comment using Annotated[]
 annotatedExpression :: Maybe String -> Syntax.Expression -> Syntax.Expression
 annotatedExpression mcomment expr =
-    Optionals.cases mcomment expr (\c -> pyPrimaryToPyExpression (primaryWithExpressionSlices (pyNameToPyPrimary (Syntax.Name "Annotated")) [
+    Optionals.match mcomment expr (\c -> pyPrimaryToPyExpression (primaryWithExpressionSlices (pyNameToPyPrimary (Syntax.Name "Annotated")) [
       expr,
       (doubleQuotedString c)]))
 
 -- | Annotate a statement with an optional comment
 annotatedStatement :: Maybe String -> Syntax.Statement -> Syntax.Statement
 annotatedStatement mcomment stmt =
-    Optionals.cases mcomment stmt (\c -> Syntax.StatementAnnotated (Syntax.AnnotatedStatement {
+    Optionals.match mcomment stmt (\c -> Syntax.StatementAnnotated (Syntax.AnnotatedStatement {
       Syntax.annotatedStatementComment = c,
       Syntax.annotatedStatementStatement = stmt}))
 
@@ -189,7 +189,7 @@
 indentedBlock :: Maybe String -> [[Syntax.Statement]] -> Syntax.Block
 indentedBlock mcomment stmts =
 
-      let commentGroup = Optionals.cases mcomment [] (\s -> [
+      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 [
@@ -213,7 +213,7 @@
 orExpression prims =
 
       let build =
-              \prev -> \ps -> Optionals.cases (Lists.uncons ps) (Syntax.BitwiseOr {
+              \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.bitwiseOrLhs = prev,
@@ -325,7 +325,7 @@
 -- | Extracts the primary from an expression, or wraps it in parentheses if the expression does not contain a primary
 pyExpressionToPyPrimary :: Syntax.Expression -> Syntax.Primary
 pyExpressionToPyPrimary e =
-    Optionals.cases (decodePyExpressionToPyPrimary e) (Syntax.PrimarySimple (Syntax.AtomGroup (Syntax.GroupExpression (Syntax.NamedExpressionSimple e)))) (\prim -> prim)
+    Optionals.match (decodePyExpressionToPyPrimary e) (Syntax.PrimarySimple (Syntax.AtomGroup (Syntax.GroupExpression (Syntax.NamedExpressionSimple e)))) (\prim -> prim)
 
 -- | Convert an Expression to a SimpleStatement (as star expressions)
 pyExpressionToPySimpleStatement :: Syntax.Expression -> Syntax.SimpleStatement
