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 +139/−0
- hydra-scala.cabal +4/−3
- src/main/haskell/Hydra/Scala/Coder.hs +231/−2
- src/main/haskell/Hydra/Scala/Language.hs +1/−0
- src/main/haskell/Hydra/Scala/Serde.hs +1/−0
- src/main/haskell/Hydra/Scala/Utils.hs +4/−3
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