packages feed

hydra-scala 0.17.0 → 0.17.1

raw patch · 6 files changed

+380/−8 lines, 6 filesdep +hydra-kerneldep ~hydra-jvm

Dependencies added: hydra-kernel

Dependency ranges changed: hydra-jvm

Files

CHANGELOG.md view
@@ -15,6 +15,145 @@  --- +## [0.17.1] - unreleased++Development version. Opens the 0.17.x line — the first release built entirely against+published 0.17.0 hosts (Hackage / PyPI / Maven Central), with the `hostOverrides` shims+that were needed while 0.16.1 lacked the effect type and I/O primitives removed.++### New features++- **Generated term-reference DSLs** ([#467](https://github.com/CategoricalData/hydra/issues/467)):+  kernel *term* modules now project generated DSL surfaces, completing the story begun with+  type-module DSLs and the generated primitive wrappers (`hydra.dsl.lib.*`, 0.17.0). A+  signature read-back in the DSL pass reuses the main pass's inference, and a demand-curated+  set of 19 kernel term modules (plus the json/test/extract modules) emits one typed,+  rename-safe reference per definition into every target language — e.g. Haskell+  `Hydra.Dsl.Strip.deannotateType`, Java `hydra.dsl.Strip.deannotateType(...)`, Python+  `hydra.dsl.strip.deannotate_type(...)`. The Java and Python coder sources were pivoted onto+  the generated references, retiring ~700 stringly-typed inline `var("hydra....")` references+  and shrinking Python's hand registry to the non-generatable remainder (byte-identical+  generated output before and after). Follow-ups:+  [#555](https://github.com/CategoricalData/hydra/issues/555) (typed references to derived+  encode/decode/show functions via `TypedName`),+  [#556](https://github.com/CategoricalData/hydra/issues/556) (non-kernel packages).+- **Package validation** ([#574](https://github.com/CategoricalData/hydra/issues/574),+  [#575](https://github.com/CategoricalData/hydra/issues/575)): a translingual `hydra.validate.*`+  framework carries per-package `ValidationProfile`s (error/warning rules, error/warning caps) and a+  catalog of checks — undeclared-package-dependency detection (now a fatal gate, #574),+  definition-name convention (PascalCase), definition ordering, documentation completeness, and+  conflicting-variant-name detection — layered on the existing core term/type well-formedness rules.+  A kernel-strict / others-relaxed policy keeps `/sync` green fleet-wide while the documentation+  backlog drains. See the new [Validation](https://github.com/CategoricalData/hydra/wiki/Validation)+  wiki page for the full per-package constraint specification.+- **DSL-authoring registration guards** ([#554](https://github.com/CategoricalData/hydra/issues/554)):+  fail-fast authoring-time validation for the host-native DSL sources — every DSL-authoring module in+  Java/`hydra-jvm`, Python, and Scala now asserts its definitions are fully registered+  (`Defs.checkComplete` / `check_complete`), backed by a Haskell kernel-source registration-completeness+  scanner, so a definition added to a source file but omitted from its `definitions` list fails loudly+  instead of silently vanishing from the generated output.+- **`hydra-build` package** ([#546](https://github.com/CategoricalData/hydra/issues/546),+  [#529](https://github.com/CategoricalData/hydra/issues/529),+  [#530](https://github.com/CategoricalData/hydra/issues/530)): the build-orchestration logic that had+  lived only in host scripts is now translingual Hydra. Pure module-list utilities became+  `hydra.build.modules` (#529) and the orphan-reconcile decision became `hydra.build.reconcile` (#530),+  each with a translingual test suite; the `hydra.build.*` modules were then extracted into a dedicated+  `hydra-build` package (#546), separate from the kernel.+- **Metadata sidecar + content/version-addressed freshness**+  ([#415](https://github.com/CategoricalData/hydra/issues/415)): dist manifests now carry a+  `moduleFormatVersion` field, and the generator's freshness basis is content/version-addressed — the+  groundwork for reproducible, cache-correct incremental generation.+- **Structured generation-provenance record**+  ([#523](https://github.com/CategoricalData/hydra/issues/523)): each generation run now records a+  structured `Generation` provenance entry (digest v2), replacing the ad-hoc shell threading of+  generation metadata.+- **Worker + issue hierarchy** ([#557](https://github.com/CategoricalData/hydra/issues/557)): a+  principled agent/worker framework — roles and parentage mirroring the GitHub issue tree, a+  cross-worktree message transport with a verify-after-copy protocol, a blocking-aware design-question+  (`decisions/`) channel, an issue-proposal queue with the `/issues` command, an autonomy-dial banner,+  and cross-machine staging coordination. Contributor-facing process only; no change to generated code.++### API and usability++- **DSL usability passes** ([#430](https://github.com/CategoricalData/hydra/issues/430),+  [#553](https://github.com/CategoricalData/hydra/issues/553)): reviewed the host-native Java, Python,+  and Scala DSL surfaces for ergonomics and cross-host parity (documented def shapes, added a Scala+  projection helper), bringing the three authoring dialects into closer alignment.++### Fixed++Bug fixes landed this cycle, grouped by area (see each issue for detail):++- **TypeScript** — reactivated `hydra.lib.*` primitive tests surfaced encoder gaps: lazy show-calling+  let bindings and compact bare-string `LiteralType` expansion in the test converter+  ([#564](https://github.com/CategoricalData/hydra/issues/564)),+  `binaryToBytes` registration + base64 binary decode in `decodeUtf8`+  ([#561](https://github.com/CategoricalData/hydra/issues/561)),+  `hydra.test.build.*` emission after the #546 split+  ([#570](https://github.com/CategoricalData/hydra/issues/570)), and `HYDRA_DEFAULT_IMPLS` support in+  the test runner ([#536](https://github.com/CategoricalData/hydra/issues/536)).+- **Tests and CI** — restored the default-primitive-implementation testing solution+  ([#388](https://github.com/CategoricalData/hydra/issues/388),+  [#549](https://github.com/CategoricalData/hydra/issues/549)), reactivated previously-ignored kernel+  test cases ([#513](https://github.com/CategoricalData/hydra/issues/513),+  [#550](https://github.com/CategoricalData/hydra/issues/550)), wired the regression harnesses into CI+  and split them out of the monolithic Haskell job for timeout headroom+  ([#535](https://github.com/CategoricalData/hydra/issues/535),+  [#563](https://github.com/CategoricalData/hydra/issues/563)), added Scala/Go/Lisp+  self-containment gates to `verify-distribution.sh`+  ([#537](https://github.com/CategoricalData/hydra/issues/537)), a manifest-JSON-tracked CI check+  ([#541](https://github.com/CategoricalData/hydra/issues/541)), a per-package test-digest freshness+  fix ([#551](https://github.com/CategoricalData/hydra/issues/551)), overlay-file restoration in+  `test-stale-output-prune.sh` ([#558](https://github.com/CategoricalData/hydra/issues/558)), and a+  sync-freshness fingerprint that now sees host-native DSL sources+  ([#562](https://github.com/CategoricalData/hydra/issues/562)).+- **Kernel and coders** — Java binary-literal signed-byte emission+  ([#528](https://github.com/CategoricalData/hydra/issues/528)), `hydra.json.Decode.fromJson`+  parametric-type (Application/Forall) support+  ([#531](https://github.com/CategoricalData/hydra/issues/531)), stale 2-param `Coder` shape in+  Java/Scala test bootstrap ([#552](https://github.com/CategoricalData/hydra/issues/552)), and a+  kernel dead-code/TODO cleanup ([#538](https://github.com/CategoricalData/hydra/issues/538)).+- **Lib and build** — `hydra.lib.files` copy/removeDirectory/status overlays across all nine hosts+  ([#525](https://github.com/CategoricalData/hydra/issues/525)), `hydra.build.*` regeneration into the+  Lisp dialects ([#544](https://github.com/CategoricalData/hydra/issues/544)), Scheme maps+  last-writer-wins in `union` ([#545](https://github.com/CategoricalData/hydra/issues/545)),+  standalone-kernel SHA dependency ([#542](https://github.com/CategoricalData/hydra/issues/542)),+  the lexicon's empty Types section ([#539](https://github.com/CategoricalData/hydra/issues/539)), and+  a duplicated generated-file header in `generate-head-haskell-build.py`+  ([#540](https://github.com/CategoricalData/hydra/issues/540)).++- **TypeScript overlay-import rewrite** ([#565](https://github.com/CategoricalData/hydra/issues/565)):+  the TypeScript coder's lib overlay-import rewrite no longer captures `hydra.lib.defaults`, which is a+  generated (not overlaid) module; excluding it fixes spurious import references in generated TypeScript.++- **Lexicon generation** (`bin/regenerate-lexicon.sh`): the generator had been failing+  silently since the encoder/decoder modules moved to in-memory synthesis+  ([#448](https://github.com/CategoricalData/hydra/issues/448)) — the script masked the+  failure and left a stale `docs/hydra-lexicon.txt` missing the four effectful primitive+  libraries (`hydra.lib.{effects,files,system,text}`). The synthesized coder modules are+  now included in the lexicon universe, the script fails loudly, and the regenerated+  lexicon covers all 267 primitives across 17 library modules.+- **`bin/prepare-release.sh`**: the non-blocking Java quality check invoked Gradle+  subprojects (`:hydra-rdf4j:test` etc.) that were removed in+  [#511](https://github.com/CategoricalData/hydra/issues/511), so it always warned without+  checking anything; it now runs the `dist/java/hydra-pg` package tests (the current home+  of the former binding tests).+- **`.gitignore`**: the ignore rules for the overlaid hand-written Haskell runtime still+  named pre-[#501](https://github.com/CategoricalData/hydra/issues/501) paths, so 41+  duplicate copies under `dist/haskell/` had become tracked; the rules now match the+  `Hydra/Overlay/` location and the duplicates are untracked.++### Documentation++- Corrected the 0.17.0 Maven coordinates throughout (Java artifacts publish under group+  `net.fortytwo.hydra.java`, Scala under `net.fortytwo.hydra.scala`,+  [#519](https://github.com/CategoricalData/hydra/issues/519)); added the npm and Scala+  release channels to the README; added TypeScript/Scala/Lisp sections to Getting started;+  repointed stale pre-#418/#501 DSL paths in the architecture docs; added GitHub issue and+  PR templates.++---+ ## [0.17.0] - 2026-07-01  Major themes: first-class support for effects and I/O in the kernel, TypeScript and
hydra-scala.cabal view
@@ -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-scala-version:        0.17.0+version:        0.17.1 synopsis:       Hydra's Scala coder: emit Scala 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". Scala support for Hydra category:       Data@@ -40,6 +40,7 @@   build-depends:       base >=4.19.0 && <4.22     , containers >=0.6.7 && <0.8-    , hydra-jvm ==0.17.0+    , hydra-jvm ==0.17.1+    , hydra-kernel ==0.17.1     , scientific >=0.3.7 && <0.4   default-language: Haskell2010
src/main/haskell/Hydra/Scala/Coder.hs view
@@ -11,6 +11,7 @@ import qualified Hydra.Coders as Coders import qualified Hydra.Constants as Constants import qualified Hydra.Core as Core+import qualified Hydra.Docs as Docs import qualified Hydra.Environment as Environment import qualified Hydra.Error.Checking as Checking import qualified Hydra.Error.Core as ErrorCore@@ -23,6 +24,7 @@ import qualified Hydra.Graph as Graph import qualified Hydra.Inference as Inference import qualified Hydra.Json.Model as Model+import qualified Hydra.Lexical as Lexical import qualified Hydra.Overlay.Haskell.Lib.Eithers as Eithers import qualified Hydra.Overlay.Haskell.Lib.Equality as Equality import qualified Hydra.Overlay.Haskell.Lib.Lists as Lists@@ -82,6 +84,27 @@           Core.applicationFunction = fterm,           Core.applicationArgument = (Core.TermVariable avar)}) +-- | Collect the set of free type variables occurring in a type (#589)+collectTypeVars :: Core.Type -> S.Set Core.Name+collectTypeVars typ = collectTypeVarsGo (Strip.deannotateType typ)++-- | Recursively collect free type variables from a type (#589)+collectTypeVarsGo :: Core.Type -> S.Set Core.Name+collectTypeVarsGo t =+    case (Strip.deannotateType t) of+      Core.TypeVariable v0 -> Sets.singleton v0+      Core.TypeFunction v0 -> Sets.union (collectTypeVarsGo (Strip.deannotateType (Core.functionTypeDomain v0))) (collectTypeVarsGo (Strip.deannotateType (Core.functionTypeCodomain v0)))+      Core.TypeApplication v0 -> Sets.union (collectTypeVarsGo (Strip.deannotateType (Core.applicationTypeFunction v0))) (collectTypeVarsGo (Strip.deannotateType (Core.applicationTypeArgument v0)))+      Core.TypeList v0 -> collectTypeVarsGo (Strip.deannotateType v0)+      Core.TypeSet v0 -> collectTypeVarsGo (Strip.deannotateType v0)+      Core.TypeOptional v0 -> collectTypeVarsGo (Strip.deannotateType v0)+      Core.TypeEffect v0 -> collectTypeVarsGo (Strip.deannotateType v0)+      Core.TypeMap v0 -> Sets.union (collectTypeVarsGo (Strip.deannotateType (Core.mapTypeKeys v0))) (collectTypeVarsGo (Strip.deannotateType (Core.mapTypeValues v0)))+      Core.TypePair v0 -> Sets.union (collectTypeVarsGo (Strip.deannotateType (Core.pairTypeFirst v0))) (collectTypeVarsGo (Strip.deannotateType (Core.pairTypeSecond v0)))+      Core.TypeEither v0 -> Sets.union (collectTypeVarsGo (Strip.deannotateType (Core.eitherTypeLeft v0))) (collectTypeVarsGo (Strip.deannotateType (Core.eitherTypeRight v0)))+      Core.TypeForall v0 -> collectTypeVarsGo (Strip.deannotateType (Core.forallTypeBody v0))+      _ -> Sets.empty+ -- | Construct a Scala package from a Hydra module and its definitions constructModule :: t0 -> Graph.Graph -> Packaging.Module -> [Packaging.Definition] -> Either Errors.Error Syntax.Pkg constructModule cx g mod defs =@@ -102,6 +125,74 @@           typeDeclStats,           termDeclStats])}))))) +-- | Filter/rewrite a raw type-application list against callee-scheme over-generalization (#589)+correctTypeApps :: Core.Name -> [Core.Type] -> Graph.Graph -> Either t0 [Core.Type]+correctTypeApps name fallbackTypeApps g =+    Optionals.cases (Lexical.lookupBinding g name) (Right fallbackTypeApps) (\el -> Optionals.cases (Core.bindingTypeScheme el) (Right fallbackTypeApps) (\ts ->+      let schemeType = Core.typeSchemeBody ts+          allSchemeVars = Lists.filter (\vv -> isSimpleName vv) (Core.typeSchemeVariables ts)+          schemeTypeVars = collectTypeVars schemeType+          usedFlags = Lists.map (\vv -> Sets.member vv schemeTypeVars) allSchemeVars+          usedSchemeVars = filterByFlags allSchemeVars usedFlags+          nParams = countFunctionParams schemeType+          peeled = peelDomainTypes nParams schemeType+          calleeDoms = Pairs.first peeled+          calleeCod = Pairs.second peeled+          overgenSubst = detectAccumulatorUnification calleeDoms calleeCod usedSchemeVars+          keepFlags =+                  Lists.map (\vv -> Logic.and (Sets.member vv schemeTypeVars) (Logic.not (Maps.member vv overgenSubst))) allSchemeVars+          schemeVars = filterByFlags allSchemeVars keepFlags+          filteredFallback0 =+                  Logic.ifElse (Equality.equal (Lists.length allSchemeVars) (Lists.length fallbackTypeApps)) (filterByFlags fallbackTypeApps keepFlags) fallbackTypeApps+          filteredFallback =+                  Logic.ifElse (Maps.null overgenSubst) filteredFallback0 (Lists.map (\t -> substituteTypeVarsWithTypes overgenSubst t) filteredFallback0)+      in (Right filteredFallback)))++-- | Count the curried function parameters of a type (#589)+countFunctionParams :: Core.Type -> Int+countFunctionParams t =+    case (Strip.deannotateType t) of+      Core.TypeFunction v0 -> Math.add 1 (countFunctionParams (Core.functionTypeCodomain v0))+      _ -> 0++-- | Detect callee-scheme type vars forced together/to-concrete by the callee's own domain shape (#589)+detectAccumulatorUnification :: [Core.Type] -> Core.Type -> [Core.Name] -> M.Map Core.Name Core.Type+detectAccumulatorUnification doms cod tparams =++      let tparamSet = Sets.fromList tparams+          allPairs = Lists.bind doms (\d -> extractInOutPair d)+          groupedByInput = groupPairsByFirst allPairs+          selfRefSubst = selfRefSubstitution groupedByInput+          directPairs = Lists.bind doms (\d -> extractDirectReturn tparamSet d)+          groupedDirect = groupPairsByFirst directPairs+          directInputVars = Sets.fromList (Lists.map (\p -> Pairs.first p) directPairs)+          codVar =+                  case (Strip.deannotateType cod) of+                    Core.TypeVariable v0 -> Just v0+                    _ -> Nothing+          directRefSubst = directRefSubstitution directInputVars codVar groupedDirect+          codSubst =+                  Optionals.cases (findPairFirst cod) Maps.empty (\cv -> Logic.ifElse (Maps.member cv selfRefSubst) Maps.empty (Optionals.cases (findSelfRefVar groupedByInput) Maps.empty (\refVar -> Logic.ifElse (Equality.equal cv refVar) Maps.empty (Maps.singleton cv refVar))))+          domVars = Sets.fromList (Lists.bind doms (\d -> Sets.toList (collectTypeVars d)))+          danglingSubst =+                  Optionals.cases (findPairFirst cod) Maps.empty (\cv -> Logic.ifElse (Sets.member cv domVars) Maps.empty (Optionals.cases (findSelfRefVar groupedByInput) Maps.empty (\refVar -> Maps.singleton cv (Core.TypeVariable refVar))))+      in (Maps.union (Maps.union (Maps.union (nameMapToTypeMap selfRefSubst) (nameMapToTypeMap codSubst)) danglingSubst) (nameMapToTypeMap directRefSubst))++-- | Compute the direct-return substitution over grouped accumulator pairs (#589)+directRefSubstitution :: (Eq t0, Ord t0) => (S.Set t0 -> Maybe t0 -> M.Map t0 [t0] -> M.Map t0 t0)+directRefSubstitution directInputVars codVar grouped =+    Lists.foldl (\subst -> \entry -> directRefSubstitutionProcessGroup directInputVars codVar subst (Pairs.first entry) (Pairs.second entry)) Maps.empty (Maps.toList grouped)++-- | Unify safe co-occurring vars onto inVar for the direct-return accumulator pattern (#589)+directRefSubstitutionProcessGroup :: (Eq t0, Ord t0) => (S.Set t0 -> Maybe t0 -> M.Map t0 t0 -> t0 -> [t0] -> M.Map t0 t0)+directRefSubstitutionProcessGroup directInputVars codVar subst inVar outVars =++      let selfRefCount = Lists.length (Lists.filter (\vv -> Equality.equal vv inVar) outVars)+          nonSelfVars = Lists.filter (\vv -> Logic.not (Equality.equal vv inVar)) outVars+          safeNonSelfVars =+                  Lists.filter (\vv -> Logic.and (Logic.not (Sets.member vv directInputVars)) (Logic.not (Equality.equal (Just vv) codVar))) nonSelfVars+      in (Logic.ifElse (Logic.and (Equality.gte selfRefCount 2) (Logic.not (Lists.null safeNonSelfVars))) (Lists.foldl (\s -> \vv -> Maps.insert vv inVar s) subst safeNonSelfVars) subst)+ -- | Drop N domain types from a function type, returning the remaining type dropDomains :: Int -> Core.Type -> Core.Type dropDomains n t =@@ -173,7 +264,7 @@           cod = dropDomains paramCount typ           zippedParams = Lists.zip (Lists.take paramCount paramNames) (Lists.take paramCount doms)           freeTypeVars =-                  Lists.filter (\v -> Logic.not (Lists.elem 46 (Strings.toList (Core.unName v)))) (Sets.toList (Variables.freeVariablesInType typ))+                  Lists.nub (Lists.filter (\v -> Logic.not (Lists.elem 46 (Strings.toList (Core.unName v)))) (Variables.freeVariablesInTypeOrdered typ))           tparams = Lists.map (\tv -> Utils.stparam tv) freeTypeVars           letBindings = extractLetBindings term           gWithTypeVars =@@ -385,7 +476,7 @@             Core.TermProject _ -> encodeTerm cx g substitutedBody             Core.TermCases _ -> encodeTerm cx g substitutedBody             Core.TermUnwrap _ -> encodeTerm cx g substitutedBody-            Core.TermVariable v1 -> Eithers.bind (Eithers.mapList (\targ -> encodeType cx g targ) typeArgs) (\stypeArgs -> Optionals.cases (Maps.lookup v1 (Graph.graphPrimitives g)) (Eithers.bind (encodeTerm cx g substitutedBody) (\svar -> Right (Utils.sapplyTypes svar stypeArgs))) (\_prim -> Right (Utils.sapplyTypes (Utils.sprim v1) stypeArgs)))+            Core.TermVariable v1 -> Eithers.bind (correctTypeApps v1 typeArgs g) (\correctedTypeArgs -> Eithers.bind (Eithers.mapList (\targ -> encodeType cx g targ) correctedTypeArgs) (\stypeArgs -> Optionals.cases (Maps.lookup v1 (Graph.graphPrimitives g)) (Eithers.bind (encodeTerm cx g substitutedBody) (\svar -> Right (Utils.sapplyTypes svar stypeArgs))) (\_prim -> Right (Utils.sapplyTypes (Utils.sprim v1) stypeArgs))))             _ -> encodeTerm cx g substitutedBody         Core.TermTypeLambda v0 -> encodeTerm cx (Scoping.extendGraphForTypeLambda g v0) (Core.typeLambdaBody v0)         Core.TermApplication v0 ->@@ -786,6 +877,33 @@       Core.TypeForall v0 -> extractCodomain (Core.forallTypeBody v0)       _ -> t +-- | Extract direct-return (input-var, output-var) pairs from a domain type (#589)+extractDirectReturn :: S.Set Core.Name -> Core.Type -> [(Core.Name, Core.Name)]+extractDirectReturn tparamSet t = extractDirectReturnGo tparamSet t++-- | Recursive worker for extractDirectReturn (#589)+extractDirectReturnGo :: S.Set Core.Name -> Core.Type -> [(Core.Name, Core.Name)]+extractDirectReturnGo tparamSet t =+    case (Strip.deannotateType t) of+      Core.TypeFunction v0 ->+        let dom = Strip.deannotateType (Core.functionTypeDomain v0)+            cod = Core.functionTypeCodomain v0+        in case dom of+          Core.TypeVariable v1 -> Logic.ifElse (Sets.member v1 tparamSet) (case (Strip.deannotateType cod) of+            Core.TypeFunction v2 ->+              let midArg = Strip.deannotateType (Core.functionTypeDomain v2)+                  retPart = Strip.deannotateType (Core.functionTypeCodomain v2)+              in case midArg of+                Core.TypeVariable v3 -> Logic.ifElse (Sets.member v3 tparamSet) [] (case retPart of+                  Core.TypeVariable v4 -> Logic.ifElse (Sets.member v4 tparamSet) (Lists.singleton (v1, v4)) []+                  _ -> [])+                _ -> case retPart of+                  Core.TypeVariable v3 -> Logic.ifElse (Sets.member v3 tparamSet) (Lists.singleton (v1, v3)) []+                  _ -> []+            _ -> []) (extractDirectReturnGo tparamSet cod)+          _ -> extractDirectReturnGo tparamSet cod+      _ -> []+ -- | Extract domain types from a function type extractDomains :: Core.Type -> [Core.Type] extractDomains t =@@ -794,6 +912,21 @@       Core.TypeForall v0 -> extractDomains (Core.forallTypeBody v0)       _ -> [] +-- | Extract an (input-var, output-var) accumulator pair from a domain type (#589)+extractInOutPair :: Core.Type -> [(Core.Name, Core.Name)]+extractInOutPair t =+    case (Strip.deannotateType t) of+      Core.TypeFunction v0 -> case (Strip.deannotateType (Core.functionTypeDomain v0)) of+        Core.TypeVariable v1 ->+          let retType = unwrapReturnType (Core.functionTypeCodomain v0)+          in case (Strip.deannotateType retType) of+            Core.TypePair v2 -> case (Strip.deannotateType (Core.pairTypeFirst v2)) of+              Core.TypeVariable v3 -> Lists.singleton (v1, v3)+              _ -> []+            _ -> []+        _ -> []+      _ -> []+ -- | Extract let bindings from a term extractLetBindings :: Core.Term -> [Core.Binding] extractLetBindings t =@@ -865,6 +998,10 @@         Syntax.paramDataDecltpe = (Just sftyp),         Syntax.paramDataDefault = Nothing}))) +-- | Keep elements of xs whose corresponding flag is true (#589)+filterByFlags :: [t0] -> [Bool] -> [t0]+filterByFlags xs flags = Lists.map (\p -> Pairs.first p) (Lists.filter (\p -> Pairs.second p) (Lists.zip xs flags))+ -- | Find the domain type from annotations findDomain :: t0 -> Graph.Graph -> M.Map Core.Name Core.Term -> Either Errors.Error Core.Type findDomain cx g meta =@@ -879,6 +1016,15 @@       Lists.map toElImport (Sets.toList elImps),       (Lists.map toPrimImport (Sets.toList primImps))]))) +-- | If t is Pair(var, _), return the first component's variable name (#589)+findPairFirst :: Core.Type -> Maybe Core.Name+findPairFirst t =+    case (Strip.deannotateType t) of+      Core.TypePair v0 -> case (Strip.deannotateType (Core.pairTypeFirst v0)) of+        Core.TypeVariable v1 -> Just v1+        _ -> Nothing+      _ -> Nothing+ -- | Find the Scala domain type for a function from annotations findSdom :: t0 -> Graph.Graph -> M.Map Core.Name Core.Term -> Either Errors.Error (Maybe Syntax.Type) findSdom cx g meta =@@ -893,6 +1039,25 @@         _ -> Right Nothing       _ -> Eithers.bind (encodeType cx g t) (\st -> Right (Just st)))) +-- | Find a type variable that is its own accumulator output, if any (#589)+findSelfRefVar :: (Eq t0, Ord t0) => (M.Map t0 [t0] -> Maybe t0)+findSelfRefVar grouped =++      let selfRefs = Lists.filter (\entry -> Lists.elem (Pairs.first entry) (Pairs.second entry)) (Maps.toList grouped)+      in (Optionals.map (\entry -> Pairs.first entry) (Lists.maybeHead selfRefs))++-- | Group a list of pairs into a map keyed by first component (#589)+groupPairsByFirst :: Ord t0 => ([(t0, t1)] -> M.Map t0 [t1])+groupPairsByFirst pairs =+    Lists.foldl (\m -> \p ->+      let k = Pairs.first p+          vv = Pairs.second p+      in (Maps.alter (\mv -> Optionals.cases mv (Just (Lists.singleton vv)) (\vs -> Just (Lists.concat2 vs (Lists.singleton vv)))) k m)) Maps.empty pairs++-- | True if a type-variable name has no namespace qualifier (#589)+isSimpleName :: Core.Name -> Bool+isSimpleName name = Equality.equal (Lists.length (Strings.splitOn "." (Core.unName name))) 1+ -- | Convert a Hydra module to Scala source code moduleToScala :: Packaging.Module -> [Packaging.Definition] -> t0 -> Graph.Graph -> Either Errors.Error (M.Map String String) moduleToScala mod defs cx g =@@ -900,6 +1065,29 @@       let s = Serialization.printExpr (Serialization.parenthesize (Serde.pkgToExpr pkg))       in (Right (Maps.singleton (Names.moduleNameToFilePath Util.CaseConventionCamel (File.FileExtension "scala") (Packaging.moduleName mod)) s))) +-- | Lift a Name->Name substitution map to a Name->Type map (#589)+nameMapToTypeMap :: Ord t0 => (M.Map t0 Core.Name -> M.Map t0 Core.Type)+nameMapToTypeMap m = Maps.map (\vv -> Core.TypeVariable vv) m++-- | Peel up to n curried domain types off a function type (#589)+peelDomainTypes :: Int -> Core.Type -> ([Core.Type], Core.Type)+peelDomainTypes n t =+    Logic.ifElse (Equality.lte n 0) ([], t) (case (Strip.deannotateType t) of+      Core.TypeFunction v0 ->+        let rest = peelDomainTypes (Math.sub n 1) (Core.functionTypeCodomain v0)+        in (Lists.cons (Core.functionTypeDomain v0) (Pairs.first rest), (Pairs.second rest))+      _ -> ([], t))++-- | Compute the self-reference substitution over grouped accumulator pairs (#589)+selfRefSubstitution :: (Eq t0, Ord t0) => (M.Map t0 [t0] -> M.Map t0 t0)+selfRefSubstitution grouped =+    Lists.foldl (\subst -> \entry -> selfRefSubstitutionProcessGroup subst (Pairs.first entry) (Pairs.second entry)) Maps.empty (Maps.toList grouped)++-- | Unify every co-occurring var onto inVar when inVar is its own accumulator output (#589)+selfRefSubstitutionProcessGroup :: (Eq t0, Ord t0) => (M.Map t0 t0 -> t0 -> [t0] -> M.Map t0 t0)+selfRefSubstitutionProcessGroup subst inVar outVars =+    Logic.ifElse (Lists.elem inVar outVars) (Lists.foldl (\s -> \vv -> Logic.ifElse (Equality.equal vv inVar) s (Maps.insert vv inVar s)) subst outVars) subst+ -- | Strip wrap eliminations from terms (newtypes are erased in Scala) stripWrapEliminations :: Core.Term -> Core.Term stripWrapEliminations t =@@ -920,6 +1108,39 @@           _ -> t       _ -> t +-- | Substitute type variables in t per subst (#589)+substituteTypeVarsWithTypes :: M.Map Core.Name Core.Type -> Core.Type -> Core.Type+substituteTypeVarsWithTypes subst t = substituteTypeVarsWithTypesGo subst (Strip.deannotateType t)++-- | Recursive worker for substituteTypeVarsWithTypes (#589)+substituteTypeVarsWithTypesGo :: M.Map Core.Name Core.Type -> Core.Type -> Core.Type+substituteTypeVarsWithTypesGo subst t =+    case (Strip.deannotateType t) of+      Core.TypeVariable v0 -> Optionals.cases (Maps.lookup v0 subst) t (\rep -> rep)+      Core.TypeFunction v0 -> Core.TypeFunction (Core.FunctionType {+        Core.functionTypeDomain = (substituteTypeVarsWithTypesGo subst (Core.functionTypeDomain v0)),+        Core.functionTypeCodomain = (substituteTypeVarsWithTypesGo subst (Core.functionTypeCodomain v0))})+      Core.TypeApplication v0 -> Core.TypeApplication (Core.ApplicationType {+        Core.applicationTypeFunction = (substituteTypeVarsWithTypesGo subst (Core.applicationTypeFunction v0)),+        Core.applicationTypeArgument = (substituteTypeVarsWithTypesGo subst (Core.applicationTypeArgument v0))})+      Core.TypeList v0 -> Core.TypeList (substituteTypeVarsWithTypesGo subst v0)+      Core.TypeSet v0 -> Core.TypeSet (substituteTypeVarsWithTypesGo subst v0)+      Core.TypeOptional v0 -> Core.TypeOptional (substituteTypeVarsWithTypesGo subst v0)+      Core.TypeEffect v0 -> Core.TypeEffect (substituteTypeVarsWithTypesGo subst v0)+      Core.TypeMap v0 -> Core.TypeMap (Core.MapType {+        Core.mapTypeKeys = (substituteTypeVarsWithTypesGo subst (Core.mapTypeKeys v0)),+        Core.mapTypeValues = (substituteTypeVarsWithTypesGo subst (Core.mapTypeValues v0))})+      Core.TypePair v0 -> Core.TypePair (Core.PairType {+        Core.pairTypeFirst = (substituteTypeVarsWithTypesGo subst (Core.pairTypeFirst v0)),+        Core.pairTypeSecond = (substituteTypeVarsWithTypesGo subst (Core.pairTypeSecond v0))})+      Core.TypeEither v0 -> Core.TypeEither (Core.EitherType {+        Core.eitherTypeLeft = (substituteTypeVarsWithTypesGo subst (Core.eitherTypeLeft v0)),+        Core.eitherTypeRight = (substituteTypeVarsWithTypesGo subst (Core.eitherTypeRight v0))})+      Core.TypeForall v0 -> Core.TypeForall (Core.ForallType {+        Core.forallTypeParameter = (Core.forallTypeParameter v0),+        Core.forallTypeBody = (substituteTypeVarsWithTypesGo subst (Core.forallTypeBody v0))})+      _ -> t+ -- | Create an element import statement toElImport :: Packaging.ModuleName -> Syntax.Stat toElImport ns =@@ -953,3 +1174,11 @@       in (Syntax.TypeVar (Syntax.VarType {         Syntax.varTypeName = Syntax.NameType {           Syntax.nameTypeValue = s}}))++-- | Unwrap a (possibly curried/applied) type down to its ultimate return type (#589)+unwrapReturnType :: Core.Type -> Core.Type+unwrapReturnType t =+    case (Strip.deannotateType t) of+      Core.TypeFunction v0 -> unwrapReturnType (Core.functionTypeCodomain v0)+      Core.TypeApplication v0 -> unwrapReturnType (Core.applicationTypeArgument v0)+      _ -> t
src/main/haskell/Hydra/Scala/Language.hs view
@@ -7,6 +7,7 @@ import qualified Hydra.Ast as Ast import qualified Hydra.Coders as Coders import qualified Hydra.Core as Core+import qualified Hydra.Docs as Docs import qualified Hydra.Error.Checking as Checking import qualified Hydra.Error.Core as ErrorCore import qualified Hydra.Error.File as ErrorFile
src/main/haskell/Hydra/Scala/Serde.hs view
@@ -7,6 +7,7 @@ import qualified Hydra.Ast as Ast import qualified Hydra.Coders as Coders import qualified Hydra.Core as Core+import qualified Hydra.Docs as Docs import qualified Hydra.Error.Checking as Checking import qualified Hydra.Error.Core as ErrorCore import qualified Hydra.Error.File as ErrorFile
src/main/haskell/Hydra/Scala/Utils.hs view
@@ -7,6 +7,7 @@ import qualified Hydra.Ast as Ast import qualified Hydra.Coders as Coders import qualified Hydra.Core as Core+import qualified Hydra.Docs as Docs import qualified Hydra.Error.Checking as Checking import qualified Hydra.Error.Core as ErrorCore import qualified Hydra.Error.File as ErrorFile@@ -67,10 +68,10 @@       Syntax.applyDataFun = fun,       Syntax.applyDataArgs = args}) --- | Apply explicit type parameters to a Scala expression (e.g. f[A, B])+-- | Apply explicit type parameters to a Scala expression (e.g. f[A, B]); a no-op for an empty type-arg list (#589) sapplyTypes :: Syntax.Data -> [Syntax.Type] -> Syntax.Data sapplyTypes fun typeArgs =-+    Logic.ifElse (Lists.null typeArgs) fun (       let typeToStr = \t -> typeToString t           typeStrings = Lists.map typeToStr typeArgs           typeArgStr =@@ -94,7 +95,7 @@               Syntax.selectDataName = Syntax.NameData {                 Syntax.nameDataValue = (Syntax.PredefString (Strings.cat2 rawName typeArgStr))}})))           _ -> fun-        _ -> fun+        _ -> fun)  -- | Create a Scala assignment expression sassign :: Syntax.Data -> Syntax.Data -> Syntax.Data