Checking, if optional parameter is provided in Dart
Asked Answered
B

4

14

I'm new to Dart and just learning the basics.

The Dart-Homepage shows following:

It turns out that Dart does indeed have a way to ask if an optional parameter was provided when the method was called. Just use the question mark parameter syntax.

Here is an example:

void alignDingleArm(num axis, [num rotations]) {
  if (?rotations) {
    // the parameter was really used
  }
}

So I've wrote a simple testing script for learning:

import 'dart:html';

void main() {

  String showLine(String string, {String printBefore : "Line: ", String printAfter}){
    // check, if parameter was set manually:
    if(?printBefore){
      // check, if parameter was set to null
      if(printBefore == null){
        printBefore = "";
      }
    }
    String line = printBefore + string + printAfter;
    output.appendText(line);
    output.appendHtml("<br />\n");
    return line;
  }

  showLine("Hallo Welt!",printBefore: null);

}

The Dart-Editor already marks the questionmark as Error:

Multiple markers at this line
- Unexpected token '?'
- Conditions must have a static type of 
 'bool'

When running the script in Dartium, the JS-Console shows folloing Error:

Internal error: 'http://localhost:8081/main.dart': error: line 7 pos 8: unexpected token '?'
if(?printBefore){
   ^

I know, that it would be enough to check if printBefore is null, but I want to learn the language.

Does anyone know the reason for this problem? How to check, if the parameter is set manually?

Bb answered 14/6, 2015 at 14:33 Comment(0)
G
10

The feature existed at some point in Dart's development, but it was removed again because it caused more complication than it removed, without solving the problem that actually needed solving - forwarding of default parameters.

If you have a function foo([x = 42]) and you want a function to forward to it, bar([x]) => f(x);, then, since foo could actually tell if x is passed or not, you actually ended up writing bar([x]) => ?x ? foo(x) : foo();. That was worse than what you had to do without the ?: operator.

Ideas came up about having a bar([x]) => foo(?:x) or something which pased on x if it was present and not if it was absent (I no longer remember the actual proposed syntax), but that got complicated fast, fx converting named arguments to positional - bar({x,y}) => foo(?:x, ?:y); - what if y was provided and x was not. It was really just a bad solution for a self-inflicted problem.

So, the ?x feature was rolled back. All optional parameters have a default value which is passed if there is no matching argument in a call. If you want to forward an optional parameter, you need to know the default value of the function you are forwarding to.

For most function arguments, the declared default value is null, with an internal if (arg == null) arg = defaultValue; statement to fix it. That means that the null value can be forwarded directly without any confusion.

Some arguments have a non-null default value. It's mostly boolean arguments, but there are other cases too. I recommend using null for everything except named boolean parameters (because they are really meant to be named more than they are meant to be optional). At least unless there is a good reason not to - like ensuring that all subclasses will have the same default value for a method parameter (which may be a good reason, or not, and should be used judiciosuly).

If you have an optional parameter that can also accept null as a value ... consider whether it should really be optional, or if you just need a different function with one more argument. Or maybe you can introduce a different "missing argument" default value. Example:

abstract class C { foo([D something]); }
class _DMarker implements D { const _DMarker(); }
class _ActualC {
  foo([D something = const _DMarker()]) {
    if (something == const _DMarker()) {
      // No argument passed, because user cannot create a _DMarker.
    } else {
      // Argument passed, may be null.
    }
  }
}

This is a big workaround, and hardly ever worth it. In general, just use null as default value, it's simpler.

Gallimaufry answered 15/6, 2015 at 6:7 Comment(4)
Unfortunately, this seems to make it impossible to implement a copyWith method when it is valid for a property to be set to null. One is forced to use the null-coalescing operator, so passing in null for that property will instead ignore it and use the existing non-null value.Shod
Yes, that is an unfortunate side-effect. If all values are valid, you cannot distinguish an actual null value from an omitted value. In that case you might want to introduce a private marker class and use instances of that as default argument, then you know that it's impossible for the client to pass an instance of that class, so the default value is distinguishable from any client value. If your arguments are typed, you might need one class per type (you can do class _XMarker extends _Marker implements Foo for a default value of a Foo parameter). If the type is int, you're stuck :(Gallimaufry
Is there any workaround for that problem, @KentBoogaart? Only thing that comes to my mind is creating custom methods for setting to null specific fields of the object, but does not look too clean for meQuasimodo
freezed can create typed unions to represent the 'null' state in a way that requires the caller to unpack it type-safely. Or if you have a nullable bool you can just replace it with an enum.Estival
S
4

I was trying something similar:

This does not work

widget.optionalStringParameter ? widget.optionalStringParameter : 'default string'

This works

widget.optionalStringParameter != null ? widget.optionalStringParameter : 'default string'

This also works

widget.optionalStringParameter ?? 'default string'
Sexpot answered 12/5, 2021 at 22:23 Comment(0)
I
0

There was support for checking if an optional parameter was actually provider in early Dart days (pre 1.0) but was removed because it causes some troubles.

Instauration answered 14/6, 2015 at 14:41 Comment(2)
Why do you need different null types. What is the use case of actually knowing if the value is set or not?Conformity
The ? operator didn't work nicely with call forwarding. That is, forwarding a call with optional arguments required an exponential (in the number of arguments) amount of code.Riegel
H
-1

Using setUp() to specify each field individually using late field in the test. This way I can nullify the field before object is created.

Hypallage answered 19/3 at 22:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.