dart-lang / dart-lang/language
Problem: It is impossible to implement a Kotlin like copy method in Dart if you have "nullable" variables
- Dominant language
- TeX
- Stars
- 2.9k
- Forks
- 239
- Avg merge
- 2d 18h
- Merged PRs (30d)
- 14
Description
In Kotlin every data class is provided with a copy method. It copies the object and optionally it allows for setting new values. Here is an example:
```kotlin
import java.time.LocalDate
data class Todo(
val body: String = "",
val completed: Boolean = false,
val dueDate: LocalDate? = null
);
fun main() {
val todo = Todo(body = "Do this", dueDate = LocalDate.now().plusDays(1));
// user decide to remove the dueDate
print(todo.copy(body = "Investigate this some day", dueDate = null));
}
```
The copy method can be implemented like this in Kotlin:
```kotlin
fun copy(
body: String = this.body,
completed: Boolean = this.completed,
dueDate: LocalDate? = this.dueDate
): Todo {
return Todo(body, completed, dueDate);
}
```
However, doing the same in Dart doesn't work, as in Dart default values of parameters must be compile time constant:
```dart
class Todo {
final String body;
final bool completed;
final DateTime dueDate;
Todo({this.body = "", this.completed = false, this.dueDate});
Todo copy({
String body = this.body,
bool completed = this.completed,
DateTime dueDate = this.dueDate,
}) {
return Todo(body: body, completed: completed, dueDate: dueDate);
}
}
```
To allow for "default values" that are non-constant, often the following is recommended:
```dart
class Todo {
final String body;
final bool completed;
final DateTime dueDate;
Todo({this.body = "", this.completed = false, this.dueDate});
Todo copy({
String body,
bool completed,
DateTime dueDate,
}) {
body ??= this.body;
completed ??= this.completed;
dueDate ??= this.dueDate;
return Todo(body: body, completed: completed, dueDate: dueDate);
}
}
```
In many cases this works well. It's different in a subtle way, passing `null` has a special meaning now. It means, "give me the default value, whatever that is". This works well for "non-nullable" variables, as null can now be used to mean something different. But also for nullable variables if the default value is null (the two meanings of `null` overlap now).
However, it makes one case impossible, if you have a "true" nullable variable with a non-null default value. In this case, `dueDate` is truely nullable. `null` means here that there exists no `dueDate` for this todo. However, this makes it impossible to use the copy method to remove the due date of the todo:
```dart
main() {
final todo = Todo(body: "Do this", dueDate: DateTime.now().add(Duration(days: 1)));
print(todo.copy(body: "Investigate this some day", dueDate: null));
// prints:
// {
// "body": "Investigate this some day",
// "completed": false,
// "dueDate": "2018-12-18 11:11:11.607"
// }
}
```
https://dartpad.dartlang.org/ccf1d10f1e5279ba93eef24c018b6290
In summary: *It is impossible in Dart to give a "nullable" optional parameter a default non-constant value.*
I think the most obvious way to solve this would be to allow optional parameters to have a non-constant default value. What is the reason for this restriction? I can not find any language with the same restrictions for the default values of parameters.
Contributor guide
Assessment
This issue has not been assessed yet.