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.
- (+) Passing an Optional result to another method, without any semantic analysis; leaving that to the method, is quite alright.
- (-) Using Optional parameters causing conditional logic inside the methods is literally contra-productive.
- (-) Needing to pack an argument in an Optional, is suboptimal for the compiler, and does an unnecessary wrapping.
- (-) In comparison to nullable parameters Optional is more costly.
- (-) 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
Firebase Firestore Not Updating Email Verification Status
Using Swift to Disable Sleep/Screen Saver for Osx
How to Set Countdowntimer Mode in Datepicker on Swiftui
Load a Collada (Dae) File into Scnnode (Swift - Scenekit)
Mkpointannotations Touch Event in Swift
Datepicker Using Time-Interval in Swiftui
How to Delete from Firebase Database
Swift: Does Closure Have References to Constants or Variables
Undefined Behavior, Or: Does Swift Have Sequence Points
Xcode 8.2.1 Not Showing Documentation Description on Autocomplete
How to Handle Multiple Network Call in Alamofire
Adjust Nsvisualeffectview Blur Radius and Transparency
Is It the Right Way Using '[Weak Self]' in Swift Closure
The File "Xxx.Mp4" Couldn't Be Opened Because You Don't Have Permission to View It
Scenekit Some Textures Have a Red Hue