Open jkramer opened 6 years ago
FWIW, I find the current semantics of .?
less than useful and also would like to see a way to only call a method if the invocant is defined.
I feel the same as lizmat; in particular, the fact that several methods exists on undefined invocants too, sometime or even on Any
, just to print a warning, makes the current behavior much less useful in practice:
➜ say Any.?match(/a/);
Use of uninitialized value of type Any in string context.
Methods .^name, .perl, .gist, or .say can be used to stringify it to something meaningful.
block <unit> at <unknown file> line 1
Nil
> say Str.?uc;
Use of uninitialized value of type Str in string context.
Methods .^name, .perl, .gist, or .say can be used to stringify it to something meaningful.
in block <unit> at <unknown file> line 1
my Str $s; say $s./trim; # => Nil
What's the real-life usecase for this? Both how would you end up with an undefined object in the first place and why would returning a Nil be useful behaviour. Also, what's the behaviour if the method doesn't exist on the object; a throw?
We already have two ways to achieve similar behaviour:
my Str $s;
$s andthen .trim.say;
.trim.say with $s;
So so far I'm voting 👎 YAGNI
saw in the #perl6 logs where this idea was incepted. The original syntax you used works fine if you put the whole colonpair behind a with
, not just the value:
<Zoffix__> m: m: my $x; my Str %h = :foo('lol'), |(:bar(.trim) with $x); dd %h
<camelia> rakudo-moar 5ef056122: OUTPUT: «Hash[Str] %h = (my Str % = :foo("lol"))»
It would really just be convenient syntactic sugar. Here's one use-case:
class Foo {
has Str $.a-string;
}
my Str $some-input;
my $foo = Foo.new(:a-string($some-input./trim));
Both Foo.new(:a-string($some-input andthen .trim))
and Foo.new(:a-string(.trim with $some-input))
won't work because they return Empty (Slip), so the type check against Str fails.
EDIT: Your last example seems to work though.
What's the real-life usecase for this? Both how would you end up with an undefined object in the first place
Lots of ways!
Say you're looking up a hash entry that may not exist:
sub negate-verb(Str $verb --> Str) {
state Str %opposite = 'GOOD' => 'BAD', #`[more loaded from database here];
return %opposite{$verb.uc}.?lc // "not $verb";
}
say negate-verb 'good'; # bad
say negate-verb 'big'; # not big
Or you're doing something with an object attribute that the users of the class may leave uninitialized:
class Person {
#| Name of the person, or (Str) if unknown
has Str $.name;
#| Initials of the person, or (Str) if unknown
has Str $.initials = $!name.?comb(/«./).?join;
}
my $p1 = Person.new(name => "John Fitzgerald Kennedy");
my $p2 = Person.new();
say $p1.initials; # JFK
say $p2.initials; # (Str)
In a language where variables, attributes, and data structure entries default to type objects, and the default type constraints for parameters and even method invocants allow both defined and undefined values to be passed in from down-stream code, there are really lots of situations where you can end up with a value that is usually .defined
but sometimes not.
and why would returning a Nil be useful behaviour.
Nil is automatically cast to the type object of whatever type it needs to be, when assigned to a variable or similar.
So whatever type the method would have returned in the "defined invocant" case, the Nil
return value for the "undefined invocant" case is compatible with it.
Lots of ways!
I think this is going backwards about it: you thought up a feature and now making up examples that might use it. Rather than having a bunch of real world code and going: "man, this code would be a lot simpler if this feature existed."
When adding a new feature, you shouldn't think of all the ways to use it. Rather think of all the ways to avoid its use, to find cases that actually require it.
Say you're looking up a hash entry that may not exist:
So just use
return .lc with %opposite{$verb.uc};
"not $verb";
or
return %opposite{$verb.uc} andthen .lc orelse "not $verb";
Or you're doing something with an object attribute that the users
This example makes no sense to me: why would I want an undefined $.name
or undefined $.initials
?
But regardless, ($!name andthen .comb(/«./).join orelse Nil)
is only 20 characters longer.
It's also worth noting we currently have mirroring consistency between methodops and regexes:
.
→ "one thing".?
→ "maybe one thing".+
→ "one or more things".*
→ "zero or more things"The ./
/.//
breaks it (and also looks awful).
Also, don't we already have like 60-80 various methodcalls? Adding 6 more to that list just to save 20 characters of typing makes little sense to me.
The LTAness of .?
was mentioned above, and I can agree with it, but I don't see ./
as a solution to it, because (a) it (as currently proposed) doesn't actually check if the method can
be called on the object and (b) that the method can be called with the given args.
I'd normally suggest making a module first, but in this case, I don't think even that is worth the time. We already have plenty of options to deal with definedness of objects.
I think this is going backwards about it: you thought up a feature and now making up examples that might use it. Rather than having a bunch of real world code and going: "man, this code would be a lot simpler if this feature existed."
I disagree. The operator proposed here exists in Groovy, exists also in C# and CoffeeScript (is currently a stage 1 proposal for JS and in PHP as well IIRC). Ruby does it with .tap
, which is more similar to andthen
.
The current .?
, .+
, .*
we have are not features that exist in any other languages that I'm aware of (except maybe for Common Lisp's method combinators).
It's useful in plenty of circumstances:
method scope-lookup($name) {
%.v{$name} // $.parent.??lookup($name);
}
my $name = get-logged-in-user().??profile??.username // "Anonymous";
$node.as-a-thing.??process;
The ./ /.// breaks it (and also looks awful).
Solid +1 to both points you have here. Though it mirrors our coalescing operator //
...
It would be great if it short-circuits, as Swift’s and kotlin’s ‘?.’, where this:
$a.??uc.subst(“a”, “i”).say;
Would print “BLI” if $a == “bla” and would do nothing if its not defined... Em sex, 13 de abr de 2018 às 16:33, ven notifications@github.com escreveu:
I think this is going backwards about it: you thought up a feature and now making up examples that might use it. Rather than having a bunch of real world code and going: "man, this code would be a lot simpler if this feature existed."
I disagree. The operator proposed here exists in Groovy, exists also in C# and CoffeeScript (is currently a stage 1 proposal for JS https://github.com/tc39/proposal-optional-chaining and in PHP as well IIRC). Ruby does it with .tap, which is more similar to andthen. The current .?, .+, .* we have are not features that exist in any other languages that I'm aware of (except maybe for Common Lisp's method combinators).
It's useful in plenty of circumstances:
method scope-lookup($name) { %.v{$name} // $.parent.??lookup($name); } my $name = get-logged-in-user().??profile??.username // "Anonymous"; $node.as-a-thing.??process;
The ./ /.// breaks it (and also looks awful).
Solid +1 to both points you have here. Though it mirrors our coalescing operator //...
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/rakudo/rakudo/issues/1732#issuecomment-381239917, or mute the thread https://github.com/notifications/unsubscribe-auth/AAGF-mHLdb7frK2VHaST1IEOwSWJXQi4ks5toP2RgaJpZM4TTad0 .
Im not sure if I agree with myself…
$ perl6 -e '
my $a; $a = "bla";
$a andthen .uc.subst("A", "I").say
' BLI
Em 13 de abr de 2018, à(s) 22:27, Fernando Oliveira fernandocorrea@gmail.com escreveu:
It would be great if it short-circuits, as Swift’s and kotlin’s ‘?.’, where this:
$a.??uc.subst(“a”, “i”).say;
Would print “BLI” if $a == “bla” and would do nothing if its not defined... Em sex, 13 de abr de 2018 às 16:33, ven <notifications@github.com mailto:notifications@github.com> escreveu: I think this is going backwards about it: you thought up a feature and now making up examples that might use it. Rather than having a bunch of real world code and going: "man, this code would be a lot simpler if this feature existed."
I disagree. The operator proposed here exists in Groovy, exists also in C# and CoffeeScript (is currently a stage 1 proposal for JS https://github.com/tc39/proposal-optional-chaining and in PHP as well IIRC). Ruby does it with .tap, which is more similar to andthen. The current .?, .+, .* we have are not features that exist in any other languages that I'm aware of (except maybe for Common Lisp's method combinators).
It's useful in plenty of circumstances:
method scope-lookup($name) { %.v{$name} // $.parent.??lookup($name); }
my $name = get-logged-in-user().??profile??.username // "Anonymous";
$node.as-a-thing.??process; The ./ /.// breaks it (and also looks awful).
Solid +1 to both points you have here. Though it mirrors our coalescing operator //...
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/rakudo/rakudo/issues/1732#issuecomment-381239917, or mute the thread https://github.com/notifications/unsubscribe-auth/AAGF-mHLdb7frK2VHaST1IEOwSWJXQi4ks5toP2RgaJpZM4TTad0.
Proposal for a new operator similar to
.?
,.*
and so on. It should behave like.?
but instead of checking if the method exists or not, it should check if the invocant is defined:(
./
or even.//
because it's remotely related to the defined-or operator//
, but that's just an idea).PS: I don't know if this is the right place for discussing new language features, I thought I'd just try and maybe someone can point me to the correct place if this doesn't belong here.