When Should I Use Optionals and When Should I Use Non-Optionals with Default Values

When should I use optionals and when should I use non-optionals with default values?

The choice depends on what you model.

If a property of the object that you model may be absent completely, e.g. a middle name, a name suffix, an alternative phone number, etc., it should be modeled with an optional. A nil optional tells you that the property is not there - i.e. a person does not have a middle name or an alternative phone number. You should also use optional when you must distinguish between an empty object and a missing object.

If a property of the object must be set, and has a meaningful default, use an non-optional with a default:

class AddressList {
var addresses : [Address]
var separator : String = ";"
...
}

If users of your class need to change the separator, they have a way to do that. However, if they do not care about the separator, they can continue using the default without mentioning it in their own code.

Should Typing.Optional be used if the default value of the parameter is not None?

An argument having the type Optional[...] is not about whether the user has to specify it or not, but rather whether the function must account for the argument being potentially None. Given this example:

def my_func(a, b='hello'):
pass

b will be inferred as str because b, even if not provided by the caller, will have a non-None value. As such, attempting to override the default with None like this:

my_func(..., None)

would result in a MyPy error.

Note that I didn't say that b can't be explicitly annotated as Optional[str]. This is fine:

def my_func(a, b: Optional[str] = 'hello'):
pass

b can be annotated as Optional[str] here because b's default is str, and Optional[str] is a superset of str. Like you pinpointed, Optional[str] is str union None.

This enables the caller to explicitly pass None like this:

my_func(..., None)

Why NOT use optionals in Swift?

There are tons of reasons to NOT use optional. The main reason: You want to express that a value MUST be available. For example, when you open a file, you want the file name to be a string, not an optional string. Using nil as filename simply makes no sense.

I will consider two main use cases: Function arguments and function return values.

For function arguments, the following holds: If the argument needs to be provided, option should not be used. If handing in nothing is okay and a valid (documented!) input, then hand in an optional.

For function return values returning no optional is especially nice: You promise the caller that he will receive an object, not either an object or nothing. When you do not return an optional, the caller knows that he may use the value right away instead of checking for null first.

For example, consider a factory method. Such method should always return an object, so why should you use optional here? There are a lot of examples like this.

Actually, most APIs should rather use non-optionals instead of optionals. Most of the time, simply passing/receiving possibly nothing is just not what you want. There are rather few cases where nothing is an option.

Each case where optional is used must be thoroughly documented: In which circumstances will a method return nothing? When is it okay to hand nothing to a method and what will the consequences be? A lot of documentation overhead.

Then there is also conciseness: If you use an API that uses optional all over the place, your code will be cluttered with null-checks. Of course, if every use of optional is intentional, then these checks are fine and necessary. However, if the API only uses optional because its author was lazy and was simply using optional all over the place, then the checks are unnecessary and pure boilerplate.

But beware!

My answer may sound as if the concept of optionals is quite crappy. The opposite is true! By having a concept like optionals, the programmer is able to declare whether handing in/returning nothing is okay. The caller of a function is always aware of that and the compiler enforces safety. Compare that to plain old C: You could not declare whether a pointer could be null. You could add documentation comments that state whether it may be null, but such comments were not enforced by the compiler. If the caller forgot to check a return value for null, you received a segfault. With optionals you can be sure that noone dereferences a null pointer anymore.

So in conclusion, a null-safe type system is one of the major advances in modern programming languages.

Confused about Optional vs Default value -1

Obviously, nil is much cleaner. Because of -1 means nothing. This is just a magic word. It is difficult to support, refactor and handle this case.

Providing a default value for an Optional in Swift?

Update

Apple has now added a coalescing operator:

var unwrappedValue = optionalValue ?? defaultValue

The ternary operator is your friend in this case

var unwrappedValue = optionalValue ? optionalValue! : defaultValue

You could also provide your own extension for the Optional enum:

extension Optional {
func or(defaultValue: T) -> T {
switch(self) {
case .None:
return defaultValue
case .Some(let value):
return value
}
}
}

