Calling a swift function right after the closing bracket? - swift

Given that I can do closures like this
var test = { () -> String in
return "this works"
}()
I would imagine you could do something like this
func testFunc() {
let _ = "this doesn't work"
}()
But this throws an error - Consecutive statements on a line must be separated by ';'
As far as I recall, swift's funcs are just named closures. Is there a way to make this work?

There are three types of closures in Swift:
Global functions are closures that have a name and do not capture any values.
Nested functions are closures that have a name and can capture values from their enclosing function.
Closure expressions are unnamed closures written in a lightweight syntax that can capture values from their surrounding context.
...
Closure expressions are a way to write inline closures in a brief,
focused syntax.
(Source: Closures in the Swift book.)
In your first example:
var test = { () -> String in return "this works" }()
{ ... } is a closure expression. This expression is evaluated
with an empty argument list (). The result is the string
"this works" which is then assigned to the variable.
Your second example is a global function.
Global functions are (named) closures, but not closure expressions.
There is (as far as I know) no similar way to define a function which
is immediately evaluated.

Because there is no syntax that accepts
func testFunc() {} ()
So it should be
func testFunc() {
let myString = "this works"
}
and called
func myFunc() {
testFunc()
}

func defines a function. If you want to call it you need to do so in a separate statement:
func testFunc() {
let _ = "this doesn't work"
}
testFunc()

Related

Uncertain of this Swift struct syntax with closure

