def do_something(n : Int32?)
value = 300
I understand the reasoning, but it just bit me when I (by mistake) did exactly that: assigned under the condition instead of above it, and then passed it on to a nillable arg.
Is there an argument to make for an optional compile mode that either checks the usage before assignment, or even disables nillable assumptions altogether?
Maybe even a warning on any Nils except return types? I can imagine myself preferring explicit sentinels like
Type | EmptyValue instead of
Type? in some situations.
I mean if you don’t want to pass nil to the method, make the type restriction not nilable?
I understand the reasoning, but it just bit me when I (by mistake) did exactly that: assigned under the condition instead of above it, and then passed it on to a nillable arg
I think this is a good point.
We copied this behavior from Ruby: when you don’t assign to a variable in all branches it just gets to be
Nil. This is “good” because you don’t need to write
value = nil on the other branch. This is bad because the
nil assignment is implicit so it can’t be seen. An immediate consequence of this is that if you try to invoke something on
value you will get a compile-error but there’s no code to show where did that
nil come from (well, I think the compiler will point to the
if but it’s still not clear). The same happens with a
case only it’s much worse: with such compiler error you have to check all branches to see where did you make a mistake.
Maybe we can remove this behavior? Then you will get a compile error when you forget to assign a variable in one branch, removing the “nil if not assigned” behavior.
This would make code easier to read and follow at the cost of having to type a bit more. I really don’t mind that.
I wonder how much code will break because of this. Maybe not that much, I don’t know…
What about making a new type to represent this? Like
Undefined. Would then be able to separate the case where a value is actually
nil, or was declared but never assigned a value.
Well, it would be one way to actually implement that. But I don’t know what you would actually do with an
Undefined value other than wanting a compile error when it’s created.
If it is assigned in all branches it works as expected. I feel comfortable with no promoting the variables that only appear on one side of the branch.
I see them as more consistent with variables declared inside a block. Although Ruby has
do |local; parent| to state the scope of the variables.
value being defined? Wtf
edit: I read @asterite’s post. I understand why now, thank you.
@OP, just do this https://play.crystal-lang.org/#/r/78sk
def do_something(n : Int32)
value = 300
Yes, I think I will drop all the nils from my code from now on, however this will not work with the code I don’t own.