Then you can just do:

optionalValue.or(defaultValue)

However, I recommend sticking to the ternary operator as other developers will understand that much more quickly without having to investigate the or method

Note: I started a module to add common helpers like this or on Optional to swift.

Why optional constant does not automatically have a default value of nil

Not setting a read-only (constant) field with either an:

  • initialization expression
  • initializer

is almost certainly an indication of an error in your program.

Since you have no other opportunity to set the value of your let field, the value of the field is going to remain nil (or some other default). It is rather unlikely that a programmer would find such behavior desirable, and request it on purpose.

That is why Swift marks this situation as an error. On the other hand, if you actually wanted your String constant to remain nil, you could add an expression to set it to nil, and silence the error:

let owner: String? = nil // Pretty useless, but allowed

Optional field requires a necessary value

Optional[..] says that the parameter passed SHOULD BE of the mentioned type OR None, like in your case url should be of type HttpUrl OR None, but it does not mean that if it's not passed it will carry a default value. See the below example:

from typing import Optional

# In this b will should be passed as str or None
def test_with_optional(a: str, b: Optional[str]):
print (a,b)

# In this if b is not passed, it will carry a default value of None
def test_with_none(a: str, b: str=None):
print (a,b)

test_with_optional('1', None)
test_with_none('2')

test_with_optional('1') will throw error TypeError: test_with_optional() missing 1 required positional argument: 'b' because what it expects b either as a string or as None.

For your case your definition will be as below. Read more about optional parameters here

def create_ads_task(
template_id: int,
ad_account_id: str,
adset_ids: List[str],
placements: Dict[str, str],
locales: List[Locale],
url: Optional[HttpUrl]=None,
) -> List[Dict[str, Union[str, int]]]:
...body...

Uses for Optional

The main design goal of Optional is to provide a means for a function returning a value to indicate the absence of a return value. See this discussion. This allows the caller to continue a chain of fluent method calls.

This most closely matches use case #1 in the OP's question. Although, absence of a value is a more precise formulation than null since something like IntStream.findFirst could never return null.


For use case #2, passing an optional argument to a method, this could be made to work, but it's rather clumsy. Suppose you have a method that takes a string followed by an optional second string. Accepting an Optional as the second arg would result in code like this:

foo("bar", Optional.of("baz"));
foo("bar", Optional.empty());

Even accepting null is nicer:

foo("bar", "baz");
foo("bar", null);

Probably the best is to have an overloaded method that accepts a single string argument and provides a default for the second:

foo("bar", "baz");
foo("bar");

This does have limitations, but it's much nicer than either of the above.

Use cases #3 and #4, having an Optional in a class field or in a data structure, is considered a misuse of the API. First, it goes against the main design goal of Optional as stated at the top. Second, it doesn't add any value.

There are three ways to deal with the absence of a value in an Optional: to provide a substitute value, to call a function to provide a substitute value, or to throw an exception. If you're storing into a field, you'd do this at initialization or assignment time. If you're adding values into a list, as the OP mentioned, you have the additional choice of simply not adding the value, thereby "flattening" out absent values.

I'm sure somebody could come up with some contrived cases where they really want to store an Optional in a field or a collection, but in general, it is best to avoid doing this.

Why should Java 8's Optional not be used in arguments

Oh, those coding styles are to be taken with a bit of salt.

  1. (+) Passing an Optional result to another method, without any semantic analysis; leaving that to the method, is quite alright.
  2. (-) Using Optional parameters causing conditional logic inside the methods is literally contra-productive.
  3. (-) Needing to pack an argument in an Optional, is suboptimal for the compiler, and does an unnecessary wrapping.
  4. (-) In comparison to nullable parameters Optional is more costly.
  5. (-) The risk of someone passing the Optional as null in actual parameters.

In general: Optional unifies two states, which have to be unraveled. Hence better suited for result than input, for the complexity of the data flow.



Related Topics



Leave a reply



Submit