News for hackers. Stuff that matters.
Re:JEP 443: Unnamed Patterns and Variables (Preview) by cosmotic on Tuesday March 21, 2023 @08:58PM (#35255209)
My $0.02: Unnamed variables just seem to add confusion and inconsistency. Inconsistency like this in Scala and Perl make it harder to learn and offer little value.
Re:JEP 443: Unnamed Patterns and Variables (Preview) by speed_spread on Tuesday March 21, 2023 @09:35PM (#35255557)
They allow to give out warnings on unused variables. If the warning is unwarranted and you really want to discard the value, you use _. It helps prevent mistakes by giving a hint on the intent of the programmer.
Re:JEP 443: Unnamed Patterns and Variables (Preview) by cosmotic on Tuesday March 21, 2023 @11:57PM (#35256637)
I find it exceptionally rare to want not to use a variable that way, it's usually something you want to make harder to ignore, not easier. In the case you really do want to ignore, there are annotations to prevent the warning.
Re:JEP 443: Unnamed Patterns and Variables (Preview) by bobbylarrybobby on Wednesday March 22, 2023 @01:02AM (#35257028)
The underscore is the annotation
Re:JEP 443: Unnamed Patterns and Variables (Preview) by cosmotic on Wednesday March 22, 2023 @02:01PM (#35264786)
I understand that; the problem is that it makes it so easy to bypass the safety net the warning is intended to provide. For example, not logging an exception; You'd have to be really intentional to block the warning with an actual annotation, but using an underscore would be so easy you'd just fall into a pattern of always commenting those out. It's better to have a line of code screaming "I'm doing something that looks wrong here but really it's okay" instead of "_".
Re:JEP 443: Unnamed Patterns and Variables (Preview) by userbinator on Tuesday March 21, 2023 @10:10PM (#35255857)
This code uses an enhanced for loop to step through a collection, calculating total as a side effect without using the loop variable order.

That seems like a very contrived example. I don't work with Java regularly, but wouldn't that be better written as something like this?

    int total = max(orders.length, LIMIT);
Re:JEP 443: Unnamed Patterns and Variables (Preview) by kaba0 on Wednesday March 22, 2023 @03:22AM (#35257864)
It is a contrived example, but the idea/code pattern isn’t — I have written plenty of code where I had to iterate over a collection with a for loop, but didn’t use the exact element.

But obviously for typical usage one should probably just use the stream API.

Re:JEP 443: Unnamed Patterns and Variables (Preview) by frant-hartm on Wednesday March 22, 2023 @03:30AM (#35257911)
Orders may be Iterable, which doesn't have length/size().
Re:JEP 443: Unnamed Patterns and Variables (Preview) by yarg on Tuesday March 21, 2023 @10:44PM (#35256131)
Next to some of the more magic language features and major library changes, this really feels like beige syntactic sugar.

Is it related to anything more useful?

Re:JEP 443: Unnamed Patterns and Variables (Preview) by illiarian on Wednesday March 22, 2023 @03:06AM (#35257755)
> Is it related to anything more useful?

To pattern matching.

Re:JEP 443: Unnamed Patterns and Variables (Preview) by invalidname on Tuesday March 21, 2023 @11:50PM (#35256594)
I don't like it. Lots of the examples seem forced e.g.:

var _ = q.remove();

This is just stupid. We can write: q.remove();

We don't need to save the return value in Java. The only case where this makes sense is:

instanceof Point(_, int y)

But that makes sense because the original code is so bad. A better solution would be type inference like we have in lambdas:

instanceof Point(x, y)

Re:JEP 443: Unnamed Patterns and Variables (Preview) by illiarian on Wednesday March 22, 2023 @03:04AM (#35257745)
> We can write: q.remove();

If that method returns, many tools will complain that the returned value is ignored

Re:JEP 443: Unnamed Patterns and Variables (Preview) by invalidname on Wednesday March 22, 2023 @03:07AM (#35257766)
That's a bug in the tools. IntelliJ doesn't and neither does Sonar Cloud.
Re:JEP 443: Unnamed Patterns and Variables (Preview) by brabel on Wednesday March 22, 2023 @03:18AM (#35257837)
IntelliJ complains if you don't check certain values (e.g. `file.delete()`). It's not a bug, it's extremely helpful.
Re:JEP 443: Unnamed Patterns and Variables (Preview) by invalidname on Wednesday March 22, 2023 @03:26AM (#35257879)
Interesting. It does do that for file.delete() but not for remove(). That's because it's smart enough to know which values are important.

In the case of delete() you would want to check the result. So the warning is good and using an underscore to ignore it would actually be a problem.

In the case of remove() we don't really need the result.

Re:JEP 443: Unnamed Patterns and Variables (Preview) by kaba0 on Wednesday March 22, 2023 @03:17AM (#35257833)
There is type inference, like ` instanceof Point(var x, var y)`.

The real winner here is obviously the used example, `instanceof ColoredPointer(_, var color)`, here every other solution would be plain ugly. The other examples are just there to make the feature intuitive - for example I quite like the `catch (SomeException _)`, making it exact that this exception is deliberately not used up in the catch block.

Re:JEP 443: Unnamed Patterns and Variables (Preview) by invalidname on Wednesday March 22, 2023 @03:22AM (#35257861)
Exactly. That's what I said in the end of my answer but this is no benefit since

instanceof ColoredPointer(x, color)

is even shorter and it's really not hard to come up with a variable name. In this case some tools might mark x as unused but that's just nonsense.

The catch example is interesting but how often do you completely ignore the exception and should you ever do that?

Is that behavior the language should encourage?

I feel that this is a solution to a very minor inconvenience that is pretty redundant.

Re:JEP 443: Unnamed Patterns and Variables (Preview) by kaba0 on Wednesday March 22, 2023 @03:29AM (#35257904)
Okay, what about `instanceof ColoredPoint(3DPoint(_, _, _), var color)`? ColoredPoint’s first field now takes some subtype, and you try to handle only the case where it is a 3DPoint. Now you have to explicitly mention that in the pattern, but it’s possibly long and recursive inner patterns have to be named again.

It can quickly get out of hand, so _ is a pretty well-known and widely used approach here in ML-like languages, and its usage causes no harm at all.

Re:JEP 443: Unnamed Patterns and Variables (Preview) by invalidname on Wednesday March 22, 2023 @03:38AM (#35257968)
I get that it exists elsewhere and saw it in other languages. I think it looks a bit weird and doesn't convey the intention properly. One of the nice things in Java is that we don't automatically copy ideas from other languages but deliberate on them first.

Personally, I think the instanceof syntax for Records is terrible and you're showing off exactly why. It breaks encapsulation entirely. What we need to do is fix records so they will have more OOP related principles. instanceof is a patch and switch should be an anti-pattern. If records allowed inheritance we could just add polymorphic APIs to them and then write more elegant code without the instanceof's.

Re:JEP 443: Unnamed Patterns and Variables (Preview) by kaba0 on Wednesday March 22, 2023 @04:03AM (#35258119)
> One of the nice things in Java is that we don't automatically copy ideas from other languages but deliberate on them first

Sure, but I’m sure it was not an automatic copy, but a well thought-out design by the very competent design team.

> It breaks encapsulation entirely

Records are almost by definition open. They are only the sum[1] of their constituents. They are part of a more data-oriented approach.

Of course the “old” OOP concerns may apply, and there is an escape hatch — one can make a class from a record, and with the coming destructors it won’t need any recompilation, every existing pattern match can work, yet its inner workings can be encapsulated. But this is not the primary purpose for them — they are just plain old data.

EDIT: though now that I thought a bit about it, hopefully with `withers` we get an alternative syntax for pattern matching on records, like `ColoredPoint { point: 3DPoint { x } }`. That way pattern matching won’t be tied to the order of the fields. Surely records don’t change often, but still.

[1] if we are being pedantic, they are the product of their constituents, they are named product types.

Re:JEP 443: Unnamed Patterns and Variables (Preview) by HiJon89 on Wednesday March 22, 2023 @03:24AM (#35257875)
A common example is try-with-resources where you don't actually need the resource and are just using it for the closing effect. For example, let's say you have a metrics library that allows you to time code blocks like so:

  try (Timer ignored = Metrics.newTimer("getOrders")) {
    return db.getOrders();
  }
Another common example is when you are implementing an interface method or overriding a superclass method, and your implementation doesn't need all the arguments. Would be great for the programmer to have a way to express this intent (to the compiler and to the reader)

This JEP would allow stronger static analysis rules like "every declared parameter and variable MUST be used", and _ would serve as the canonical escape hatch for edge cases.

One datapoint: searching our codebase for variables named "ignored" produces >1,000 hits. And probably an order of magnitude more unused variables that aren't named ignored.

Re:JEP 443: Unnamed Patterns and Variables (Preview) by invalidname on Wednesday March 22, 2023 @03:31AM (#35257914)
Wouldn't the try with resources example be better served by allowing this syntax?

  try (Metrics.newTimer("getOrders")) {
    return db.getOrders();
  }
> Another common example is when you are implementing an interface method or overriding a superclass method

This doesn't seem to be supported and could cause many issues e.g. if a base interface adds an overloaded method with a similar signature. Suddenly we will have a conflict and compilation issues.

> This JEP would allow stronger static analysis rules like "every declared parameter and variable MUST be used", and _ would serve as the canonical escape hatch for edge cases.

This is a good point. I'm not sure if it's worth the odd syntax and potential misuse but it is interesting.

Re:JEP 443: Unnamed Patterns and Variables (Preview) by HiJon89 on Wednesday March 22, 2023 @06:05AM (#35258922)
> > Another common example is when you are implementing an interface method or overriding a superclass method

> This doesn't seem to be supported and could cause many issues e.g. if a base interface adds an overloaded method with a similar signature. Suddenly we will have a conflict and compilation issues.

I think this is already a risk when modifying interfaces or superclasses. For example, if you add a method to a superclass, a subclass might already have a method with the same name/arguments but different return type, which would break compilation.

Re:JEP 443: Unnamed Patterns and Variables (Preview) by pdevr on Wednesday March 22, 2023 @03:26AM (#35257887)
Yes, some of the examples are weak. However, the case of unused variables is a well-known problem. Caught but unused exceptions is one common use case, and they have some code snippets of that in there too. Using _ is a neat solution.

For those who don't face that issue, life continues as before.

Re:JEP 443: Unnamed Patterns and Variables (Preview) by kazinator on Wednesday March 22, 2023 @12:27AM (#35256831)
TXR Lisp:

  1> (time-struct-utc (time))
  #S(time year 2023 month 3 day 22 hour 4 min 25 sec 0 wday 3 yday 80 dst nil
          gmtoff 0 zone "GMT")
  2> (match @(struct @type year @y) (time-struct-utc (time))
       (list type y))
  (#<struct-type time> 2023)
We don't want to capture the type: use the @nil pattern which matches an object without capturing a variable:

  3> (match @(struct @nil year @y) (time-struct-utc (time))
     y)
  2023
Wrong/nonexistent type:

  4> (match @(struct foo year @y) (time-struct-utc (time))
       y)
  ** expr-4:1: warning: if-match: no such struct type: foo
  ** match: @(struct foo year @y) failed to match object #S(time year 2023 month 3 day 22 hour 4 min 29 sec 32 wday 3 yday 80
                                                               dst nil gmtoff 0 zone "GMT")
It's all macros, found in one file:

https://www.kylheku.com/cgit/txr/tree/stdlib/match.tl

If a student handed in a pattern matching system that didn't have a "null variable" pattern, I would not award full marks. That's just useless beyond words; you can't do things like

  (match (@a @nil @nil @c) ...)
to match a four-element list, capturing the first and last element; you have to name the variables and then tell your compiler they are deliberately not used.
Re:JEP 443: Unnamed Patterns and Variables (Preview) by kazinator on Wednesday March 22, 2023 @12:44AM (#35256929)
Under the hood:

  1> (compile-toplevel '(match @(struct @type year @y) (time-struct-utc (time))
       (list type y)))
  #<sys:vm-desc: 986dff0>
Disassembly of VM descriptor, annotated:

  2> (disassemble *1)
Constant data table (preinitialized registers D0 through D3):

  data:
      0: year
      1: t
      2: match
      3: @(struct @type year @y)
Referenced functions, indexed by number:

  syms:
      0: time-struct-utc
      1: time
      2: structp
      3: struct-type
      4: slotp
      5: slot
      6: list
      7: sys:match-pat-error
Actual code:

  code:
      0: 20000003 gcall t3 1
      1: 00000001
      2: 2001000D gcall t13 0 t3
      3: 00030000
Call (time), result in T3. Then (time-struct-utc T3) -> T13.

      4: 20010002 gcall t2 2 t13
      5: 000D0002
(structp T3) -> T2

      6: 38000017 if t2 23
      7: 00000002
If T2 is true, keep going, otherwise jump to 23

      8: 20010009 gcall t9 3 t13
      9: 000D0003
Call (struct-type T13) to get the structure's type object.

     10: 2C0B0009 movsr t11 t9
This is a failure in dataflow analysis to remove a useless register copy.

     11: 20020002 gcall t2 4 t9 d0
     12: 00090004
     13: 00000400
D0 is the year symbol; this is calling slotp to test whether the incoming struct object has a year slot.

     14: 38000017 if t2 23
     15: 00000002
If so, continue, otherwise go to error block at 23.

     16: 20020008 gcall t8 5 t13 d0
     17: 000D0005
     18: 00000400
Now get the year slot's value.

     19: 2002000C gcall t12 6 t11 t8
     20: 000B0006
     21: 00000008
List the type in T11 and the year value in T8, into a list held in T12.

     22: 1000000C end t12
Execution ends returning T12.

23 is the error block: run-time function sys:match-pat-error is called with some arguments, like D3, which holds the source code of the pattern.

     23: 20030002 gcall t2 7 d2 d3 t13
     24: 04020007
     25: 000D0403
Not reached, since that function throws:

     26: 10000002 end t2
  instruction count:
     13
  #<sys:vm-desc: 986dff0>
Re:JEP 443: Unnamed Patterns and Variables (Preview) by signaru on Wednesday March 22, 2023 @02:31AM (#35257536)
Looks similar to Rust's underscore.
Re:JEP 443: Unnamed Patterns and Variables (Preview) by illiarian on Wednesday March 22, 2023 @03:05AM (#35257748)
Like any language with pattern matching. See Erlang, Haskell etc.
Re:JEP 443: Unnamed Patterns and Variables (Preview) by kiviuq on Wednesday March 22, 2023 @03:06AM (#35257761)
and Scala's underscore