Paul King created GROOVY-12485:
----------------------------------
Summary: Recognise Zazr control carriers by name in the monadic
carrier allow-list
Key: GROOVY-12485
URL: https://issues.apache.org/jira/browse/GROOVY-12485
Project: Groovy
Issue Type: Bug
Components: groovy-macro-library, groovy-typecheckers, Static Type
Checker
Affects Versions: 6.0.0
Reporter: Paul King
h2. Background
GEP-23 ships a standard carrier allow-list
({{org.apache.groovy.runtime.MonadicCarrierRegistry}}) shared by the {{DO}}
runtime dispatcher, {{MonadicChecker}} and {{MonadicShapeChecker}}. Third-party
libraries Groovy must not
depend on are listed by fully-qualified name: Functional Java ({{fj.data.*}},
{{fj.P1}}) and Vavr (ry/Either/Validation}}).
[Zazr|https://zazr.dev/] ({{dev.zazr:zazr-core}}, Apache 2.0, JDK 25+, no
runtime dependencies) is er {{dev.zazr}} with the same four control carriers
plus {{Lazy}}. All of them exposesingle-argument {{flatMap}}/{{map}}, so they
already participate in {{DO}} via the structural path. However, structural
participation is deliberately *untrusted*:
{{MonadicChecker.trustedCarrierName}} returns {{null}} for it,
and {{MonadicShapeChecker.carrierFor}} only consults the registry and
{{@Monadic}}. The result is tguarantees than the equivalent Vavr code.
h2. Observed behaviour (Groovy 6.0.0, zazr-core 0.4.0, JDK 25)
||Scenario||Zazr carrier||Same shape over Optional/Stream||
|{{DO}} over Option/Try/Either/Validation/Lazy under
{{MonadicChecker}}|compiles and runs|compiles and runs|
|{{MonadicShapeChecker}} on {{Option.some(1).map \{ Option.some(it)
\}}}|accepted silently, yields error: "did you mean 'flatMap'?"|
|Mixed carriers in one {{DO}} under {{MonadicChecker}}|compiles, then
{{GroovyCastException}} at runtime|compile error: "Mixing carriers in a
comprehension is not supported"|
Reproducer for the mixed-carrier case:
{code:groovy}
import dev.zazr.control.*
import groovy.transform.TypeChecked
import static org.apache.groovy.macrolib.MacroLibGroovyMethods.DO
@TypeChecked(extensions = 'groovy.typecheckers.MonadicChecker')
def mixed() {
DO(x in Option.some(1), y in Try.success(2)) { Option.some(x + y) } //
should be rejected at
}
{code}
Reproducer for the shape-lint gap:
{code:groovy}
import dev.zazr.control.Option
import groovy.transform.TypeChecked
@TypeChecked(extensions = 'groovy.typecheckers.MonadicShapeChecker')
def nested() { Option.some(1).map { Option.some(it) } } // M<M<T>>, no
diagnostic today
{code}
h2. Proposed change
Add name-keyed rows to {{MonadicCarrierRegistry.NAMED_ENTRIES}}, using the
structural {{flatMap}}/{ as the Vavr rows do:
{code:java}
// Zazr (dev.zazr) — recognised by name; no dependency. Structural flatMap/map,
// listed so the carriers are trusted by MonadicChecker and linted by
MonadicShapeChecker.
new NamedEntry("dev.zazr.control.Option", "flatMap", "map"),
new NamedEntry("dev.zazr.control.Either", "flatMap", "map"),
new NamedEntry("dev.zazr.control.Try", "flatMap", "map"),
new NamedEntry("dev.zazr.control.Validation", "flatMap", "map"),
new NamedEntry("dev.zazr.Lazy", "flatMap", "map")
{code}
No new dependency and no behavioural change for any existing carrier. Because
matching walks the tynested record cases ({{Option.Some}}, {{Try.Failure}},
{{Validation.Invalid}}, ...) resolve to their sealed parent as they do for
{{fj.data.Some}} today.
h2. Scope of work
* {{MonadicCarrierRegistry}}: add the entries above and extend the class
Javadoc.
* Tests: a {{ZazrCarrierTest}} in groovy-macro-library mirroring
{{FunctionalJavaCarrierTest}}, usir {{dev.zazr.control}} (same pattern as the
existing {{fj/data/Option.groovy}} stub) so the buildtakes no dependency.
Cover: {{DO}} dispatch, {{MonadicChecker}} rejecting a mixed Zazr
comprehension, {{MonadicShapeChecker}} reporting the three shape mistakes on a
Zazr chain.
* Docs: {{_monadic-comprehensions.adoc}} allow-list paragraph; GEP-23
allow-list and version historvy 6.0 release notes paragraph listing recognised
libraries (if backported).
h2. Notes
* Zazr's {{Validation.flatMap}} short-circuits (its Javadoc says so;
accumulation is via {{zip}}/{{ so {{DO}} semantics over it match Vavr and
Functional Java {{Validation}}: dependent steps, firstfailure wins.
* Zazr's collections ({{Vector}},
{{List}}, {{LazyList}}) also have structural {{flatMap}}/{{map}};ft out,
matching the treatment of {{io.vavr.collection.*}}.
* The user-configurable registration channel deferred to 7.0 by GEP-23 is the
general fix for libraries Groovy does not know about; until it exists,
extending the core allow-list is the only route for a type owned by another
project.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)