-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature: Add default result to try method #3650
Feature: Add default result to try method #3650
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the addition. It is aligned with Hash#fetch(key, default)
actually.
-
(mandatory) run the formatter, otherwise travis will complain.
-
(opinion) I would leave a single
Nil#try
method with the same default as Object. The default would be duplicated but it is easier to follow I think. WDYT? -
(opinion) rename
if_nil_value
todefault
so it is the same asHash#fetch
.
|
||
it "yields self when not nil even when default given" do | ||
obj = "An arbitrary string" | ||
obj.try("another value") {|o| o}.should eq(obj) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{|o| o}
-> &.itself
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, I did that idiomatic crystal in other examples, but wanted to show that this way resulted in the same behaviour
obj = /.* (.)(.)(.)/.match("An arbitrary string") | ||
typeof(obj).should eq(Regex::MatchData | Nil) | ||
typeof(obj.try(&.size)).should eq(Int32 | Nil) | ||
typeof(obj.try(0, &.size)).should eq(Int32) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
Ironically, it was originally named default, but I wanted to indicate that it was only used if the value was nil. Will change it back to default, as well as the other items you identified. |
@bcardiff Do you want me to flatten the PR to one commit, or keep each commit separate? |
|
(also the above is a strong idiom in Crystal, the usage of |
@asterite wouldn't that misbehave if |
An alternative would be to have something as But I think the proposed change to |
problem with || that need to add () when chains methods: seems better: |
@kostya If you have to do that, it's a sign that you should split the expression into multiple variables. |
Rational: To facilitate chaining of method calls without successive try wrappers, the addition of a default value to the try call that is returned when the receiver is nil allows the returned type to be made homogeneous. ``` my_string_or_nil.try(&.size) # => could be Nil or Int32 my_string_or_nil.try(0, &.size) # => will always be Int32 ``` While this is similar to `(my_string_or_nil.try(&.size) || 0)`, the use of a default in try more elegantly solves the case when the result could be false or nil.
f1f7320
to
3fce3e7
Compare
As a newbie to crystal, I'm wondering why this option would be done as: my_string_or_nil.try(0, &.size) # => will always be Int32 instead of: my_string_or_nil.try(&.size, 0) # => will always be Int32 (I have no opinion about which would be better, I'm just wondering which one is "more crystal-ish") |
@drosehn The block argument must always come last in the call |
Ah, yes, okay. I understand it better now. |
Let me use a different example, to see if I understand @asterite 's earlier point. Given: my_craZclass_or_nil.try(CrazyClass.new, &.size).craZ_meth this statement will always create a new CrazyClass object, even when |
@drosehn Exactly! |
@drosehn, The point @asterite makes is valid, though your example makes incorrect use of the proposed The intent for this addition is in the spirit of the 80/20 rule where the default is already pre-computed or a simple literal and better handles the case where the receiver might be falsy or the output of the block is falsy. As @bcardiff points out, this has a similar benefit and limitation to using the default on Hash#fetch. For where the default would need to be compute or memory intensive, then it is recommended to use the |
Oops, my mistake. I missed one change when I copied the original example to make my new one. What I meant was something more like: my_craZclass_or_nil.try(CrazyClass.new, &.craZ_dup).craZ_meth I'm not saying I have any good reason to type that specific statement, but right now I'm too tired to think of a more sensible example. I liked the idea when looking at the example of Disclaimer: I haven't written all that much crystal, so I know I don't have enough experience to rate the feature. I'm asking questions here just to make sure I understand what people are saying. (and I'm typing in this reply just to make sure the fixed version of my example does make sense!) |
#codetriage Reading through, it seems like there's a couple of opinions expressed here. @dennisjbell wants the output of @asterite and @RX14 are in support of using existing methods such as How do we drive this discussion to a conclusion? Should |
In my biased opinion, this issue has been discussed to completion and can be closed. |
Closing. Above discussions made it clear that |
Rational:
To facilitate chaining of method calls without successive try
wrappers, the addition of a default value to the try call that is
returned when the receiver is nil allows the returned type to be made
homogeneous.
While this is similar to
(my_string_or_nil.try(&.size) || 0)
, theuse of a default in try more elegantly solves the case when the result
could be false or nil.