Skip to content
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

Solution: Make a parameter optional by giving it a default value #16

Open
kasperpeulen opened this issue Aug 16, 2018 · 10 comments
Open
Labels
feature Proposed language feature that solves one or more problems

Comments

@kasperpeulen
Copy link

kasperpeulen commented Aug 16, 2018

Solution to #15.

As @munificent has pointed out before, in many mainstream languages it is common to make a parameter optional by giving it a default value.

 function foo(i = 123)          // JavaScript
      def foo(i = 123)          // Python
      def foo(i = 123)          // Ruby
 function foo($i = 123)         // PHP
     void foo(int i = 123)      // C++
     void Foo(int i = 123)      // C#
      fun foo(i: int = 123)     // Kotlin
procedure foo(i: integer = 123) // Pascal
      def foo(i: int = 123)     // Scala
     func foo(i: Int = 123)     // Swift
 function foo(i: number = 123)  // TypeScript

Given that Dart is a language that strives to be familiar, I think that dart Dart should adopt this syntax as well. The current syntax is also unnecessary verbose, especially in the case of a required named parameter.

// BEFORE
foo(bool required)
foo([bool optional = false])
foo({@required bool named})
foo({bool optionalNamed = false})

// AFTER
foo(bool required)
foo(bool optional = false)
foo({bool named})
foo({bool optionalNamed = false})

This is a breaking change, Dart gives a optional parameter with no default value implicitely the value null. With this syntax, you would have to be explicit about this:

// BEFORE
foo([MyObject nullableObject])
foo({MyObject nullableObject})

// AFTER
foo([MyObject nullableObject = null])
foo({MyObject nullableObject = null})
@Hixie
Copy link

Hixie commented Aug 16, 2018

We would need really really strong evidence that this is a problem to justify a breaking change of this magnitude...

@kasperpeulen
Copy link
Author

@Hixie I agree if you see this solution as independent of any other breaking changes. But I think other breaking changes are suggested such as types that are non nullable by default.

If this will happen, there will probably be some codemod released to migrate code to Dart 3 in a non breaking way. If we already have to migrate code to Dart 3, I don't see a reason to also include an automatic fix in the codemod for this proposal.

@matanlurey
Copy link
Contributor

Non-nullable types (by default, or otherwise) would basically allow your change "for free". Consider:

void main() {
  // Compile-time error: `a` is non-nullable but no value was passed.
  method();

  // Compile-time error: `a` is non-nullable.
  method(a: null);

  // OK.
  method(a: 5);
}

void method(
  a,
  // Nullable
  b?,
) {
  // ...
}

Or, if ! meant non-nullable without the breaking change to make everything NNBD:

void main() {
  // Compile-time error: `a` is non-nullable but no value was passed.
  method();

  // Compile-time error: `a` is non-nullable.
  method(a: null);

  // OK.
  method(a: 5);
}

void method(
  // Non-nullable (similar to what C# is doing to transition to NNBD)
  a!,
  b,
) {
  // ...
}

@munificent
Copy link
Member

I was initially in favor of this syntax too. Unfortunately, it doesn't work well outside of function declarations. There are other places where you write a function type and need to be able to indicate which parameters are optional:

// In a typedef:
typedef TakeOneOrTwo = void Function(int a, [int b]);

// In a type annotation:
void Function(int a, [int b]) takeOneOrTwo = ...;

In those places, you can't specify a default value. The default value isn't part of the type, and putting a default value there wouldn't be meaningful.

@matanlurey
Copy link
Contributor

matanlurey commented Aug 16, 2018

@munificent: If we had non-nullable types, could you write:

// In a typedef:
typedef TakeOneOrTwo = void Function(int! a, [int b]);

// In a type annotation:
void Function(int! a, [int b]) takeOneOrTwo = ...;

(Or NNBD):

// In a typedef:
typedef TakeOneOrTwo = void Function(int a, [int? b]);

// In a type annotation:
void Function(int a, [int? b]) takeOneOrTwo = ...;

I realize this is breaking (now you can't pass null to a), though I wonder how breaking it is in practice, and it seems to sort of cover both of the cases.

@munificent
Copy link
Member

I was talking to Leaf about doing just this thing for named parameters. It might make sense for named parameters, but it breaks down for positional parameters. If changing a parameter's type to something nullable implicitly makes it optional, it means you can't change a non-trailing parameter type without messing with the arity and signature of the method in a likely breaking way.

Also, it would cause weird things like making every positional parameter who's type is Object (which permits null) or dynamic implicitly be optional. I think that might be reasonable for named parameters where omitting one has no bearing on the others. But it's really weird for positional parameters where omitting one shifts the others forward.

@matanlurey
Copy link
Contributor

matanlurey commented Aug 16, 2018

Or replace optional-positional parameters with overloads and fix that problem once and for all!

@munificent
Copy link
Member

Now you have a combinatorial number of problems. :)

@lrhn lrhn changed the title Solution: Make a parameter optional by given it a default value Solution: Make a parameter optional by giving it a default value Aug 20, 2018
@kasperpeulen
Copy link
Author

kasperpeulen commented Aug 22, 2018

@munificent Do you know how other languages tackle this issue with typedefs? For example, Swift and Kotlin?

@munificent
Copy link
Member

I believe most other languages don't make optionality part of the type, just part of an invocation. A function type always has a single explicit signature with a fixed arity so there's no need to specify optional parameters in a typedef.

Dart is a little special in that we almost have "first-class overloads". When you take a reference to a function that takes optional parameters, you get an object that you can call at runtime with multiple different signatures.

@mit-mit mit-mit added the feature Proposed language feature that solves one or more problems label Nov 8, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Proposed language feature that solves one or more problems
Projects
None yet
Development

No branches or pull requests

5 participants