-
-
Notifications
You must be signed in to change notification settings - Fork 115
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
feat(compiler)!: Labeled and default arguments #1623
Conversation
This is awesome! Apologies in advance for the syntax bikeshedding 😅, but personally I find the
syntax where the label comes first a bit unclear; if I were reading a function defined like this I feel I would very likely have to double-take to remember which one is the label and which is the reference (I even had to double-take to make sure I didn't mix them up after writing the example above 😆). In the community meeting I believe you brought up doing the |
Yeah, that's totally fair, this syntax comes from Swift. The thing that sucks about using But in this one case, you'd be using
That first I don't think the Swift syntax is perfect, but I do think it's better than overloading Certainly still open to suggestions for syntax 😄 |
Something else that I thought of that I probably shouldn't bring up due to the sheer level of bikeshedding this could cause: Some time ago we decided that we were going to use double colon The syntax could then be:
And of course still just
for short. |
Which would be separate from
and distinguish
|
I see, assuming that explicit typing is changed to
|
22616b2
to
6d63369
Compare
@alex-snezhko Decided to drop the requirement that labels be required, so it avoids the whole issue entirely. Argument labels are just inferred, and this covers 95% of cases just fine. The only time it's less than ideal is when an argument label is unable to be inferred (such as doing a destructure) in a module signature, but you can do Doing it this way keeps the language complexity down tremendously, and I believe now this is a non-breaking change. |
Awesome! |
24e01d7
to
167202d
Compare
e932a6b
to
7b372c1
Compare
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.
A couple questions and some cleanup requests. I also made some suggestions on the error messages because I'd prefer to not use optional
in the error messages.
f555013
to
3f4113b
Compare
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.
Let's GO!
* feat(compiler)!: Labeled and default arguments * graindocs * remove formatting fixme * make loc required * Use helper for argument types * impossible case instead of assert false * use label name for generated options * use more standard user unaccessible name * standardize around call over apply * rework error messages * Add out of order use of argument in default value test * add order test
Closes #388
Draft because this needs tests, code cleanup, parser messages updates, and as always, because things may still change 🙂
Optional and Labeled Arguments
This PR introduces the ability to provide default values for function arguments and optionally omit those arguments when calling the function. Additionally, this PR allows regular arguments to be supplied by name if desired. If you are familiar with how "keyword arguments" work in Python, you'll find this to be fairly similar.
Declaring an argument as optional
For an argument to be omitted by the caller, a default value must be provided.
The
join
function here can be called without theseparator
argument, and the default value will be used instead:We can optionally provide our own separator:
And we can provide the
separator
argument in any position:Type signatures
The type signature of
join
is(?separator: String, strings: List<String>) -> String
. The?
beforeseparator
indicates that this parameter is optional. The name of the parameterstrings
is also included in the type signature.Because the parameter name
strings
is also included in the type signature, we can also provide that parameter with its label:And of course, we can provide these in whatever order we like:
Typechecking
Two function types are considered equal if, with labels removed, the types are equal. This is to allow function authors to name their function parameters whatever they like and still be able to pass them as arguments to other functions. For example, even if
List.map
had type(fn: (value: a) -> b, list: List<a>) -> List<b>
, your mapper function would not need to call its argumentvalue
.A function that accepts an optional argument does not typecheck with a function that does not accept that optional argument, even if all other types are equal.
If a function type is inferred by arguments that are supplied positionally, they are unlabeled.