[HN Gopher] Java record pattern matching in JDK 19
___________________________________________________________________
Java record pattern matching in JDK 19
Author : SemanticStrengh
Score : 115 points
Date : 2022-05-14 15:29 UTC (7 hours ago)
(HTM) web link (openjdk.java.net)
(TXT) w3m dump (openjdk.java.net)
| zelphirkalt wrote:
| Another feature, that Java finally adopts, after decades of not
| adopting it. More and more language features and concepts are
| being introduced, finally exposing Java-only programmers to them.
| Lambdas, pattern matching, project Loom, at some point in the
| future, Java might be called a modern language. Good for Java and
| Java programmers.
|
| Just checked some other languages:
|
| - SRFI (Scheme Request for Implementation):
| https://srfi.schemers.org/srfi-200/srfi-200.html
|
| Syntax:
|
| > | ($ record-name pat_1 ... pat_n) a record
|
| - GNU Guile:
| https://www.gnu.org/software/guile/manual/html_node/Pattern-...
|
| Seems to be implementing the SRFI:
|
| > | ($ record-name pat_1 ... pat_n) a record
|
| - Rust: https://doc.rust-lang.org/book/ch18-03-pattern-
| syntax.html and https://doc.rust-
| lang.org/reference/patterns.html#struct-pat...
|
| > We can also use patterns to destructure structs, enums, and
| tuples to use different parts of these values. Let's walk through
| each value.
|
| > Struct patterns match struct values that match all criteria
| defined by its subpatterns. They are also used to destructure a
| struct.
| pron wrote:
| > Another feature, that Java finally adopts, after decades of
| not adopting it.
|
| Back in 1997, James Gosling published an article that serves as
| a blueprint to Java's evolution to this day:
| https://www.win.tue.nl/~evink/education/avp/pdf/feel-of-java...
| He explained it further, as related by Brian Goetz in the first
| 20 minutes of this talk:
| https://www.youtube.com/watch?v=Dq2WQuWVrgQ
|
| In short, Java attempts to be "a wolf in sheep's clothing,"
| with a state-of-the-art, innovative runtime, wrapped in a
| language that's intentionally conservative. Java the _language_
| only adopts features that have already proven themselves as
| worthwhile elsewhere, and when mainstream programmers are ready
| for them. We don 't always live up to that standard, but we
| try.
|
| So another way of putting what you said is that this is another
| feature that has proven itself enough, for long enough, and
| that's matured enough for Java to adopt it. Many if not most
| programming language features don't make it to that stage.
|
| The flip side is to examine which features have never made it
| to Java and probably never will (at least, they're not on our
| long-term roadmap). Those include, among others, macros,
| extension methods, first-class properties, and async/await.
| SemanticStrengh wrote:
| there is nothing to be proud of about lacking extension
| methods. Kotlin has them and it allow to make amazing APIs
| that are much more ergonomic/reduce cognitive overhead.
| pron wrote:
| I personally think there's much to be proud of in lacking
| _any_ feature, as long as you deliver what your users
| expect, especially if the number of users is very large.
| After all, the goal of a mainstream programming language is
| not to have as many features as possible, but _as few as
| necessary_ , where "necessary" takes into account both the
| common software requirements of the era (which are also
| shaped by the hardware characteristics of the era) as well
| as the expectations and habits of the majority of
| programmers at that point in time.
|
| I can tell you that if Java's language team encounters some
| language feature that solves a particular problem, they'd
| rather spend spend several years thinking how to _avoid_
| adding that feature while still addressing the problem,
| than the six months needed to implement it. A good strategy
| for doing that is to wait, and try to think of ways to
| solve multiple problems with one feature (even if
| imperfectly) rather than solve multiple problems with
| multiple features.
| _old_dude_ wrote:
| The same feature does not entail the same tradeoff
| depending on the languages.
|
| For Kotlin, extensions methods are essential otherwise you
| can not extend existing Java types with Kotlin types.
|
| For C#, you need extension methods for LINQ because
| modifying the .Net runtime was not an option at that time.
|
| For Java, changing the VM is not an issue, instead of
| extension methods you add default methods which can be
| overridden by implementations.
|
| Same feature, different tradeoffs.
| kaba0 wrote:
| I think Kotlin went the easy way with extension methods.
| Scala's solution is much more elegant with implicits
| (especially now with Scala 3).
| SemanticStrengh wrote:
| I'd be very interested in a comparison between scala 3
| using/given and Kotlin new context receivers https://gith
| ub.com/Kotlin/KEEP/blob/master/proposals/context...
| elric wrote:
| > Another feature, that Java finally adopts, after decades of
| not adopting it
|
| Java has been steadily moving in this direction for quite a few
| years now. It's tricky business. There's the language proper,
| and there's the JVM, which are interdependent. Both are aiming
| to remain backwards compatible. Introducing new major language
| features is not an easy feat.
| pavelbr wrote:
| C# has been adding new major language features all the time
| since its inception, including pretty much everything we are
| seeing being added to Java these days.
| Skinney wrote:
| Yeah, and this "kitchen sink" approach is partly why I
| stopped writing C#. Same thing is turning me away from JS.
|
| It's exhausting trying to stay up-to-date with the
| language, in addition to changes in the frameworks and
| tools on top of the challenges of my day job.
|
| I might be showing my age, but I've come to appreciate that
| features come after long and careful consideration, and not
| every single release.
| grishka wrote:
| What I love about Java though, is that this new syntax is very
| clear and its intentions are obvious when you're reading
| someone else's code. It doesn't require you to hold a mountain
| of context in your head at all times like Swift and Kotlin do.
| Yes, it's verbose at times. But verbosity is a good property
| for a programming language because it allows the code to be
| read and understood with ease outside of an IDE.
| jeroenhd wrote:
| Most Java is very easy to comprehend, but then you have
| things like implicit finals and whatever you call the `List<?
| extends Something>` language construct. I think most Java is
| very boring, functional code with some notable exceptions
| related to threading and the typing system. It gets the job
| done, but often in an ugly, roundabout way.
|
| The way `Optional` is implemented and the roundabout way to
| just grab the first item in a list
| (`list.stream().findFirst()` or `var e = null; if
| (!list.isEmpty()) e = list.get(0);`?) where other languages
| have added helpers years ago. Or, even worse, the lack of
| tuples, leading to your average medium-sized Java project
| containing five different implementations of Tuple/Pair.
| There are also runtime restrictions that sometimes crop up
| because Oracle can't break compatibility (type erasure, for
| example).
|
| If code clarity was the only metric for language quality then
| we'd all be writing some BASIC derivative. I don't think more
| modern languages like Swift and Kotlin are all that
| applicable to all areas where Java shines, but ever since
| dotnet went open source and cross platform I'm really
| starting to wonder why anyone still bothers with Java.
| Jach wrote:
| I was almost with you up until the end then I had to just
| shake my head. I mean, it's readable syntax and doesn't
| require some crazy new sigils or operators, it's "fine"
| though I'm also shaking my head at the scope rules for these
| bindings. Similarly I'm ok with the "fine" syntax of
| Optional, and being explicit with my Optional.orElse()'s,
| rather than introducing an 'elvis operator' or some
| bikeshedded derivative. Though I wouldn't mind such things --
| I'm glad Java keeps evolving useful things anyway, but it
| does wear on you (or at least me) to have to speak them (or
| even to tell your IDE to speak them on your behalf) as if
| your mouth was full of sand or to read them as if they're a
| high school student's essay obviously padded to reach some
| word count minimum.
|
| If I came across this new thing organically, I could go "ah,
| neat, we have that now" and not necessarily need to go look
| up the JEP (meanwhile Python's PEP 636 had me going wtf) --
| though over time I'd expect to come across it in a negative
| context where the original programmer made a scope mistake
| because they _didn 't_ read the JEPs, or because they tried
| to make a change _without_ their IDE 's assistance where
| through various means it could have pointed out shadowing or
| scope concerns, that I now have to fix.
|
| It's in no minor part thanks to Java's verbosity that the
| overall Java ecosystem is so verbose. Having mostly non-
| confusing syntax whose meaning you can mostly guess at on
| first exposure is nice, yes, but isn't so helpful for the
| more important aspect of having non-confusing programs. For
| that you really want a more concise and tasteful language
| over a verbose one. Anyway, I've found that for anything non-
| trivial about the program itself, I'm going to need an IDE
| because the meat of the thing is going to be verbosely spread
| out in many places (sometimes for good reasons, at least in
| the Java world). Sure, after I acquire the mountain of
| (program) context, I can review small changes even on paper
| printouts, but that's true of most anything.
|
| Verbosity is not a good property for a language to have in
| general -- Java itself admits this, otherwise we wouldn't
| have so many shortcuts in syntax like omitting this, or java
| 7's diamond operator or try-with-resources or catching
| multiple exceptions or for-each syntax, or java 8's lambdas
| and special syntax for simple lambdas, or java 9's var, or...
| And of course, almost always in some other more concise
| language a concept is much clearer. There's a reason pseudo-
| code isn't written to look like Java.
| [deleted]
| mabbo wrote:
| It's not the feature that excites me, it's the pace of Java
| language improvements. The 6-month cycle of versions, the whole
| process of letting experimental features in for a couple versions
| to test, refine, and likely accept, it's all really _working_ ,
| you know? We're _getting_ stuff that 's been so long desired, and
| it's happening quickly enough to be exciting but not so fast that
| we're overwhelmed and things are breaking.
|
| People often say that Java 8 was the big important update to the
| language (streams, lambdas, java.time, and more), but I think
| Java 9, which began this process was the true rebirth of the
| language.
| eecc wrote:
| Cool, thanks Java gatekeepers for procrastinating your change of
| mind long enough to save face.
|
| In the meantime I wish we had a community that could evolve at
| that sweet, optimal pace, a promise that the Javascript one
| completely blew. Rust?
| [deleted]
| cutler wrote:
| Despite recent improvements including raw string literals it
| beggars belief how Java still requires regex metacharacters to be
| escaped. Until that's fixed Java is not an option for me.
| [deleted]
| hn_throwaway_99 wrote:
| Cool to see Java getting these features. After having been a Java
| dev most of my life, and then moving to Typescript years ago,
| it's hard to believe I went so long without this.
|
| Related, Typescript is the only language I know of that through
| flow analysis does _not_ require you to redefine the variable.
| E.g. you can do something like if (typeof foo ===
| 'string') { //Typescript knows foo is a string here
| }
|
| All the other languages I know of, like this proposal for Java,
| require the variable to be redefined. I personally find the
| Typescript way, as VSCode will give you appropriate type warnings
| in the right places, easier to use in practice. Just curious if
| there are any other languages that do it like TS.
| papercrane wrote:
| The Typescript way would only work for local variables in Java.
| Since another thread might update a field between the
| instanceof check and the use of the field.
| SemanticStrengh wrote:
| those cases are rare. Defensive copying should be optin, not
| the default. Java really deep copy objects for even the most
| basic instanceof checks??
| kaba0 wrote:
| You can't change the object's type in Java, so if you hold
| on to the same reference it will be safe to use after the
| instanceof check.
| zarzavat wrote:
| You're definitely holding it wrong if you have polymorphic
| fields in your lock-free shared memory. Or at least holding
| it weirdly.
| gizmo686 wrote:
| Thats the problem with language design. If the language
| allows something, it needs to correctly support it; even if
| actually doing it is a bad idea.
| swaranga wrote:
| There are some edge cases because of which this had to be done,
| I think. For example calling static methods via the instance
| variables, the actual method called would be the static type of
| the variable at compile time and not the actual type at
| runtime: public static void main(String[]
| args) { Parent instance = new Child();
| if (instance instanceof Child p) {
| instance.print(); // prints Parent p.print();
| // prints Child } } static
| class Parent { static void print() {
| System.out.println("Parent"); } }
| static class Child extends Parent { static void
| print() { System.out.println("Child");
| } }
|
| Hence, with flow typing, existing code could break in subtle
| ways.
| hilbertseries wrote:
| If you're writing code calling static methods on instances of
| classes, you deserve to have that code broken.
| yarg wrote:
| Does java provide warnings against calling static methods on
| Objects?
| smarks wrote:
| Yes, if javac is given the `-Xlint:static` option.
| SemanticStrengh wrote:
| How does Kotlin solve those use cases?
|
| > instance.print(); // prints Parent
|
| why? the parent print should be shadowed/overidden by the
| child bruh
| spullara wrote:
| statics don't override
| SemanticStrengh wrote:
| oops my bad
| danachow wrote:
| That's not dataflow analysis. It looks like a cast similar to
| something like in C# ie: if (foo is string as foo) where the
| "as foo" scoped variable is implicit. It's basically implicitly
| redefining the variable with limited scope. Type guards are a
| slick feature but it is just an expression match and some
| syntactic sugar. Even user defined type guards in typescript
| have to be explicitly declared as such.
|
| This would be more an example of CFA. const a =
| "string"; if (typeof v === a)
| hn_throwaway_99 wrote:
| It's not just a simple cast, but I should have used the
| correct term which is "control flow analysis". It's not just
| redefining the variable, control flow analysis works in lots
| of different places besides just typeof checks, and is
| possible in many cases because TS is structurally typed, not
| nominally typed.
| jung_j wrote:
| Java 14 does have something similar
|
| https://openjdk.java.net/jeps/305
| hn_throwaway_99 wrote:
| No, Java requires you to redefine the variable, i.e.
| if (obj instanceof String s) { // s is a String here,
| but obj is not }
| SemanticStrengh wrote:
| but is it a deep copy??
| sebazzz wrote:
| It is not a copy. It still references the same object.
| Pet_Ant wrote:
| That I would say still counts as "similar" not "the same"
| danachow wrote:
| That's the same, just that the scoped definition is
| implicit.
| hn_throwaway_99 wrote:
| No, that's the entire point of my question. In Typescript
| (and Kotlin, as others have noted), the type system knows
| _within the scope of that if statement_ that obj is a
| string, so it lets you call string-specific methods on
| obj withOUT introducing the new variable s.
| danachow wrote:
| What I'm saying is that this: if (typeof
| obj === 'string') { // obj is a string }
|
| is really not anything meaningfully different than this
| if (obj is string as obj) { // as in C# // obj is
| a string (shadowing the outer scoped obj) }
|
| or this if (obj instanceof String obj) {
| // obj is a String here }
|
| you can imagine a pseudo TypeScript language like this
| if (typeof obj === 'string' as obj) { // obj is a
| string }
|
| Actual TypeScript merely allows you to elide the "as obj"
| and all it needs to trigger this is the "typeof obj ===
| <string literal>" inside of an if expression. This can be
| done by a simple syntactic replacement, it doesn't
| require control flow analysis to get this specific
| feature. But yes, if you have a more general computed
| expression, that would apply, but that was not the case
| you were stating. Ie the variable isn't the issue
| (redefining in this case seems like a distinction without
| a difference)... However this does work in TS, which is a
| demonstration of CFA: const isStr =
| typeof obj === "string"; if (isStr) {
| // obj is str }
|
| For what it's worth I think CFA is useful in TypeScript
| based on it at its core being a structural typed bandaid
| over JS, but I think these specific CFA type narrowing
| features are redundant in stronger typed languages.
| Smaug123 wrote:
| F# also has: let foo : obj = failwith ""
| match foo with | :? SomeType as blah -> //
| use blah : SomeType
|
| (You're allowed to just use the identifier `foo` in the match
| arm.)
| halfmatthalfcat wrote:
| As does Scala: match thing { case
| a: String => // use a as string case a @
| MyCaseClass(b: String, c, d) => // use a as the
| instance of MyCaseClass or use b as a string }
| dwaite wrote:
| Some of this is a consequence of how typescript works - it
| carries any structural constraints it can find forward in an
| advisory capacity, because it is somewhat decoupled from the
| quite-dynamic runtime behavior of the javascript engine.
|
| It isn't even block-based scoping in the flow analysis above -
| if in your block there was the code foo = 1; the typescript
| engine would then expect that foo will behave like a Number at
| runtime.
|
| I haven't dived in deep enough, but I suspect foo could even
| have different structural type information within the same
| expression, e.g. a ternary (typeof foo === 'string') ?
| something(foo) : somethingelse(foo)
| Squarex wrote:
| Kotlin is the same as typescript.
| pkulak wrote:
| Kotlin does that as well, and it's a JVM language.
| arthurcolle wrote:
| > All the other languages I know of, like this proposal for
| Java, require the variable to be redefined.
|
| can you give a few examples? This makes no sense to me and I've
| touched quite a few ecosystems in my time. Maybe I am
| misunderstanding but my current understanding makes this seem
| not realistic
| TheMatten wrote:
| And of course, Haskell has equivalent too:
| case eqT @a @String of Just Refl -> ...
| moomin wrote:
| Refinement typing (the TS way) is relatively common in type
| systems that sit on top of dynamic languages because basically
| they have no choice. Binding to a new variable, however, is a
| heck of a lot easier to analyse so tends to be done in
| languages that don't need to implement refinement typing.
|
| A couple of interesting exceptions: explicit interfaces make it
| impractical in C#, and there's a project called liquid Haskell
| that adds refinement typing on top of an already strong type
| system.
| SemanticStrengh wrote:
| Don't be mistaken, Typescript do not has proper pattern
| matching support, The proposal is far from being adopted
| https://github.com/tc39/proposal-pattern-matching
|
| > Related, Typescript is the only language I know of that
| through flow analysis does not require you to redefine the
| variable. E.g. you can do something like
|
| This is called smart casting and is widely used in Kotlin
| viewfromafar wrote:
| The PL crowd calls that type system feature "occurrence
| typing."
| SemanticStrengh wrote:
| yes or flow typing https://en.wikipedia.org/wiki/Flow-
| sensitive_typing It probably overlap with the research on
| gradual typing too
| PartiallyTyped wrote:
| Mypy (Python) does this as well. VSCode and PyCharm provide
| typehints after if isinstance(foo, str):
| # code
| dehrmann wrote:
| Sometimes. If foo isn't local scope, this won't work because
| it could be modified in another thread.
| saghm wrote:
| In my compilers class, this is how we did static downcasting
| for the toy language we created (I think it was called Oat?). I
| think it's clever, but I find that I prefer mechanisms that
| rebind the variable for clarity purposes. It's super easy IMO
| to accidentally gloss over something as being just a regular if
| statement rather than something that's actually statically
| changing the type of the variable compared to having different
| syntax than just a regular conditional. I also tend to prefer
| being able to look at a variable's usage and then look back to
| the time it was initialized and know that it's still the same
| type; having the type change only within a given scope without
| any explicit binding seems like something I'd mess up a lot,
| especially when reviewing code I didn't write myself.
| skybrian wrote:
| Dart also does this.
| markdog12 wrote:
| And it's getting patterns too: https://github.com/dart-
| lang/language/tree/master/working/05...
| blacksmithgu wrote:
| I love seeing modern language features coming to Java! Sadly, I
| suspect it will be at least a decade before I see Java 19 used
| anywhere I work...
| quilombodigital wrote:
| I am an old school java developer. Can someone explain me why
| this is a "feature"? I can remember by counting my fingers the
| times I had to use "instanceof", and it was some classical
| reflection/injection/instantiation framework 'vodu'. If you are
| using instanceof in a normal java program, you are just making it
| wrong. It looks like some new javascript guys arrived in the
| block and are trying to make java look something messy like
| Scala. What real world problem is this trying to solve? Are you
| just fucking bored because you are all out of ideas and every JDK
| release we need "something"? Why these JEPs only have
| "boxes,rectangles,A,B,I" class examples and not a simple real
| world class scenario? Why we need to make java "friendly" to work
| with generic objects? it should be hell! I cant wait for JDK 50.
| mathgladiator wrote:
| I wish they had better examples, so here is one. Suppose you
| are making a scene graph, and you want to have a visitor and
| run code based on the type of the nodes in the graph. Well,
| consider two approaches. The first approach is to use a bunch
| of ifs and instanceof, but this isn't clean and it is fragile.
| The second approach is to make an interface that has all the
| types listed under a handler (void handle(Type1 t), void
| handle(Type2 t), ...) and then write some boiler plate code to
| do the dispatch.
|
| This feature aims at the conciseness of first model
| (instanceof) with the safety of the second approach (i.e. all
| types are handled).
|
| I would love this feature for my project! ( https://www.adama-
| platform.com ) since I use Java to implement a programming
| language.
| quilombodigital wrote:
| This is the point, you dont consider the instanceof approach,
| is just wrong. You already know your types in advance.
| equalsione wrote:
| > If you are using instanceof in a normal java program
|
| In applications yes, but instanceof is heavily used if you
| write frameworks.
|
| And as someone else has mentioned, the more functional style
| java you write the more use you start to make of instanceof
| hztar wrote:
| you sound like a Cobol developer I once knew.. RIP
| quilombodigital wrote:
| This is true.... Old guys know nothing... for years we told
| the javascript guys the mess they were doing, and voila, all
| dynamic languages suddenly started to convert into
| statically-typed, and at the end, they have this webassembly,
| that runs in a schizophrenia-type VM wannabe. Congrats. You
| lost 10 years of your life because you didnt listen to us
| when we said that we have learned something from Self and
| Smalltalk.
| Skinney wrote:
| > and voila, all dynamic languages suddenly started to
| convert into statically-typed
|
| This is... just wrong.
|
| Plenty of people are using JS without any sort of typing
| whatsoever. Other dynamic languages, like Clojure, Gradle,
| Elixir, Pharao (a smalltalk), just to mention a few, are
| still going strong.
|
| > they have this webassembly, that runs in a schizophrenia-
| type VM wannabe
|
| wasm is mostly being used as a target for C(++), Rust and
| even C# programs that want to run in the browser. It's
| there to expand the reach of the browser. Most JS projects
| don't touch the stuff.
| jstimpfle wrote:
| > wasm is mostly being used as a target for C(++)
|
| because... JS isn't good enough?
| wiseowise wrote:
| > This is true.... Old guys know nothing... for years we
| told the Java guys the mess they were doing, and voila, all
| OOP languages suddenly started to move to functional
| paradigm. Congrats. You lost 10 years of your life because
| you didnt listen to us when we said that we have learned
| something from ML and Scheme.
| quilombodigital wrote:
| good point... but I was not talking about OOP, I was
| talking about dynamic typing versus static typing. :)
| jayd16 wrote:
| The sealed interface, final implementation case is an
| interesting one to inspect. Now you can, for example, write a
| parser with a fixed set of tokens and write switches that
| exhaustively handle every token. You now get compile time
| guarantees.
| quilombodigital wrote:
| ok... so you save a few lines in a parser. program your
| language for the 0.005% developers
| jayd16 wrote:
| It's just an example...
|
| And you don't seem to understand that it's about the safety
| of the exhaustive check not just the sugar.
| kaba0 wrote:
| You really could do away with the arrogance.
| quilombodigital wrote:
| I dont think saving a few lines for a very rare corner
| case is a strong reason to add a language feature
| jstimpfle wrote:
| Maybe they have a point? Arrogance != Wrong in Your
| Opinion.
| kaba0 wrote:
| Sure, though as I mentioned in other comment, pattern
| matching is basically a better Visitor pattern. Which is
| while not the most common pattern, is not that much of a
| niche either.
| halfmatthalfcat wrote:
| "...look something messy like Scala", I feel bad you feel this
| way as Scala is an incredibly powerful and elegant language.
|
| It's ironic because you're throwing shade at Javascript when JS
| developers are doing the same thing against Typescript. Fearing
| what you don't know or understand under the guise that change
| is bad or things are "good enough".
| quilombodigital wrote:
| I agree Scala is very powerful. I said messy because it is
| "too powerful", added too many features. I think this is
| their mistake. I respect the community, but can show my
| opinion as you can about java.
| kaba0 wrote:
| I really don't feel that Scala has too many features. It is
| a relatively small language which is very elegant due to
| having almost no edge cases. Sure, due to not having many
| features yet being very expressive it has to have very
| powerful language primitives that can be abused, but I
| really don't think that it is as bad as its name.
| wiseowise wrote:
| > show my opinion as you can about java.
|
| Showing your opinion doesn't make it immune from critique.
| quilombodigital wrote:
| true
| dopidopHN wrote:
| I really liked scala... until I had a to maintain a large
| and << old >> code base in it.
|
| I think messy fits.
| dboreham wrote:
| "Write-only language"
| grumpyprole wrote:
| Java is also a messy language and the reason for a lot of
| the mess in Scala. If you want elegance, then you should
| probably learn something like Haskell.
| quilombodigital wrote:
| can we have a not-messy-not-elegant language? :)
| orthoxerox wrote:
| Eiffel?
| Frost1x wrote:
| Highly compositional design patterns in Java. When you have a
| fairly large set of custom types that are hierarchically
| structued in nature, it can be quite useful.
|
| In most standard IT applications it may be less common. I've
| seen it used fairly well in scientific applications. There are
| of course other ways of approaching working with and acting on
| wide taxonomies of objects that could exist but this is one of
| them.
| kaba0 wrote:
| instanceof is but one possible use case. Arguably the most
| useful part of pattern matching is inside switch expressions,
| and there is an equivalence between the Visitor pattern and
| this, but the latter is much more readable and maintainable.
| Sure, the visitor pattern is not that often used, but in
| certain niches that make use of ASTs often it is pretty much a
| must.
| jstimpfle wrote:
| I've never been a fan of Java due to the GC and the boilerplate
| and runtime overhead (time but also space) that come with it.
| But I've always had respect for it because it felt so minimal
| and consistent in its own way, bulky but solid kinda like a BMW
| car.
|
| Now that functional stuff and instanceof fluff is something
| else. I don't like it either.
| kaba0 wrote:
| Unless you only prefer low-level languages, Java's runtime is
| anything but bulky. It is one of the fastests runtimes out
| there, so runtime overhead is relative.
| [deleted]
| rr808 wrote:
| I kinda feel the same. Modern Java is written like some
| Python/JS Mashup. Its one reason old code bases are a huge mess
| because code is written in many different styles.
| BlackFly wrote:
| It shows the majority of its strength in switch statements. You
| can mostly just use the instanceof for a one line destructuring
| of a compound object into its constituents. The corresponding
| rust statement is the `if let` or `if case let` if you are more
| of a swift fan. Generally you would use it with sum types which
| were previously extremely nasty to work with in Java but are
| more useful now with sealed classes.
|
| The pattern shows up a lot with result and option types but you
| can do it any other time where a tagged union makes sense. The
| classic way to do a tagged union in Java was just a personal
| taste amongst bad options: tagging interface and instanceof on
| implementations or an encapsulating class that will return null
| (or throw an exception) if the gets are called for the types
| which aren't encapsulated.
|
| If in your career you never needed to enumerate cases that
| required different data in each case and you couldn't see how
| binding those requirements into an invariant via the type
| system was helpful then you probably won't use these new
| features either. Some people thought it was helpful but the
| boilerplate cost was too high so wouldn't do it in practice (I
| am in this category).
|
| Edit: you could also make tagged unions more type safe with
| callbacks, but then implementing a closure to pull the value
| out of the callback was just annoying.
| quilombodigital wrote:
| "If in your career you never needed to enumerate cases that
| required different data..." . Maybe one or two times... but
| it is not like "hey, it is impossible", or "shit, I have to
| write 10 more lines" in this rare code I will never put my
| eyes again. What I get angry is that people will start to use
| everywhere, in places they should not be using. because the
| guy did not wanted to stop five minutes to _think_ about it.
| The river flows through the easiest path, but this does not
| means it is the smartest path.
| Twisol wrote:
| Unfortunately, I've seen plenty of Java 8 code where people
| did not "stop five minutes to think about it". I don't
| think any language can protect us from ourselves.
|
| It's fair that you've found sensible ways to achieve your
| goals without ever _needing_ pattern matching. Nobody
| should fault you for that. But can you grant that other
| developers might have other sensible ways to achieve other
| goals (or sometimes other ways to achieve the same goals!)?
|
| I think most Java developers will be familiar with the
| visitor pattern. In almost all cases (there are
| exceptions!), I detest it; I find sum types with pattern
| matching to be a far superior way to say what I mean. Java
| added `sealed` interfaces recently, so you can actually
| model a closed family of types. Now pattern matching closes
| the loop, giving you a supremely ergonomic way of
| dispatching over that closed family without using a
| visitor.
|
| Maybe it's not clear from other responses, but the part of
| the JEP about an "exhaustive switch" is critical here. Java
| statically guarantees that you've handled each member of
| your closed family, just like if you forgot to implement a
| method for a visitor.
| metadat wrote:
| This doesn't take anything away, it just adds extra convenience
| and flexibility.
| quilombodigital wrote:
| My problem is with the "just adds"... Scala made this
| mistake, and ended with many different ways to do the same
| task. It is a powerful language, but I believe languages must
| have only one (or two) concise way to to things.
| adra wrote:
| If you wanted the idiomatic only-one-way-to-do-things
| approach to do things, Java is not and has never been the
| language of choice for that. Why do we have 10 different
| dependency injection /logging / web frameworks? Who cares!
| Let's add an 11th for good measure. I don't want java to be
| idiomatic and purist conformant tedium.
|
| You're welcome to switch your language of choice to go if
| that's really your bag.
| quilombodigital wrote:
| The examples you gave are not language features, are
| frameworks/libraries... not the discussion. (the only
| exception I can see is the "logging API", but this is
| debatable)
| wiseowise wrote:
| > but I believe languages must have only one (or two)
| concise way to to things.
|
| Java has none.
| quilombodigital wrote:
| I humbly accept the critic. :)
| halfmatthalfcat wrote:
| The "multiple ways of doing things" is a feature, not a bug
| and something which lives in almost every programming
| language. It's become a meme at this point and a lazy
| argument against a language.
| metadat wrote:
| > It's become a meme at this point and a lazy argument
| against a language.
|
| This might be a casual dismissal of what is in fact a
| nuanced aspect of programming languages.
|
| Multiple ways of doing the same thing can make the
| language less accessible because it becomes confusing to
| reason about what the "right" way is.
|
| That said, this particular addition merely reduces
| repetitive toil, and it follows similar existing Java
| conventions, for example see the Java-7 equivalent of
| Python "with-statements".
|
| https://stackoverflow.com/a/35116251
| quilombodigital wrote:
| yep, agree with you. I become more aware of this when I
| worked some years in a huge ruby project. Everything was
| allowed, and with tons of DSLs you dont know anymore what
| is happening.
| valcron1000 wrote:
| 100% agree. Using 'instance of' in Java is a terrible anti-
| pattern, same as trying to do pattern matching. You should lift
| that code into instance methods and let the runtime pick the
| right implementation.
| Fellshard wrote:
| There are some cases when it's actually simpler or more
| maintainable or more useful to do the dispatch locally. A
| language that allows you to both is tougher to learn and can
| have a higher risk of pitfalls, but also means that a
| seasoned practitioner can apply the right tool in the right
| situation.
|
| In terms of the classic 'patterns', it's the difference
| between standard dispatch and the 'Visitor' pattern. There
| are cases where you want to specify each case of data
| structure locally, and others where you want to specify each
| method of each variant locally. Welcome to the expression
| problem!
|
| There are cases in Java where you do want a true POJO - just
| a bag of structured data. This is probably a very strong
| candidate for using along side that.
| nchi3 wrote:
| Because there are scenarios where pattern matching (or
| instanceof) is the better and more ergonomic thing to use. One
| obvious example I can think of is event handling.
|
| If anything, this is _not_ something JS devs are asking for,
| but rather devs using functional languages.
| Fordec wrote:
| I also think of API ergonomics. Unspecified input with the
| same single endpoint, while under the hood will be absolute
| spaghetti, from a developer adoption standpoint can be make
| or break.
|
| And if you're strongly typing your system rather than relying
| on strings, extra so.
| quilombodigital wrote:
| Hum... maybe this is the reason my events usually have an
| abstract base class with an eventype enum field...
| grumpyprole wrote:
| Sometimes it isn't desirable to scatter the logic across
| many classes, or conflate the data and the handling. The
| Java world has the "visitor pattern" to help deal with
| that, but its double-dispatch is clunky, complex and
| verbose. Pattern matching just generalises switch and makes
| it more useful. Java is genuinely a better language with
| this change.
| misja111 wrote:
| Exactly, this is what OP was trying to point out. With the
| record matching feature, you don't need the extra abstract
| base class anymore.
|
| This is not to say that abstract base classes are never a
| good idea, but sometimes in Java they were only there for
| the convenience of one handler method, to prevent the use
| of instanceof. This didn't improve readability. That's when
| the record matching can be a good improvement.
| Strs2FillMyDrms wrote:
| For everyone saying the visitor pattern requieres switch
| statements this is false. The visitor can build its case inside
| the iterface/implemetation. If the complexity is being solved by
| the "housing" class that the visitor will be visiting you are not
| doing enough.
___________________________________________________________________
(page generated 2022-05-14 23:00 UTC)