Omitting named arguments values (Ruby 3.1 feature)

how about reuse the underscore as a placeholder?

h = {x: _, y: _}
p(x: _, y: _)

I think if we try to omit named arguments, then at least we should have some thing to denote that you are intentionally omitting thing, not some careless mistake.

personally I’d like to just use h = {x::, y::}, because typing two colons is easier, but too bad that :: is already used for namespace resolution.

8 Likes

I like the underscore! In other languages and in Crystal’s own typechecker _ is used as “infer here what it should go”. Here, the only sane inference is the thing with the same name. Voilá, easy to explain and visually clear. To me the best option so far.

5 Likes

I thought in underscore. BUT… as its usual semantic meaning is “discard”, I thought it wasn’t a good fit.

At first I thought in

h = {x: <=, y: <=}

as a placeholder saying “the value of that guy on the left”

Then I tested it as

Whatever(a_huge_name_of_something: <=, another_crazy_stuff: <=, a: 3)

And it “felt” visually “not good” at a glance. Like a chained inverse attribution.

So I tried Whatever(a_huge_name_of_something: ==, another_crazy_stuff: ==, a: 3)

And that “bad feeling at a glance” disappeared. The operator in such context says “the same” (value). Its a fast and easy 2 keystrokes of the same key.

I still keep my original suggestion over others until now.

in some languages, _ is used as placeholder in pipe operator, like
"1" |> String.parse_int(_, 10). so it’s actually easier to understand if you come from those languages.

Well…, it exits in Crystal, and means a “matches anything” for such purpose of being discarded.

It’s also already used in Crystal methods definitions, in a diverse way, that will cause confusion, when thinking in definition, and invocation.

An underscore is used in type restrictions. It means “matches anything”:

# Same as not specifying a restriction, not very useful
def foo(x : _)
end

# A bit more useful: any two-parameter Proc that returns an Int32:
def foo(x : _, _ -> Int32)
end
1 Like

Well…, it exits in Crystal, and means a “matches anything” for such purpose of being discarded .

well, <= and == exist in crystal too, and certainly they already have other meanings, too.

also, in swift you have named parameters (crystal also have one), and you can omit the external name by using _.

init(_ x: Int, _ y: Int) {
  self.x = x
  self.y = y
}

also I’m actually surprised that _ value is discarded in crystal, iirc ruby still keeps the value

_, a = [1, 2]
puts _ => 1

maybe this is the reason why they did not choose the _ as placeholder.

FWIW, i find that first context helpful when i want to differentiate between a parameter without a type restriction, versus one that is allowed to accept any type. Makes it clearer to the reader what the intent is.

1 Like

Not in such context of method arguments, and underscore in Crystal already have a meaning in such context making its use confuse adding “sub-contexts” like:

_ in the context of parameters, WHEN defining it means matches anything, BUT when invoking it would mean “the value of the previous symbol”.

I would avoid such thing if possible.

Well, this part is just for people curious about other languages

In Go, it’s called “blank” and is used to discard things. Very useful, because Go does not accept unused variables so a

func main() {
  a, b := get_2_vals()
  fmt.Println("val a = ", a)
}

will raise an error because “b” is never used. So the author fix that expliciting “discard and ignore b” as

func main() {
  a, _ := get_2_vals()
  fmt.Println("val a = ", a)
}

In Rust, it means basically the same as Crystal depending on the context; in place of a variable it means “discard/ignore”; in places where we need to match “patterns” in such context, it is called “wildcard” and matches anything.

Looks nice!

Might be a bit confusing maybe because of other semantic already assigned to it? I mean overloading _ with different meanings.

https://crystal-lang.org/reference/1.3/syntax_and_semantics/assignment.html#underscore

The underscore can appear on the left-hand side of any assignment. Assigning a value to it has no effect and the underscore cannot be read from.

This part

underscore cannot be read from

So here

h = {x: _, y: _}
p(x: _, y: _)

It’s like reading from _ which not something you could do if you assigned to _.

imagine that someday crystal will implement pattern matching.
then _ semantic can be rephrased as a wildcard that match anything.

it’s basically the same, too.

then _ in the right side will still be explainable, it matches the left side of :.
the value of _ is discarded after the matching, not during.

The _ is a decent option. Isn’t that also the “default” variable in Pearl?

Still, this is 2022 and we have the whole vastness of Unicode codepoints to choose from. lets pick something fun. like ≡ or ≈ or the musical repeat sign : 𝄇 :D

A good language still need what’s on most keyboards worldwide without needing juggling to generate a char. We should stick with the ASCII universe, and less keystrokes and modifiers (control-shift-alt-key) == more productivity and less complexity.

1 Like

I don’t know, this seems like a problem that can be solved in hardware : Huge Hand-Wired Ortho Is A Beautiful Battleship Keyboard | Hackaday

:D

I still prefer this symbol to honor the name “implicit” :

2 Likes

Yep. Implicit or direct, if possible; and not complex. Not tied to unusual “hardwares” to be written, accessible in most standard keyboards seeing the symbols, and not having to remember a combination of keys to generate it. The math symbol ≍ means “equivalent to”, would be interesting as an option for f( x: ≍) but it does not exists in standard keyboards, so it is impractical.

today I learned that some language use the % operator instead of _ for placeholder:

https://2ality.com/2022/01/pipe-operator.html

this might be worth considering, too.

After thinking a bit on it, I will not miss it anytime if it will not be implemented but, at the same time, it would not be so harmful in the case it will.

For the later, I’m in favor of the original spec:

h = {x:, y:}
p(x:, y:)

or the underscore one

h = {x: _, y: _}
p(x: _, y: _)

Both of those aren’t strange (for me), and are clear and concise.

To me, underscore reads like sending nothing, like sending nil. Ruby’s approach seems ok. Here’s another:

p(:var1:, :var2:)
2 Likes

I also dislike this “thing:” syntax because I’m expecting something else following it on method calls.
In Spanish, and I think other languages too, there is the concept of using quotation marks
to repeat something already written:

method(option:", another_option:`)

If anything, I would use ^ which is something that points up, almost with the meaning “what’s above”:

x = 1
y = 2
call(x: ^, y: ^)

The only problem is that this isn’t always true: in Ruby (and eventually maybe in Crystal) omitting the value could also mean a call to a method named like that, like:

x = 1
call(x: ^, y: ^)

def y
  2
end

So “^” is pointing up, but “y” is below it.

So maybe…

x = 1
call(x: ^, y: v)

def y
  2
end

(this last part is just a joke, of course :sweat_smile:)

2 Likes