compactMap() closure fails when adding irrelevant NOP declaration? - swift

Playground
XCode Version 13.3 (13E113)
Swift 5.6
First print of compactMap() closure displays this:
["What\'s", "Going", "On?"]
The second print displays this:
[(), (), ()]
Seems like if I declare anything inside the closure, the output of the closure changes.
print( "What's\nGoing\nOn?".split(separator:"\n").enumerated().compactMap
{ idx, lineText in
lineText
})
print( "What's\nGoing\nOn?".split(separator:"\n").enumerated().compactMap
{ idx, lineText in
let _ : String
lineText
})
Is there a way to wrap some of it to hide the other declaration?
Is the closure confused about the type?
Is there any way to unconfuse it (or me)?
Is it a Swift bug?
Issue is root of why other things I'm trying to with this pattern aren't working.
UPDATE
As per #Shadowrun's answer below, I added a return statement in the 3rd example, but that leads to compile time errors. So is that resolvable?
print( "What's\nGoing\nOn?".split(separator:"\n").enumerated().compactMap
{ idx, lineText in
let _ : String
return lineText
})
expression failed to parse:
error: test playground.playground:38:52: error: generic parameter 'ElementOfResult' could not be inferred
print( "What's\nGoing\nOn?".split(separator:"\n").enumerated().compactMap
^
Swift.Sequence:2:28: note: in call to function 'compactMap'
#inlinable public func compactMap<ElementOfResult>(_ transform: (Self.Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]</b>

If you have a one line closure there's an implicit return, so the first one is returning lineText which is of type string. See "Functions With an Implicit Return" at https://docs.swift.org/swift-book/LanguageGuide/Functions.html
The second one doesn't actually return anything, once it is more than one line, there is no implicit return. So the return type is Void which is also spelled as the empty tuple, () and that's what you get there.
You need to say return lineText explicitly if you mean to return something.
This function:
{ idx, lineText in
let _ : String
lineText // This line evaluates the value of lineText and does nothing with the result
}
does not return a value. Any function that doesn't return a value returns a Void value. Void is a type with only one possible value, called (). Mapping to void is a pointless thing to do. Even if your function did a side effect, like printing something, it wouldn't be good style to use map just for side effects.
You can guard against this kind of mistake by being more explicit about the return type in the closure
{ idx, lineText -> String in ... }

Related

swift 5.1 evaluate a closure in the wrong way

I'm having an issue with evaluation of one line of code
if i break it down to two lines, it's working , but in one line of code, it's just evaluate in a 'new' to a 'wrong' way.
my main reason for asking this question, is not to solve it, I know I can use parenthesis to solve it, and break it to Two line, but don't want to solve it, I just want to know why its evaluated like this , and if there's a solution for this : some setting to patch , in Order THAT it will work in ONE LINE OF CODE :
Heres the code that working in Two lines
Heres the code that trying to do the same thing, but rise an error as you can see:
full code of both working and not working :
class ClosuresStack {
var dic = Dictionary<String,(()->String)->String >()
subscript(_ str:String)-> (()->String)->String {
get {
return dic[str]!
}
set {
dic[str] = newValue
}
}
}
func createClosuresStak() -> ClosuresStack {
let cs = ClosuresStack()
func takesAClosureReturnA_string(_ closure:()->String) ->String {
return closure() + " Two"
}
cs["C"] = takesAClosureReturnA_string
return cs
}
let c = createClosuresStak()["C"]
let str = c{"One"}
print(str) // print: One Two
let c = createClosuresStak()["C"]{"One"} // error -->
now, I want to somehow understand how to change it that it will work in ONE LINE OF CODE : meaning that the evaluation of 'createClosuresStak()["C"]{"One"}' will create a closure after ["C"] , and then from that point writing the {"One"}
will make it a full evaluate of the line :
let c = createClosuresStak()["C"]{"One"}
making 'c' a String
if that's not possible, I need to know it Too , tnx :)
UPDATE
tnx for the comments , its help me understand the problem more clearly :
1) im understanding that the createClosuresStak()["C"]{"One"}
acutely trying to add the string 'One' as another parameter to the sub script , and there for the error from the compiler was that is cannot subscript (String,()->String} , 'C' as the string inside the [] , and the other parameter {"One"} -> BUT , isn't that some kind of a bug?, been that i'm using [] ,Cleary the compiler need to 'understand' that I want to subscript a String, also by power of inferring that swift has,
2) now I'm still trying to get that syntax to work as it is so I try to change some things, in order to get it to work :
so I created a function that take a string, and return a dictionary of type : Dictionary<String,()->String>, and then trying so subscript it
and the compiler don't rise an error that way :
func closuresDictionary(_ s:String) -> Dictionary<String,()->String> {
var dic = Dictionary<String,()->String>()
func foo()->String {
return s + " Two"
}
dic["C"] = foo
return dic
}
let c = closuresDictionary("One")["C"]{ "SomeString" }
c is now a closure of type ()->String which does noting with string that I put inside, so the syntax works, but the outcome is not doing anything.
when im changing the return type of the dictionary to a different closure : (String)->String instead of ()->String , im getting the same old error, that I'm trying to subscript a (String,(String)->String)
and I need a function that will take the string inside the {} , and create something from it meaning that I need to subscript to return a closure of (String)->String
its seems like there's no way to do that
im adding two more pictures of my last trying in order to get this line of code in current syntax to work
the wanted syntax working but the outcome is not an outcome not doing any thing with the string inside the {}:
same error, by changing the function to (String)->String
Your example:
let c = createClosuresStak()["C"]{"One"}
is using trailing closure syntax.
Trailing closure syntax works by including the trailing closure as an additional parameter to a function call. Subscripting an array is really a function call under the hood (to a function called subscript), and Swift is trying to pass that closure as a second parameter to the subscripting call, which is what the error is explaining:
Cannot subscript a value of type 'ClosuresStack' with an argument of type '(String, () -> String)'.
In other words, you can't pass both "C" and the closure {"One"} to the subscripting function.
There are at least 3 ways to fix this and still put it on one line:
Option 1: Use an explicit call to pass the closure instead of using trailing closure syntax
Wrap the closure in () to make the call explicit:
let c1 = createClosuresStak()["C"]({"One"})
print(c1)
Option 2: Wrap the createClosureStak()["C"] in parentheses
That lets Swift know the subscripting only gets "C" as a parameter and allows trailing closure syntax to work as expected:
let c2 = (createClosuresStak()["C"]){"One"}
print(c2)
Option 3: Add .self to the result before the trailing closure syntax:
That again finishes the subscripting call and avoids the confusion.
let c3 = createClosuresStak()["C"].self {"One"}
print(c3)
Personally, I would choose option one, because trailing closure syntax is unnecessary syntactic sugar that clearly is not working here.

