From john.r.rose at oracle.com Tue Nov 2 03:30:07 2021 From: john.r.rose at oracle.com (John Rose) Date: Tue, 2 Nov 2021 03:30:07 +0000 Subject: Effectively final loop counter (was: Are templated string embedded expressions "method parameters" or "lambdas"?) In-Reply-To: References: <2F2E1E93-2B84-434E-B446-672270048C0A@oracle.com> <6c2e73bb-7fde-e1a0-d36c-0c8caf13d7f0@oracle.com> Message-ID: On Oct 30, 2021, at 9:30 PM, Tagir Valeev > wrote: This is a somewhat separate topic but I would be glad to see some improvement here with respect to good old for loops, as I often saw copying to a new variable for the sake of lambda capture inside the loop. Like if a variable is declared at the for loop initializer and it's never modified inside the `for` loop body, then let's assume that before body entry, a fresh variable is created with the same name and assigned to the original variable shadowing the original variable. This way, the counter variable of the classic counting loop will be considered as effectively final inside the loop body (but not inside the condition and update expressions). This will make for (int i=0; i for (int i = 0; i < a.length; i++) { __Synthetic final var i$final = i; foo(i); } Oddities: - The split name i$inner shows up in the debugger, maybe. - Or else there are confusingly duplicate declarations in the debugger. - You can only get lambda-capture of ?i? in the loop body proper, not in the loop header. - The transform applies to individual variables; with ?for (int i,j?)?? the transform may be applied independently to i and j. This design admits an ?effectively final? transformation, which would get what you are requesting directly. for (int i = 0; i < a.length; i++) { foo( () -> i ); } =effectively final=> for (final int i = 0; i < a.length; i++) { foo( () -> i ); } =desugar=> for (final int i = 0; i < a.length; i++) { __Synthetic final var i$final = i; foo( () -> i$final ); } This can be made to work for multiple variables also, and has reasonable effects, although it stretches for-loops a bit. for (final int i = 0, j = a.length; i < j; i++) { foo( () -> i ); } =desugar=> for ({ int i = 0; final int j = a.length; }; i < j; i++) { __Synthetic final var i$final = i; foo( () -> i$final ); } OR ELSE (not as good): for (int i = 0, j = a.length; i < j; i++) { __Synthetic final var i$final = i; __Synthetic final var j$final = j; //unused foo( () -> i$final ); } The latter approach is not as good because, although it makes for a simpler desugaring, it makes the de-finaling of i ?viral? to j. The change of j in the loop header, to a mysteriously non-final variable, could be surprising. By contrast, the appearance of ?i++? is unambiguously and aggressively clear, that the programmer intends to change i, even at the same time as declaring the programmer?s desire for a final i. This is usually a contradiction, but can be interpreted usefully and consistently in the setting of a loop: It?s final *only in each iteration of the body*. From john.r.rose at oracle.com Tue Nov 2 03:37:02 2021 From: john.r.rose at oracle.com (John Rose) Date: Tue, 2 Nov 2021 03:37:02 +0000 Subject: [External] : Effectively final loop counter (was: Are templated string embedded expressions "method parameters" or "lambdas"?) In-Reply-To: <89d60b81-fe2a-815e-24b6-37110e908513@oracle.com> References: <2F2E1E93-2B84-434E-B446-672270048C0A@oracle.com> <6c2e73bb-7fde-e1a0-d36c-0c8caf13d7f0@oracle.com> <89d60b81-fe2a-815e-24b6-37110e908513@oracle.com> Message-ID: On Oct 31, 2021, at 6:47 AM, Brian Goetz wrote: > > >> This is a somewhat separate topic but I would be glad to see some >> improvement here with respect to good old for loops, as I often saw >> copying to a new variable for the sake of lambda capture inside the >> loop. > > As a separate separate topic, I'd like to make good-old-for-loops more of a thing of the past. > > For example, there's a reasonable stacking of features atop Valhalla that gets us to: > > for (int i : 0.. > with no performance overhead (e.g., no Iterator creation.) > > My point is not to dive into the stack of features that get us there, but to point out that the "enhanced" for loop, which is currently extremely limited, has some legs to be generalized to the point where old-school for loops might look more quaint. So I'd rather spend energy on that, rather than rehabilitating the borrowed-from-C for loop. That too. My previous message is my best take for the cheapest way to inject a little more life and relevance into old-for. Another way to breath life into old-for would be to allow non-final variables to be shadowed by final variables of the same name and value, with a very restricted redeclaration of that name: for (int i = 0; i < a.length; i++) { final i; foo( () -> i ); } =desugar=> for (int i = 0; i < a.length; i++) { __Synthetic final var i$final = i; foo( () -> i$final ); } In this case, the magic declaration ?final var i = i? switches i from mutable in the header to non-mutable in the body. Such magic is not specific to loops but can be used in other places also, where mutable bindings don?t conform to ?effectively final? rules, but where at a particular point all downstream uses are in fact frozen. From john.r.rose at oracle.com Tue Nov 2 03:39:17 2021 From: john.r.rose at oracle.com (John Rose) Date: Tue, 2 Nov 2021 03:39:17 +0000 Subject: [External] : Effectively final loop counter (was: Are templated string embedded expressions "method parameters" or "lambdas"?) In-Reply-To: References: <2F2E1E93-2B84-434E-B446-672270048C0A@oracle.com> <6c2e73bb-7fde-e1a0-d36c-0c8caf13d7f0@oracle.com> <89d60b81-fe2a-815e-24b6-37110e908513@oracle.com> Message-ID: On Nov 1, 2021, at 8:37 PM, John Rose > wrote: In this case, the magic declaration ?final var i = i? switches (There are two bikeshed colors shown here; it is very simply ?final i? in the desugaring example. ?final i? is very clearly a super-narrow feature, another option to use alongside the effectively-final rule.) From john.r.rose at oracle.com Tue Nov 2 06:40:46 2021 From: john.r.rose at oracle.com (John Rose) Date: Tue, 2 Nov 2021 06:40:46 +0000 Subject: Are templated string embedded expressions "method parameters" or "lambdas"? In-Reply-To: References: <2F2E1E93-2B84-434E-B446-672270048C0A@oracle.com> <6c2e73bb-7fde-e1a0-d36c-0c8caf13d7f0@oracle.com> <635336FF-72B7-4745-B708-203A4A281437@oracle.com> <8EA56F3B-9615-471F-8C79-C84952B5FBE9@oracle.com> Message-ID: <9DA1812F-398B-4E7B-9D80-8E084F809FEF@oracle.com> On Oct 31, 2021, at 8:52 AM, Brian Goetz > wrote: I suggest that, until we roll out more of the machinery we intend to roll out, such as type classes, that we restrict the operand x (the receiver LHS of the S.T.) to be a statically constant expression. I think this is taking it way too far. TL;DR: I?m trying to avoid taking us too far in a different direction: Into unproven mechanisms for bootstrapping static validation from dynamic method calls. I?m trying to get us to use Java?s pre-existing strengths, which include a useful place for link-time validation, encoded via indy. *If* the receiver is a statically constant expression, *then* it should be possible to get better type checking / translation. I?m pointing out a different proposition: *If* you need static validation of a string, *then* you need something constant about the receiver. By constant I mean, very specifically, ?fixed no later than just before the first execution of of the string template expression?. The ?something constant? might be the type (some subtype of a known ?ST-Handler?), or it might be a constant value (e.g., a static final field value). So, is it to be the type alone, or a constant receiver reference (with its own very specific type as well)? I would prefer either choice over the current proposal which uses an unreliable MCS under the covers. Or, a third choice (your plan of record, IIUC) is to attempt harvest the ?something constant? from the *dynamic* receiver and arguments of the *temporally first* execution of the string template, and stash that somewhere, hoping it does not change. This has the simplicity of having just one execution, but the new (and IMO objectionable) complexity of trying to recover the ?something constant? needed for validation from a mishmash of dynamic information, filtered through a MCS, with unclear results when the ?something constant? changes, and a murky optimization story for how to perform continual revalidation to detect dynamic receiver and arguments that turn out to be inconsistent with the chosen ?something constant?. That won?t optimize well, compared with a clear phase1/phase2 process where phase1 is run exactly once (via indy BSM) and phase2 is a method call with constant-folded configuration information from phase1. (To be clear, this one-time validation hook I?m talking about is also what?s needed for self-configuration to feed something more efficient to the downstream computation; the Pattern.compile example shows this, and is typical of the cases I think are important.) In a nutshell, one-time validation must be driven by one of 1. just the static type of the receiver, 2. the dynamic type of a link-time evaluated receiver, or 3. some dynamically testable aspect of a run-time evaluated receiver (which entails an N-time cost to maintain validation). In a slightly larger nutshell, one-time validation/optimization/configuration requires one of the following, in the design space we are discussing: 1. validation driven by (only) the static type of the receiver, 2. validation driven by the dynamic type of the receiver, which is subject to an evaluate-once rule (via indy BSM, a link-time execution), 3. validation driven by the dynamic type, identity, or other state in the receiver, which may change, and therefore validation must be re-checked and potentially recomputed on every call, with various interfering effects from concurrency, dynamic invalidation testing, and at least potential deoptimization and revalidation. Any of us could also spin a story for choice 1. which uses the type alone, of the receiver, rather than the type-plus-constant-value of the receiver. I have illustrated the latter (choice 2.) because it seems more in line with *your* desires, to have everything pivot off of a dynamic dispatch (virtual method call) to a receiver. But in order for that ?everything? to include the ?something constant? needed for validation, the receiver must be constant. Until we get another source of ?something constant? which would seem to be type class witnesses. Choice 1 would be OK too, but it requires a clearer commitment to design in a validation call to a *static* method on the *static type* of the receiver (in lieu of a type-class witness, which would do a better job), followed by an run time (per-execution) *dynamic* call to whatever intermediate object was produced by the static method. Would you prefer that over choice 2? I think it might have more of the ?magic method? stink you are trying to avoid. I?d be happier, on balance, with choice 1 (types only, magic static method on type for validation) even though it anticipates type classes in a clumsy way. I have a write-up for that also, if you want to see my work. Choice 3 needs a design pattern that I do not yet understand. It interrupts the first execution of a dynamic-receiver computation, extracts some ?static stuff? from that receiver (type? identity? field?), and computes a validated execution method for the rest of the computation, on the assumption that the ?static stuff? will not change in subsequent calls. This design pattern needs to be simple to reason about, needs appropriate bounds on when the ?static stuff? will fail (and what happens then), and of course should optimize. I suppose you think you have exhibited such a thing and that it is satisfactory, but I think it is much more complicated and unreliable than a two-step computation where a BSM takes some explicitly demarcated ?static stuff? and its one-time product is applied N times to the remaining ?dynamic stuff?. But isn't constraining the receiver to be a static constant just more of the same sort of nannyism that you've been objecting to? No, I don?t think so. Someone reminding us of the laws of physics is not a nanny. (In a moment I?ll try to figure out how you might think I?m nannying people away from doing something marginal but possible, and maybe that leads me somewhere.) In terms of choice 1 or 2, starting from the requirement that there is a natural and physical need to call a method (validation) on the link-time configuration information of a S.T., there is then also a natural and physical need to marshal, statically at link time, the inputs to the validation. If the receiver is to be a static input to the static validation computation, then of course it needs to be statically evaluable. That?s not a nanny preventing me from doing something I can handle for myself, that?s a static checking rule preventing me from forming a physically impossible request (in the terms of design choice 2). The design choice 3, your and Jim?s creation (and sharing features with wild ideas from Remi and other indy-nauts) is to manipulate a fully dynamic expression by extracting a quasi-static validation phase on the fly, and creating a cache which purports to safely and efficiently maintain the benefits of a quasi-static validation as long as possible, on a best-efforts basis. With benchmark numbers that say, ?see, as long as possible is pretty long!? To the extent that I?m advocating nannyism, it?s of the form, ?don?t lead users into design patterns that require magic caches and surprise re-validations?. Am I wrong that that is in effect what is being proposed? Or is there a ?safe zone? for the use of your proposed S.T. semantics that (a) provably validates the string (and types) before first execution and (b) provably never re-validates, but runs full speed on the merits of the initial validation. (Think Pattern.compile: The S.T. body string disappears into the compiled regex, which is the only thing present for all subsequent executions. I suppose the S.T. returns the compiled Pattern, perhaps in an incremental variant with additional information folded in from the expression holes.) But my desire is to double down on Java?s pre-existing link-time evaluation phase, which is present for many kinds of expressions, including static member references and anything using indy. That?s not nannying users, but rather it?s playing to Java?s existing strengths. I want to nail the result of S.T. validation, exactly once, to the exact location that it occurs, which requires a static computation. Requiring the target to be a static constant (in the absence of a type class witness) is simply a physical necessity to meet this need. I don?t need a nanny to tell me not to flap my wings and fly; it?s just physics that keeps me grounded. OK, so I would believe your nanny argument more if I believed there was a plausible alternative to an indy-based caching mechanism (sans MCS) for one-time validation. Anything that uses a Mutable call site (or mutable anything) is going to be a hard sell for me, because it looks suspiciously hard to optimize and unreliable. Am I wrong about that? Please, show me how your MCS-based design pattern allows a variable receiver to co-exist with reliable one-time validation. It would be best to distill it to its essentials, without the trappings of string templates and types interpolation holes; it really is a separate problem. Or maybe (and this could be a disconnect on my part) you are proposing that people who use non-constant receivers (a) never get validation and (b) won?t care, and therefore (c) my objection to the presence of such don?t-care use cases is kind of nannyish. After all if they don?t care about validation why should I take away their extreme variablility? But I don?t think that?s the proposal. I think you are offering variable receivers and quasi-constant validation. Side note, applying to all designs we are talking about: If someone doesn?t care about validation, or wants to control validation separately from the intrinsic semantics of the S.T. expression, then we all know there is a workaround: Build a generic S.T. and pass it ?manually? to a validation API point; then use the validated constant according to its desugared API; maybe it?s bound to a static variable for speed. The sugar does not need to fully support every conceivable use case. And because the sugar does not need to fully support every conceivable use case, I think it?s reasonable to make concessions to effective one-time static validation, including requiring a constant receiver (until type classes come on line). From brian.goetz at oracle.com Tue Nov 2 17:43:29 2021 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 2 Nov 2021 13:43:29 -0400 Subject: Translation musings (was: Are templated string embedded expressions "method parameters" or "lambdas"?) In-Reply-To: <9DA1812F-398B-4E7B-9D80-8E084F809FEF@oracle.com> References: <2F2E1E93-2B84-434E-B446-672270048C0A@oracle.com> <6c2e73bb-7fde-e1a0-d36c-0c8caf13d7f0@oracle.com> <635336FF-72B7-4745-B708-203A4A281437@oracle.com> <8EA56F3B-9615-471F-8C79-C84952B5FBE9@oracle.com> <9DA1812F-398B-4E7B-9D80-8E084F809FEF@oracle.com> Message-ID: I think we need to redirect a bit. > I?m pointing out a different proposition: ?*If* you need static > validation of a string, *then* you need something constant about the > receiver. ?By constant I mean, very specifically, ?fixed no later than > just before the first execution of of the string template expression?. > ?The ?something constant? might be the type (some subtype of a known > ?ST-Handler?), or it might be a constant value (e.g., a static final > field value). And I'm saying something different: ?- We never *need* static validation of the string. ?- We never *need* an indy-based, multi-step translation. Both of these are nice to haves, not must-haves; the goal is to extract these when we can have them, not to restrict the feature so that we can't use the feature if we can't produce them. We are in deep danger of way over-rotating towards the translation. I would go as far as to say that if we always, now and for the forever future, just translated with "invokeinterface", it would *still* be a successful and popular feature.? Do we want to do better when we can?? Of course.? Are we willing to restrict the feature to the cases where can do so?? Absolutely not. It's really easy to get wrapped around the axle over cases like String::format, and convince ourselves that this is the 99% use case.? But this represents only a slice (yes, an important slice) of the benefits we get from this feature.? In many cases, the work we'll do to translate the template (e.g., a CLASS."public class Foo { ... }" policy) or after we translate the template (execute a SQL query) completely dominates the performance profile. So yes, let's optimize for the best-case translation when we can, but let's not let that constrain what feature we deliver. From forax at univ-mlv.fr Tue Nov 2 18:25:13 2021 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 2 Nov 2021 19:25:13 +0100 (CET) Subject: Translation musings (was: Are templated string embedded expressions "method parameters" or "lambdas"?) In-Reply-To: References: <2F2E1E93-2B84-434E-B446-672270048C0A@oracle.com> <6c2e73bb-7fde-e1a0-d36c-0c8caf13d7f0@oracle.com> <635336FF-72B7-4745-B708-203A4A281437@oracle.com> <8EA56F3B-9615-471F-8C79-C84952B5FBE9@oracle.com> <9DA1812F-398B-4E7B-9D80-8E084F809FEF@oracle.com> Message-ID: <828046832.610546.1635877513896.JavaMail.zimbra@u-pem.fr> > From: "Brian Goetz" > To: "John Rose" > Cc: "amber-spec-experts" > Sent: Mardi 2 Novembre 2021 18:43:29 > Subject: Translation musings (was: Are templated string embedded expressions > "method parameters" or "lambdas"?) > I think we need to redirect a bit. >> I?m pointing out a different proposition: *If* you need static validation of a >> string, *then* you need something constant about the receiver. By constant I >> mean, very specifically, ?fixed no later than just before the first execution >> of of the string template expression?. The ?something constant? might be the >> type (some subtype of a known ?ST-Handler?), or it might be a constant value >> (e.g., a static final field value). > And I'm saying something different: > - We never *need* static validation of the string. > - We never *need* an indy-based, multi-step translation. > Both of these are nice to haves, not must-haves; the goal is to extract these > when we can have them, not to restrict the feature so that we can't use the > feature if we can't produce them. > We are in deep danger of way over-rotating towards the translation. I would go > as far as to say that if we always, now and for the forever future, just > translated with "invokeinterface", it would *still* be a successful and popular > feature. Do we want to do better when we can? Of course. Are we willing to > restrict the feature to the cases where can do so? Absolutely not. > It's really easy to get wrapped around the axle over cases like String::format, > and convince ourselves that this is the 99% use case. But this represents only > a slice (yes, an important slice) of the benefits we get from this feature. In > many cases, the work we'll do to translate the template (e.g., a CLASS."public > class Foo { ... }" policy) or after we translate the template (execute a SQL > query) completely dominates the performance profile. > So yes, let's optimize for the best-case translation when we can, but let's not > let that constrain what feature we deliver. Templated strings can have a awful performance model if we are not careful, and i don't think that relying on the user being careful aka the C++ strategy is a good strategy. They have been enough lengthy discussions about the performance of String.format() vs StringBuilder, i think it's reasonable to avoid to create new performance pot holes when designing this feature. So static validation and being indy-based are a little more than just nice to have, they are here to provide an easy to understand performance model. regards, R?mi From brian.goetz at oracle.com Tue Nov 2 18:49:31 2021 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 2 Nov 2021 14:49:31 -0400 Subject: [External] : Re: Translation musings (was: Are templated string embedded expressions "method parameters" or "lambdas"?) In-Reply-To: <828046832.610546.1635877513896.JavaMail.zimbra@u-pem.fr> References: <2F2E1E93-2B84-434E-B446-672270048C0A@oracle.com> <6c2e73bb-7fde-e1a0-d36c-0c8caf13d7f0@oracle.com> <635336FF-72B7-4745-B708-203A4A281437@oracle.com> <8EA56F3B-9615-471F-8C79-C84952B5FBE9@oracle.com> <9DA1812F-398B-4E7B-9D80-8E084F809FEF@oracle.com> <828046832.610546.1635877513896.JavaMail.zimbra@u-pem.fr> Message-ID: I am all for an easy-to-understand performance model.? But it is merely one quality of many that we are optimizing for, and it is absolutely, positively, *not* the high-order bit for how we expose the feature.? There will be room for outlining the tradeoffs, but there is not room for "we can't allow case X strictly because it would be slower than case Y."? That's performance myopia, and even to the degree it would be acceptable, it is surely premature.? So let's move on. On 11/2/2021 2:25 PM, Remi Forax wrote: > > > ------------------------------------------------------------------------ > > *From: *"Brian Goetz" > *To: *"John Rose" > *Cc: *"amber-spec-experts" > *Sent: *Mardi 2 Novembre 2021 18:43:29 > *Subject: *Translation musings (was: Are templated string embedded > expressions "method parameters" or "lambdas"?) > > I think we need to redirect a bit. > > I?m pointing out a different proposition: ?*If* you need > static validation of a string, *then* you need something > constant about the receiver. ?By constant I mean, very > specifically, ?fixed no later than just before the first > execution of of the string template expression?. ?The > ?something constant? might be the type (some subtype of a > known ?ST-Handler?), or it might be a constant value (e.g., a > static final field value). > > > And I'm saying something different: > > ?- We never *need* static validation of the string. > ?- We never *need* an indy-based, multi-step translation. > > Both of these are nice to haves, not must-haves; the goal is to > extract these when we can have them, not to restrict the feature > so that we can't use the feature if we can't produce them. > > We are in deep danger of way over-rotating towards the > translation.? I would go as far as to say that if we always, now > and for the forever future, just translated with > "invokeinterface", it would *still* be a successful and popular > feature.? Do we want to do better when we can?? Of course.? Are we > willing to restrict the feature to the cases where can do so?? > Absolutely not. > > It's really easy to get wrapped around the axle over cases like > String::format, and convince ourselves that this is the 99% use > case.? But this represents only a slice (yes, an important slice) > of the benefits we get from this feature. In many cases, the work > we'll do to translate the template (e.g., a CLASS."public class > Foo { ... }" policy) or after we translate the template (execute a > SQL query) completely dominates the performance profile. > > So yes, let's optimize for the best-case translation when we can, > but let's not let that constrain what feature we deliver. > > > Templated strings can have a awful performance model if we are not > careful, and i don't think that relying on the user being careful aka > the C++ strategy is a good strategy. > > They have been enough lengthy discussions about the performance of > String.format() vs StringBuilder, i think it's reasonable to avoid to > create new performance pot holes when designing this feature. > > So static validation and being indy-based are a little more than just > nice to have, they are here to provide an easy to understand > performance model. > > regards, > R?mi > From forax at univ-mlv.fr Wed Nov 24 11:22:35 2021 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 24 Nov 2021 12:22:35 +0100 (CET) Subject: GADT and type variable specialization during inference In-Reply-To: References: <_hUs5IQRMm-eJDvie67qbTMxaXyqWmWiqU82I1ix6N8=.33c811c5-9661-4a2a-a4ad-e6c3f810aceb@github.com> Message-ID: <1377222396.1682986.1637752955762.JavaMail.zimbra@u-pem.fr> Moved to amber-spec-experts, I've got a very similar issue, if we support patter matching on Class. public Stream createPrimitiveStrem(Class type) { return switch(type) { case int.class -> new SpecializedStream(); case double.class -> new SpecializedStream(); ... }; } A cast and a pinky swear @SuppressWarnings("unchecked") solve the issue but we may do better. R?mi ----- Original Message ----- > From: "Brian Goetz" > To: "Jan Lahoda" , "amber-dev" > Sent: Lundi 8 Novembre 2021 21:37:24 > Subject: Re: RFR: 8273328: Compiler implementation for Pattern Matching for switch (Second Preview) > This is more of a spec issue than a compiler issue.? I finally got > around to running some of my favorite GADT examples on this. > > I started with this Haskell type: > > ??? data Term t where > ??????? Lit :: t -> Term t > ??????? Succ :: Term Int -> Term Int > ??????? IsZero :: Term Int -> Term Bool > ??????? If :: Term Bool -> Term a -> Term a -> Term a > > I can map it to this Java hierarchy: > > ??? sealed interface Term { } > > ??? record Lit(T val) implements Term { } > ??? record Succ(Term a) implements Term { } > ??? record IsZero(Term a) implements Term { } > ??? record If(Term cond, Term a, Term b) implements > Term { } > > We correctly eliminate the impossible cases in: > > ??? String foo(Term t) { > ? ?? ?? return switch (t) { > ??????????? case Lit -> "Lit"; > ??????????? case If -> "If"; > ??????? } > ??? } > > And the compiler correctly tells me that the switch is exhaustive.? But > if I try to write an evaluator: > > ??? static T eval(Term term) { > ??????? return switch (term) { > ??????? case Lit t -> t.val; > ??????? case Succ t -> eval(t.a) + 1; > ??????? case IsZero t -> eval(t.a) == 0; > ??????? case If t -> eval(t.cond) ? eval(t.a) : eval(t.b); > ??????? }; > ??? } > > > I get errors on the Succ and IsZero cases.? In Haskell, the equivalent: > > ??? eval :: Term t -> t > ??? eval (Lit t) = t > ??? eval (Succ i) = (eval i) + 1 > ??? ... > > works correctly because when we match on `eval (Succ i)`, we unify t > with Int, since that's the only instantiation for t that would work in > this case.? That's what allows us to return an Int when we're expecting > a t, because we've already figured out they are the same. > > So in order to make our eval work, we would have to know to _refine_ the > bounds of T on the RHS of > > ??????? case Succ t -> eval(t.a) + 1; > > where we would say "Succ is Term, and Succ extends Term, so > T=Integer here". > > In the absence of this, I have to do an explicit boxing plus an > unchecked cast: > > ??? static T eval(Term term) { > ??????? return switch (term) { > ??????? case Lit t -> t.val; > ??????? case Succ t -> (T) (Integer) (eval(t.a) + 1); > ??????? case IsZero t -> (T) (Boolean) (eval(t.a) == 0); > ??????? case If t -> eval(t.cond) ? eval(t.a) : eval(t.b); > ??????? }; > ??? } > > That we need both the boxing and the T cast is probably a bug. >