6

Swift allows us to use the shorthand notation str! to unwrap an optional. But what if we want to do the opposite?

Say I have a variable:

var str = String() // String

Is there any shorthand notation to convert this to an optional (i.e. String? or String!)?

(E.g. I want to do something like var strOptional = ?(str).)

Alternatively, if there is no shorthand for this notation, how can I convert it to an optional without explicitly mentioning its type (e.g. I don't want to mention String).

In other words, I know that I can wrap a variable as an optional with any of these methods:

var strOptional = str as String?
var strOptional: String? = str
var strOptional = String?(str)

... but in each case, I must explicitly write String.

I would rather write something like: var strOptional = str as typeof?(str), if there is no shorthand syntax. (The advantage is that if the variable's type is frequently changed in the code base, it would be one less place to update.)


As far as a real world example of where this would be useful, imagine I want to use an AVCaptureDevice and I use the following code:

let device = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
device.lockForConfiguration(nil)

lockForConfiguration() will crash at runtime on a device that has no video camera, and the compiler won't warn me about it. The reason is that defaultDeviceWithMediaType may return nil according to the documentation[1], yet it is defined to return a AVCaptureDevice!.

To fix a faulty API like this, it would be nice to do something like:

let device = ?(AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo))

... to get a AVCaptureDevice?, and have the compiler catch any mistakes I might make.

Currently, I must resort to the more verbose:

let device: AVCaptureDevice? = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)

Another example:

In this case, I want to give a default value to my variable that is a string, but later on, I may want to assign it a nil value.

var myString = "Hi"
// ...
if (someCondition()) {
    myString = nil // Syntax error: myString is String
}

Currently I have to resort to var myString: String? = "Hi" but something like var myString = ?("Hi") would be less verbose.


[1] If you open AVCaptureDevice.h, you will see the following documentation about the return value: "The default device with the given media type, or nil if no device with that media type exists."

Senseful
  • 86,719
  • 67
  • 308
  • 465
  • Hmm, I deleted my answer. You have enough rep to still see it. Most of my answer still applies, but I think `AVCaptureDevice` is a holdout. When Apple first introduced Swift, basically *everything* returned implicitly unwrapped optionals, but they're supposed to be converting all of these over to either non-optionals or regular optionals. That's what the Objective-C nullability macros are for. – nhgrif Apr 20 '15 at 22:01
  • @nhgrif: Thanks for the comments. I added another example of where this might be useful which doesn't involve Apple's frameworks. – Senseful Apr 20 '15 at 22:20
  • In your second example, I think it's important to be explicit with your type. There's a reason Apple didn't bring preprocessor macros into Swift... – nhgrif Apr 20 '15 at 22:21
  • I agree with @nhgrif on both points. AVFoundation just isn't ported yet. And the latter is clear as `let myString = Optional("Hi")` (that doesn't work for `!` types, though). I wouldn't oppose some simple syntax if someone comes up with one, but I certainly wouldn't create an operator or anything heavy like that. You could wrap it up easily in a function called `lift` that takes an optional or a non-optional and returns an optional. – Rob Napier Apr 21 '15 at 00:23

3 Answers3

4

Optional is just an enum in swift, so you can do: Optional(str)

From the swift interface:

/// A type that can represent either a `Wrapped` value or `nil`, the absence
/// of a value.
public enum Optional<Wrapped> : _Reflectable, NilLiteralConvertible {
    case None
    case Some(Wrapped)
    /// Construct a `nil` instance.
    public init()
    /// Construct a non-`nil` instance that stores `some`.
    public init(_ some: Wrapped)
    /// If `self == nil`, returns `nil`.  Otherwise, returns `f(self!)`.
    @warn_unused_result
    public func map<U>(@noescape f: (Wrapped) throws -> U) rethrows -> U?
    /// Returns `nil` if `self` is `nil`, `f(self!)` otherwise.
    @warn_unused_result
    public func flatMap<U>(@noescape f: (Wrapped) throws -> U?) rethrows -> U?
    /// Create an instance initialized with `nil`.
    public init(nilLiteral: ())
}
icecrystal23
  • 331
  • 3
  • 10
0

To wrap up a variable you could use operator overloading and generic functions. For example:

prefix operator ??? {}

prefix func ??? <T> (x: T) -> T? {
    return Optional(x)
}

To relate this to your second example, you would do the following:

var myString = ???"Hi" // Optional("Hi")

if someCondition() {
    myString = nil
}
ABakerSmith
  • 22,759
  • 9
  • 68
  • 78
  • I was working on the exact same answer. This doesn't solve the problem of a function that returns an explicitly unwrapped optional that you want to treat as a regular optional. For example, if `func test()->Int! { return 17 }` is called like so, `let a = ??test()`, then `a` will have the type `Int!?` instead of `Int?`. – vacawama Apr 20 '15 at 23:08
  • Isn't "??" a bad choice of operator, seeing as how that's the symbol for the nil coalescing operator? – Duncan C Apr 20 '15 at 23:42
0

In the lighthearted spirit that, as I understand, the question was posed, you can define a postfix operator that uses one character less than your ideal solution (a pair of parens and a question mark):

postfix operator =? {}
postfix func =? <T> (rhs: T) -> T? { return rhs }
postfix func =? <T> (rhs: T!) -> T? { return rhs ?? nil }

var x = 3=?
x is Int? //--> true

var imp: Int! = 3
var opt = imp=?
opt is Int? //--> true

As to Apple's APIs you mention, since they already return an optional, albeit an implicitly unwrapped optional, you could use the nil coalescing operator ?? to convert it to a plain optional:

let y: Int! = 3 // posing for a rogue API 

y is Int? //--> false

let z = y ?? nil

z is Int? //--> true

Perhaps a sligtly more interesting use of a dodgy operator is a higher order operator function that lifts T -> Us into T? -> U?s...

func opt <T, U> (f: T -> U) -> T? -> U? { // we'll implement this as an operator
    return { $0.map(f) }
}

postfix operator ->? {}
postfix func ->? <T, U> (f: T -> U) -> T? -> U? {
    return { $0.map(f) }
}

Now, let's use it:

let square: Int -> Int = { $0 * $0 }

let i: Int? = 3

square->? (i)

Which is equivalent to:

opt(square)(i)

Which is equivalent to (but potentially a little more versatile than):

i.map(square)

This all makes much more sense when used along with the pipe forward operator:

infix operator |> { associativity left precedence 89 }
func |> <A, B>(a: A, f: A -> B) -> B {
    return f(a)
}

Then you can do:

i |> opt(square)

or

i |> square->?

Or we can conflate these into an optional pipe forward, which can take care of implicitly unwrapped optionals too:

infix operator ?> { associativity left precedence 89 }
func ?> <T, U> (lhs: T?, rhs: T -> U) -> U? {
    return lhs.map(rhs)
}

let im: Int! = 5
let op: Int? = 5

im ?> square |> debugPrintln //--> Optional(25)
op ?> square |> debugPrintln //--> Optional(25)

Which is equivalent to:

debugPrintln(op.map(square))
Milos
  • 2,728
  • 22
  • 24