How to read swift headers

When I cmd click the split function in Xcode, it takes me to the header file. This is what it reads
public func split(maxSplit: Int = default, allowEmptySlices: Bool = default, #noescape isSeparator: (Self.Generator.Element) throws -> Bool) rethrows -> [Self.SubSequence]
How does the following implementation work with above declaration?
someString.characters.split { $0 == "." }
Let's break it down:
public func split(maxSplit: Int = default, allowEmptySlices: Bool = default, #noescape isSeparator: (Self.Generator.Element) throws -> Bool) rethrows -> [Self.SubSequence]
maxSplit: The first parameter, maxSplit, allows you to specify the maximum number of pieces that the sequence will be split into. The default is Int.max.
allowEmptySlices: The second parameter, allowEmptySlices specifies whether or not two consecutive separators in the sequence should lead to an empty slice. The default is false. For example if you had a String, "A..B", and you split on the . character, you could end up with either two (["A", "B"]) or three (["A", "", "B"]) items in the output array depending on what you pass for this parameter.
isSeparator: The last parameter is the closure that you pass to identify where to split the sequence.
Since both maxSplit and allowEmptySlices have default arguments, you don't need to include them in your function call unless you want to change them. The only parameter that you have to supply is the isSeparator closure.
In your case, you called:
someString.characters.split { $0 == "."}
...which is the equivalent of:
someString.characters.split(maxSplit: Int.max, allowEmptySlices: false) { $0 == ".' }
You could also write your function call like this:
someString.characters.split(isSeparator: { $0 == "." })
The way that you have written it makes use of the "trailing closure" syntax. If a function takes a closure as it's final argument, you can move the closure outside the parentheses like this:
someString.characters.split() { $0 == "." }
And if the function takes only one argument (not counting any default arguments that you are not supplying) then you can omit the parentheses altogether:
someString.characters.split { $0 == "." }
At the highest level, what happens is that split iterates through the sequence of characters. It tests each character using the supplied closure, and if the closure returns true, it splits the sequence on that character. In your case it will split the sequence of characters every time it locates a ".".
Some other notes:
rethrows: The whole function is marked rethrows. It will throw an error, but only if the closure that you pass for the isSeparator argument throws an error itself. Note that the isSeparator parameter allows you to pass a closure that throws an error, but you don't have to. Any time a function accepts a closure that throws an error, it will also accept a closure that does not throw. This is because non-throwing functions are a sub-type of throwing functions.
#noescape: The isSeparator parameter is marked #noescape. That simply means that nothing in the closure will survive past the end of the call to split.
Your line someString.characters.split { $0 == "."} uses the default values for maxSplit and allowEmptySlices and specifies a custom closure for isSeparator. Some longer version of calling the function split is:
let arr = str.characters.split(Int.max, allowEmptySlices: false) { (char) -> Bool in
char == "."
}
Since the above code uses the same values as the actual defaults (Int.max and false) you can remove the first two parameters and since the closure is the last parameter (making it a trailing closure) you can omit the entire parameter and simply write a closure in the following way:
let arr = str.characters.split { (char) -> Bool in
char == "."
}
Now we can simplify the closure by omitting the specific signature:
let arr = str.characters.split { $0 == "." }
someString.characters.split { $0 == "."}
is equivalent to
someString.characters.split(isSeparator: { $0 == "."})
the closure in your code is called a trailing closure, closures that are the last parameter to a function can be placed after the function declaration, and if no other parameters remain, you can also omit ().
The first two parameters have default values, so they can be omitted, leading to the simplified call you posted.

String(x) puts "optional" in the output

I'm writing a little program that reads and writes text files into an NSTableView. I had the reading working fine, and then moved onto the writing. And I got...
FR Optional(0) Optional(0) Optional(0) Optional(0) Optional(46.29) Optional(0)
I understand why this is happening: the values are NSNumbers in a dictionary, so they are, by definition, optional. But obviously this is not useful output.
Is there an easy way to output the value without the Optional and any similar bumpf?
The reason why you see Optional(n) is because you're printing the optional without unwrapping.
I suggest you re-read the optionals chapter in the Apple book to get a better grasp at why this is happening to you.
The short version is, an Optional is a type, in fact if you look at the Swift source code, you will find that's it's just an Enum!
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: ())
}
So if your values are Optionals, you have to unwrap them to see their values.
Take a look at this code and try to guess the output:
var code: String? = "hello"
if let code = code where code == "hello" {
print(code)
}
var upperCase = code?.uppercaseString
print(upperCase)
Output:
Did you figure it out?
It looks like this:
hello
Optional("HELLO")
Why does the first hello print ok?
Because it's being unwrapped in the if let statement.
But the second one, upperCase is never unwrapped, so an Optional will remain optional (unless unwrapped). code?.uppercaseString returns a new Optional. By printing it directly, we get what you see.
If you want to extract the value the optional is holding you have two operators. ? and !.
The first one is usually preferred, but if you're sure that the value is ok, you can use force the unwrap by using !, so you could do
print(uppercase!)
Careful tho, because if upperCase happens to be nil, you'd get a runtime crash.
If the values you write are held in an (optional) array, then .flatMap, prior to printing, will do the trick for you
// myOptionalDoubles : [Double?]
myOptionalDoublesForWriting = myOptionalDoubles.flatMap { $0 }
Using .flatMap like this, you unwrap any non-nil values in the array myOptionalDoubles, and return an array myOptionalDoublesForWriting of type [Double] (non-optional). Any nil entries in myOptionalDoubles will not follow to myOptionalDoublesForWriting.
If you don't want to lose information about optional entries, you can use .map instead:
// myOptionalDoubles : [Double?]
var myOptionalDoublesForWriting = myOptionalDoubles.map { $0 ?? 0.0 }
In this case, you make use of the nil coalescing operator to either unwrap each (non-nil) array entry, or, if it's nil, give it value 0.0. As above, the mapped to array myOptionalDoublesForWriting will be of non-optional type [Double].

Difference between nil and () in Swift

I noticed that the empty tuple () is used to represent an absence of value in Swift. For example, the signature of a function that returns nothing, is:
func zz(){
println("zz")
}
The compiler will also accept this body for the function above:
func zz(){
println("zap")
return () // returning () and returning nothing is the same thing!
}
An equivalent way of defining this function would be:
func zz() -> (){
println("zap")
return ()
}
There's even a typealias for () called Void:
typealias Void = ()
So if the empty tuple is the absence of value in Swift, what is its relationship with nil? Why do we need both concepts? is nil defend in terms of ()?
nil is a special value that you can assign to an Optional to mean the optional doesn't have a value. nil is not really the absence of a value, it's a special value that means no proper value
() means that nothing is returned (or passed in), not even an optional. There is no return value, nor can there ever be. You can't assign it to anything unlike a return value of nil.
The situation is analogous to the difference between (in C)
void foo(void);
and
SomeType* bar(void);
if the latter function uses a return value of NULL to indicate some sort of failure. i.e.
someVariable = foo(); // Illegal
someVariable = bar(); // Not illegal
if (someVariable != NULL) { ... }
nil is absence of value, () is a tuple with 0 elements. Conceptually the same as comparing nil with an empty array: an empty array is not absence of value.
They are simply 2 different and unrelated things.
Just try this in a playground:
var x: Int?
x = () // <== () is not convertible to Int
that produces an error.

swift, optional unwrapping, reversing if condition

Let's say I have function which returns optional. nil if error and value if success:
func foo() -> Bar? { ... }
I can use following code to work with this function:
let fooResultOpt = foo()
if let fooResult = fooResultOpt {
// continue correct operations here
} else {
// handle error
}
However there are few problems with this approach for any non-trivial code:
Error handling performed in the end and it's easy to miss something. It's much better, when error handling code follows function call.
Correct operations code is indented by one level. If we have another function to call, we have to indent one more time.
With C one usually could write something like this:
Bar *fooResult = foo();
if (fooResult == null) {
// handle error and return
}
// continue correct operations here
I found two ways to achieve similar code style with Swift, but I don't like either.
let fooResultOpt = foo()
if fooResult == nil {
// handle error and return
}
// use fooResultOpt! from here
let fooResult = fooResultOpt! // or define another variable
If I'll write "!" everywhere, it just looks bad for my taste. I could introduce another variable, but that doesn't look good either. 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
Did I miss something in the specification or is there some another way to write good looking Swift code?
Your assumptions are correct—there isn't a "negated if-let" syntax in Swift.
I suspect one reason for that might be grammar integrity. Throughout Swift (and commonly in other C-inspired languages), if you have a statement that can bind local symbols (i.e. name new variables and give them values) and that can have a block body (e.g. if, while, for), those bindings are scoped to said block. Letting a block statement bind symbols to its enclosing scope instead would be inconsistent.
It's still a reasonable thing to think about, though — I'd recommend filing a bug and seeing what Apple does about it.
This is what pattern matching is all about, and is the tool meant for this job:
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 form is just a convenience for when you don't need both legs.
If what you are writing is a set of functions performing the same sequence of transformation, such as when processing a result returned by a REST call (check for response not nil, check status, check for app/server error, parse response, etc.), what I would do is create a pipeline that at each steps transforms the input data, and at the end returns either nil or a transformed result of a certain type.
I chose the >>> custom operator, that visually indicates the data flow, 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
}
The operator is a function that receives as input a value of a certain type, and a closure that transforms the value into a value of another type. If the value is not nil, the function invokes the closure, passing the value, and returns its return value. If the value is nil, then the operator returns nil.
An example is probably needed, so let's suppose I have an array of integers, and I want to perform the following operations in sequence:
sum all elements of the array
calculate the power of 2
divide by 5 and return the integer part and the remainder
sum the above 2 numbers together
These are the 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 this is how I would use:
let res: Int? = [1, 2, 3] >>> sumArray >>> powerOf2 >>> module5 >>> sum
The result of this expression is either nil or a value of the type as defined in the last function of the pipeline, which in the above example is an Int.
If you need to do better error handling, you can define an enum like this:
enum Result<T> {
case Value(T)
case Error(MyErrorType)
}
and replace all optionals in the above functions with Result<T>, returning Result.Error() instead of nil.
I've found a way that looks better than alternatives, but it uses language features in unrecommended way.
Example using code from the question:
let fooResult: Bar! = foo();
if fooResult == nil {
// handle error and return
}
// continue correct operations here
fooResult might be used as normal variable and it's not needed to use "?" or "!" suffixes.
Apple documentation says:
Implicitly unwrapped optionals are useful when an optional’s value is confirmed to exist immediately after the optional is first defined and can definitely be assumed to exist at every point thereafter. The primary use of implicitly unwrapped optionals in Swift is during class initialization, as described in Unowned References and Implicitly Unwrapped Optional Properties.
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 following output:
0 produces 0
1 produces 1
Error
2 produces 99