Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Checking, if optional parameter is provided in Dart

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?

like image 644
Anaeijon Avatar asked Jun 14 '15 14:06

Anaeijon


2 Answers

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'
like image 185
john mason Avatar answered Sep 18 '22 02:09

john mason


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.

like image 34
lrn Avatar answered Sep 22 '22 02:09

lrn