Fast, optional deployment, reversal, if condition

Let's say I have a function that returns an optional parameter. nil if error and value if success:

func foo() -> Bar? { ... } 

I can use the following code to work with this function:

 let fooResultOpt = foo() if let fooResult = fooResultOpt { // continue correct operations here } else { // handle error } 

However, there are several problems with this approach for any non-trivial code:

  • Error handling is done at the end and it's easy to skip something. This is much better when error handling code follows a function call.

  • The operation code is indented one level. If we have another function to call, we must retreat again.

With C, you can usually write something like this:

 Bar *fooResult = foo(); if (fooResult == null) { // handle error and return } // continue correct operations here 

I found two ways to achieve a similar code style with Swift, but I also don't like it.

 let fooResultOpt = foo() if fooResult == nil { // handle error and return } // use fooResultOpt! from here let fooResult = fooResultOpt! // or define another variable 

If I write "!" everywhere, it just looks bad for my taste. I could introduce another variable, but that is also not very good. Ideally, I would like to see the following:

 if !let fooResult = foo() { // handle error and return } // fooResult has Bar type and can be used in the top level 

Am I missing something in the spec or is there some other way to write beautiful Swift code?

+5
source share
5 answers

Your assumptions are correct - in Swift there is no syntax "negation of if-let".

I suspect that one of the reasons for this may be the integrity of the grammar. Throughout Swift (and, as a rule, in other languages ​​that support C), if you have an operator that can bind local characters (i.e. name new variables and give them values) and which may have a block body (e.g. , if, while, for), those bindings are bound to the specified block. Instead of using the block operator, binding characters to its enclosing area will be inconsistent.

It’s still prudent to think, although I recommend giving an error and seeing what Apple does with it.

+2
source

This is what matches the template, and is a tool for this task:

 let x: String? = "Yes" switch x { case .Some(let value): println("I have a value: \(value)") case .None: println("I'm empty") } 

The if-let shape is just a convenience when you don't need both legs.

+2
source

If what you write is a set of functions that perform the same sequence of transformations, for example, when processing the result returned by the REST call (checking the response is not zero, checking the status, checking for application / server error, parsing response, and etc.), I would do it by creating a pipeline that converts input data at each step, and in the end returns either nil or a converted result of a certain type.

I chose the user operator >>> , which visually displays the data stream, but, of course, feel free to choose your own:

 infix operator >>> { associativity left } func >>> <T, V> (params: T?, next: T -> V?) -> V? { if let params = params { return next(params) } return nil } 

An operator is a function that receives a value of a certain type and a closure that converts a value to a value of another type as an input value. If the value is not nil, the function calls a closure, passing in the value and returning the return value. If the value is nil , then the operator returns nil .

Perhaps an example is needed, so suppose I have an array of integers, and I want to perform the following operations sequentially:

  • sum all array elements
  • calculate power 2
  • divide by 5 and return the integer part and the remainder
  • add the above 2 numbers together

These are 4 functions:

 func sumArray(array: [Int]?) -> Int? { if let array = array { return array.reduce(0, combine: +) } return nil } func powerOf2(num: Int?) -> Int? { if let num = num { return num * num } return nil } func module5(num: Int?) -> (Int, Int)? { if let num = num { return (num / 5, num % 5) } return nil } func sum(params: (num1: Int, num2: Int)?) -> Int? { if let params = params { return params.num1 + params.num2 } return nil } 

and that’s how I will use:

 let res: Int? = [1, 2, 3] >>> sumArray >>> powerOf2 >>> module5 >>> sum 

The result of this expression is either nil or a type value, as defined in the last pipeline function, which in the example above is Int .

If you need to improve error handling, you can define an enumeration as follows:

 enum Result<T> { case Value(T) case Error(MyErrorType) } 

and replace all options in the above functions with Result<T> , returning Result.Error() instead of nil .

+1
source

I found a method that looks better than alternatives, but it uses language functions in a non-recommended way.

An example of using the code from the question:

 let fooResult: Bar! = foo(); if fooResult == nil { // handle error and return } // continue correct operations here 

fooResult can be used as a normal variable, and there is no need to use "?" or "!" suffixes.

Apple's documentation says:

Implicitly expanded options are useful when it is confirmed that the value of optionals exists immediately after it is optionally defined first and, of course, it can presumably exist at every point after that. Initial use of implicitly deployed options in Swift during class initialization, as described in Unowned References and Implicitly Unwrapped Additional Properties.

+1
source

How about the following:

 func foo(i:Int) ->Int? { switch i { case 0: return 0 case 1: return 1 default: return nil } } var error:Int { println("Error") return 99 } for i in 0...2 { var bob:Int = foo(i) ?? error println("\(i) produces \(bob)") } 

Results in the next release:

 0 produces 0 1 produces 1 Error 2 produces 99 
0
source

Source: https://habr.com/ru/post/1204441/


All Articles