dart-lang / dart-lang/language

Problem: It is impossible to implement a Kotlin like copy method in Dart if you have "nullable" variables

Open
#137 13 comments 18 reactions 0 assignees View on GitHub
feature
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.