I'm stumped by a bit of Swift struct syntax.
For a regular struct, I understand how to define and initialize it.
struct Thing {
let name: String
}
let aThing = Thing(name: "The Name")
But I was reading a bit about functional programming and came across this syntax and what stumped me most was the initialization.
struct Effect<T> {
let run: (#escaping (T) -> Void) -> Void
}
// What is "callback" here? How does this work?
let anIntInTwoSeconds = Effect<Int> { callback in
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
callback(42)
}
}
anIntInTwoSeconds.run { theInt in
print(theInt)
}
If I were to see that with a "normal" initializer, I would understand it, but what is happening on this line? What is this syntax called so I can research it further?
let anIntInTwoSeconds = Effect<Int> { callback in
The declaration for the Effect type
struct Effect<T> {
Defines Effect as using a Generic type T. So you can create Effect objects that work on different types.
The line
let anIntInTwoSeconds = Effect<Int> { callback in
Creates an Effect object that operates on Ints, and assigns it to the variable anIntInTwoSeconds.
The { callback in part defines the closure for the Effect object. That declaration could be rewritten like this:
let anIntInTwoSeconds = Effect<Int>(run: { callback in
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
callback(42)
}
}
)
You can interpret as:
'Create an instance of the Effect struct, with a run property that contains the closure that begins with "{ callback in"...'
(It's confusing because the run property is, itself, a closure. So we create an instance of Effect, and pass it a "run" value that's a closure.)
The "callback in" part, specifically, tells the compiler that, inside that closure, the parameter to the closure should be named "callback".

Swift lambda gets inserted into end of argument list

With Swift 3.1
func foo() {
{ () -> Void in
print("oh, nice, now this works too!!!")
}()
}
foo()
works... but
func foo() {
print("Hi!")
{ () -> Void in
print("oh, nice, now this works too!!!")
}()
}
foo()
Will cause
ERROR at line 2, col 2: cannot invoke 'print' with an argument list
of type '(String, () -> Void)'
That can be fixed inserting semicolon after print
print("Hi!");
or parenthesis around the lambda definition. However, what I'm interested in is what is the root cause of the behaviour from the Swift compiler perspective?
In the second case, you need to separate the print call from the lambda block.
First case:
func foo() { () -> () }
No problem you can do whatever you want in your closure
Second case:
The compiler thinks you're telling the print statement to execute your closure after it ended.
So you need to separate with semicolon your print("hi") statement or wrap the lambda block to be explicit.

How are values captured when assigning a function to something that retains a closure

If I have a class like this:
class Example {
var emptyBlock: (Void -> Void)?
var string: String = "Here's some string"
func someFunction() {
let string = self.string
print(string)
}
}
And I assign at some point:
let variable: Void -> Void = exampleInstance.someFunction
exampleInstance.emptyBlock = variable
Do I have a retain cycle because variable captures exampleInstance from someFunction and variable is retained by exampleInstance? Or is there some sort of magic that makes this not a problem?
I made a quick test and I believe I've confirmed that they are in fact strongly captured as suspected. I'll post my findings for anyone else who has this question:
Take this class, and breakpoint in deinit:
class Example {
var emptyBlock: (Void -> Void)?
var string: String = "Here's some string"
func someFunction() {
let string = self.string
print(string)
}
deinit {
print("I was deinitialized") // Breakpoint Here
}
}
Then run these two examples of code:
Reference Cycle
Will not hit breakpoint because can't deinitialize:
let exampleInstance = Example()
let variable = exampleInstance.someFunction
exampleInstance.emptyBlock = variable
Non-Reference Cycle
Will hit breakpoint because weekly capturing:
let exampleInstance = Example()
let variable: Void -> Void = { [weak exampleInstance] in exampleInstance?.someFunction() }
exampleInstance.emptyBlock = variable
Because it hits the breakpoint when we weak capture, we can safely conclude that without that it is strongly captured as if a closure.
This should be considered when using this tool since we don't get the same obviousness of capture we do when declaring a closure. There's no explicit warnings to call self to remind us, and there's no weak declarations within their scope.
Guide
I also found a reference in the guide:
Global functions are closures that have a name and do not capture any values.
Nested functions are closures that have a name and can capture values from their enclosing function.
Closure expressions are unnamed closures written in a lightweight syntax that can capture values from their surrounding context.
Yes, there's a retain cycle. Assigning something to a local variable and then assigning it to something else is the same as assigning it directly. So that code is equivalent to
exampleInstance.emptyBlock = exampleInstance.someFunction
and exampleInstance.someFunction is just syntactic sugar for
{ x in exampleInstance.someFunction(x) }

Swift optional inout parameters and nil

Is it possible to have an Optional inout parameter to a function in Swift? I am trying to do this:
func testFunc( inout optionalParam: MyClass? ) {
if optionalParam {
...
}
}
...but when I try to call it and pass nil, it is giving me a strange compile error:
Type 'inout MyClass?' does not conform to protocol 'NilLiteralConvertible'
I don't see why my class should have to conform to some special protocol when it's already declared as an optional.
It won't compile because the function expecting a reference but you passed nil. The problem have nothing to do with optional.
By declaring parameter with inout means that you will assign some value to it inside the function body. How can it assign value to nil?
You need to call it like
var a : MyClass? = nil
testFunc(&a) // value of a can be changed inside the function
If you know C++, this is C++ version of your code without optional
struct MyClass {};
void testFunc(MyClass &p) {}
int main () { testFunc(nullptr); }
and you have this error message
main.cpp:6:6: note: candidate function not viable: no known conversion from 'nullptr_t' to 'MyClass &' for 1st argument
which is kind of equivalent to the on you got (but easier to understand)
Actually what #devios1 needs is "optional pointer".
But inout MyClass? means "pointer to an optional".
The following should work in Swift 4
class MyClass {
// func foo() {}
}
func testFunc(_ optionalParam: UnsafeMutablePointer<MyClass>? ) {
if let optionalParam = optionalParam {
// optionalParam.pointee.foo()
// optionalParam.pointee = MyClass()
}
}
testFunc(nil)
var myClass = MyClass()
testFunc(&myClass)
This is possible, and it may have everything to do with Closure.()
The optional can have a nil value, but it is not expecting nil. So the optional would work if you simply did not pass it to the function or passed a variable of type MyClass? with value nil. Like Bryan states.
When you write the function it has to have a default value to be an optional param like so:
func testFunc(inout optionalParam:MyClass?={var nilRef:MyClass?;return &nilRef}()) {
if optionalParam != nil {
...
}
}
Notice if optionalParam {...} is changed to if optionalParam != nil {...}
you can NOT uwwrap optionalParam without checking it,i.e.if optionalParam? != nil, as uwrapping, optionalParam? fails when optionalParam==nil. ~note, var a :MyClass? has a value of nil until it is assigned.
Call is either, testFunc(optionalParam:&a) or testFunc(), but never testFunc(nil)
Think you may be getting two separate concepts intertwined as they have similar names, optional parameters and optionals. Optionals are variables with an extended nil condition. Optional parameters are function parameters which are optional.
Further reading here. Apple is trying to change the verbiage from optional parameter to 'parameters with default values'. It is unfortunate they didn't incorporate optional parameter behavior within these new optional thingies. What is the point of passing nil optionals as optionals if they will fail when unwrapped. Maybe it gets deeper at the heart of what this optional thing is if it can be passed before unwrapping... Schrodinger's cat
The exact passage from the docs is:
You can only pass a variable as the argument for an in-out parameter.
You cannot pass a constant or a literal value as the argument, because
constants and literals cannot be modified. You place an ampersand (&)
directly before a variable’s name when you pass it as an argument to
an inout parameter, to indicate that it can be modified by the
function.
Note however, that (edited) per #gnasher's comment, you only need to pass a class as inout (aka by reference), if you intend or want to allow for the instance to be / being replaced by another instance, and not just have the original modified. The passage in the documentation that covers this is:
In-Out Parameters
Variable parameters, as described above, can only be changed within
the function itself. If you want a function to modify a parameter’s
value, and you want those changes to persist after the function call
has ended, define that parameter as an in-out parameter instead.
Here are three tests that cover the usage of var and inout.
class Bar : Printable {
var value = 1
init(_ value:Int) { self.value = value }
var description:String { return "Bar is: \(value)" }
}
let bar = Bar(1)
func changeBarWithoutInoutSinceBarIsAClassYaySwift(b:Bar) { b.value = 2 }
changeBarWithoutInoutSinceBarIsAClassYaySwift(bar)
println("after: \(bar)") // 2
var bar2 = Bar(0)
func tryToReplaceLocalBarWithoutInout(var b:Bar) { b = Bar(99) }
tryToReplaceLocalBarWithoutInout(bar2)
println("after: \(bar2)") // 0
var bar3 = Bar(0)
func replaceClassInstanceViaInout(inout b:Bar) { b = Bar(99) }
replaceClassInstanceViaInout(&bar3)
println("after: \(bar3)") // 99

How does one make an optional closure in swift?

I'm trying to declare an argument in Swift that takes an optional closure. The function I have declared looks like this:
class Promise {
func then(onFulfilled: ()->(), onReject: ()->()?){
if let callableRjector = onReject {
// do stuff!
}
}
}
But Swift complains that "Bound value in a conditional must be an Optional type" where the "if let" is declared.
You should enclose the optional closure in parentheses. This will properly scope the ? operator.
func then(onFulfilled: ()->(), onReject: (()->())?){
if let callableRjector = onReject {
// do stuff!
}
}
To make the code even shorter we can use nil as default value for onReject parameter and optional chaining ?() when calling it:
func then(onFulfilled: ()->(), onReject: (()->())? = nil) {
onReject?()
}
This way we can omit onReject parameter when we call then function.
then({ /* on fulfilled */ })
We can also use trailing closure syntax to pass onReject parameter into then function:
then({ /* on fulfilled */ }) {
// ... on reject
}
Here is a blog post about it.
Since I assume, that this "optional" closure should simply do nothing, you could use a parameter with an empty closure as default value:
func then(onFulfilled: ()->(), onReject: ()->() = {}){
// now you can call your closures
onFulfilled()
onReject()
}
this function can now be called with or without the onReject callback
then({ ... })
then({ ... }, onReject: { ... })
No need for Swift's awesome Optionals? here!
Maybe it's a cleaner way. Specially when the closure has complicated parameters.
typealias SimpleCallBack = () -> ()
class Promise {
func then(onFulfilled: SimpleCallBack, onReject: SimpleCallBack?){
if let callableRjector = onReject {
// do stuff!
}
}
}
As an alternative to creating a TypeAlias or using well-placed parentheses, there's always Swift's Optional type itself.
It can be used just like your typical Java (or in this case Swift) Generic Array syntax Optional<() -> ()>
OR in context:
func callAClosure(firstClosure: () -> (), secondClosure: Optional<() -> ()> {
if let secondClosure = secondClosure {
secondClosure()
}
else { firstClosure() }
}
I find this to be fairly clean. As a bonus, in the context of SwiftUI where generics can be common, like struct CommonList<T: View>: View then you don't have to create a typealias that only gets used once (commonly the init function for that struct). Instead, you make one simple optional closure parameter, and you're all done!
Hopefully this helps anyone that runs into the issue and happy coding!