-
-
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
Syntactic type for "No Argument" #12191
Comments
Somewhat related: https://forum.crystal-lang.org/t/rfc-undefined-type/2695. |
It would be interesting to know why Avram doesn't use My answer is that the language can't add a feature for every little thing a library defines or uses. |
I also don't see how we can change that. If instead of |
Avram explanation for Nothing type: # This class is used in various places
# where the question of "Did I not pass in anything or did I pass in nil?"
# needs to be answered. I guess it's used for telling the user whether it defaults to nil or not? |
I think the difference is:
So basically These semantics are related to |
Also related: #11106 As for why we use I would say if we had an easier way to highlight the difference between what was passed in, and the missing arg type values, that would be a much better improvement. We can chat more about that in #11106 |
I tried to split this off of #11106 as it is IMO a simpler and independent issue. I know that it's a convention of Crystal, and Ruby before, that everything is a value. But doesn't it sound kind of like we're discussing a Void type? |
FWIW |
Void in Crystal is essentially an alias for Nil and is only useful for inter-language function definitions. We're really looking for a type that is never a valid value for anything, but can still be passed. |
Is there such a thing in other languages? I'd like to understand what's the actual problem. Is it that the output of the error message becomes too verbose? Could we maybe show one argument per line in errors? I think this is just a duplicate of the other issue to improve error messages 🤔 |
I agree with @asterite here that I'm not sure if having such a type would even help in this case. I think just having an easier way to read the compiler error would be a much better benefit. Taking what Avram does for an example class SaveUser
def initialize(@id : Int64 | Nothing = Nothing.new, @created_at : Time | Nothing = Nothing.new, @updated_at : Time | Nothing = Nothing.new)
end
end This allows you to do: SaveUser.new
SaveUser.new(id: 1)
SaveUser.new(id: 1, created_at: Time.utc)
SaveUser.new(id: 1, created_at: Time.utc, updated_at: Time.utc) but also prohibits you from doing: SaveUser.new(id: nil) Since the args have defaults, they're optional to pass them in, and since none of them have Now say we added some sort of type called class SaveUser
def initialize(@id : Int64 | Undefined, @created_at : Time | Undefined, @updated_at : Time | Undefined)
end
end If I did # doing this
@id = uninitialized Int64
# as opposed to
@id = Undefined I do agree that the UX we have currently isn't the greatest for newcomers, and I'd love a better solution, but I'm just not sure that a new type would make it any better in this case. |
My main issue is that Regarding whether other languages have things like this, one example is that IEEE 488 has both signaling and quiet NaN values, and they mean that an operation has not resulted in any valid numeric value. |
Something I wonder is what the signature for a lib struct's constructor should look like: lib Foo
struct Bar
x : Int32
y : Bar*
end
end
# Error: wrong number of arguments for 'Foo::Bar.new' (given 1, expected 0)
#
# Overloads are:
# - Foo::Bar.new()
Foo::Bar.new 1 The error message is obviously wrong, because these are all valid calls: a = Foo::Bar.new x: 1, y: nil
b = Foo::Bar.new y: pointerof(a) It comes from the fact that lib struct constructors are rewritten to something like the following, which explains why only the parameter-less constructor is necessary: a = begin
__temp_1 = Foo::Bar.new
__temp_1.x = 1
__temp_1.y = nil
__temp_1
end
b = begin
__temp_2 = Foo::Bar.new
__temp_2.y = pointerof(a)
__temp_2
end If we were to define a constructor for diagnostics purposes, it might look like: lib Foo
struct Bar
def initialize(*, x : Int32 = ..., y : Bar* = ...); end
end
end The parameters are not nilable, because actually passing |
Avram builds arbitrarily long argument lists for setting database records - an argument for every field. Each of these fields is set optionally, and thus the argument type is always
Type | Avram::Nothing = Avram::Nothing.new
. In long argument lists you end up with many repetitions of this cybercrud, obscuring the relevant information.How would we better do this? Nil is the natural type to use for this, so we'd at least have
Type | Nil = nil
, but I was thinking of adding a language convention to indicate that no argument was an option, which the compiler would understand and print as a type without the value assignment. So,Type | Nothing
, for example.The text was updated successfully, but these errors were encountered: