required function argument with default UUID value

389 views
Skip to first unread message

Guyren Howe

unread,
Apr 19, 2021, 5:00:51 PM4/19/21
to Dart Misc
How do get a required string argument to a function to default to a Uuid?

I have this:

Foo get_or_create({
  final String id = Uuid().toString(),
})
but I’m told “ The default value of an optional parameter must be constant”.
const Uuid().toString() 
makes no difference. 

Bob Nystrom

unread,
Apr 19, 2021, 9:16:24 PM4/19/21
to General Dart Discussion
Default values must be constant expressions known at compile time. I'm guessing that in this code you want it to generate a new Uuid each time the function is called if the user doesn't pass one in. In that case, do:

Foo getOrCreate({String? id}) {
  id ??= Uuid().toString();
  // ...
}

Cheers!

– bob

--
For more ways to connect visit https://dart.dev/community
---
You received this message because you are subscribed to the Google Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to misc+uns...@dartlang.org.
To view this discussion on the web visit https://groups.google.com/a/dartlang.org/d/msgid/misc/95454FBA-0E41-454E-AC37-E05D3B69BBBB%40gmail.com.

Mathieu Blais D'Amours

unread,
Apr 20, 2021, 2:13:09 AM4/20/21
to mi...@dartlang.org
Hello,

You’d have to do that as part of the function’s body:

Foo get_or_create({String? id}) {
  id ??= Uuid().toString():
}

Default arguments need to be constants, as the analyzer pointed out, and Uuid().toString() clearly is not a constant.
--
For more ways to connect visit https://dart.dev/community
---
You received this message because you are subscribed to the Google Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to misc+uns...@dartlang.org.
To view this discussion on the web visit https://groups.google.com/a/dartlang.org/d/msgid/misc/95454FBA-0E41-454E-AC37-E05D3B69BBBB%40gmail.com.
--



LE COURAGE DE FAIRE AUTREMENT

Mathieu D'Amours - CTO mathieu@ braver.health +1 418 933-6233 95, rue Saint-Joseph E, suite 250 Québec, QC G1K 3A6

Guyren Howe

unread,
Apr 20, 2021, 11:12:24 AM4/20/21
to mi...@dartlang.org
Uuid() returns a constant. There ought to be a way to tell the compiler I’d like the results of .toString() to be a constant.

Guyren Howe

unread,
Feb 14, 2023, 11:22:38 PM2/14/23
to Dart Misc, Guyren Howe
What is the most elegant way to have a class with a final string state passed in the constructor, with a computed default for that value?

I regard it as inelegant eg if the string that is going to have a value one way or another has to be declared String?

```
class  A {
  final String a;

  A(String this.a = Uuid());
}
```

 is what I'd like to be able to write. `a` will have a value when the object is initialised, so it should be able to be final.

Guyren Howe

unread,
Feb 15, 2023, 12:09:53 AM2/15/23
to Dart Misc, Guyren Howe
I guess a factory constructor is the most elegant way. But this is an abstraction leak that I don't like.

James D. Lin

unread,
Feb 15, 2023, 12:37:09 AM2/15/23
to mi...@dartlang.org, Guyren Howe
Oops, I meant `A([String? a]) : a = a ?? Uuid();` if you want callers to be able to omit the argument instead of explicitly passing null.

Also, to clarify, the member variable can stay non-nullable.

- James

On Tue, Feb 14, 2023 at 9:23 PM James D. Lin <jame...@gmail.com> wrote:
`A(String this.a = Uuid())` isn't legal for a number of reasons.  If `Uuid` doesn't have a `const` constructor (and presumably it doesn't if it's a UUID), you can do:

`A(String? a) : a = a ?? Uuid();`

- James


James D. Lin

unread,
Feb 15, 2023, 12:37:20 AM2/15/23
to mi...@dartlang.org, Guyren Howe
`A(String this.a = Uuid())` isn't legal for a number of reasons.  If `Uuid` doesn't have a `const` constructor (and presumably it doesn't if it's a UUID), you can do:

`A(String? a) : a = a ?? Uuid();`

- James


Lasse R.H. Nielsen

unread,
Feb 23, 2023, 5:01:55 AM2/23/23
to mi...@dartlang.org, Guyren Howe
There is no perfect solution, because if a parameter is optional, it needs to have a default value, or be nullable (and have `null` as implicit default value).
A default value must be constant.

If you want to do a computation if no argument is passed, you need to be able to recognize that no argument was passed, which you can only do by recognizing the default value.

I'd use a nullable parameter. Then explicitly passing `null` is a way to trigger the default value too, which makes it easy to forward parameters from one function to another if necessary.

I don't see it as "ugly" to have a nullable type for something, because `null` means "no value provided". Turning that into a value later is reasonable, expectable, idiomatic and therefore readable. People seeing a nullable optional parameter will just think that passing `null` means the same as omitting an argument.


The next alternative would be using a recognizable non-`null` default value, say `A({String a = ""}) : a = a != "" ? a : Uuid().toString();`.

That requires there to be at least one value of the type which cannot be a valid argument. But at that point, you should probably be validating the inputs too, if some of them are not valid or reasonable. 
I see no advantage over just using a nullable argument. Users still need to know that some argument values are significant, and now they can't rely on it being `null`, and forwarding parameters has to take extra care to use the same default values.

Then you could have two separate methods:

    A({String this.a});
    A.fresh() : a = UUid().toString();

Dart doesn't have overloading, so they need different names.
If the behavior is significantly different depending on whether the argument is there or not, like I'd consider "get" vs. "create", I'd prefer two methods.
If it's really just choosing a default value for the same operation, it's fine to use the same function.

Then there is the hacky option of hiding the nullability, but that only works for instance methods:

   abstract class A {
     factory A() = _A();
     void something({String a});
   }
   
   class _A implements A {
     void something({String? a}) {
       a ??= UUid().toString();
       // ...
     }
   }

If you really, really don't want people passing in `null`, even if they are allowed to provide no argument, this can at least prevent them from doing
that in typed code (it still works for dynamic calls).
And again, I don't see that as a goal. 

Embrace that nullable and optional go hand in hand, and let `null` mean the same as no value every time.

/L




--
Lasse R.H. Nielsen - l...@google.com  
'Faith without judgement merely degrades the spirit divine'
Google Denmark ApS - Frederiksborggade 20B, 1 sal - 1360 København K - Denmark - CVR nr. 28 86 69 84
Reply all
Reply to author
Forward
0 new messages