This compiles:
let badger = get_closure()
func get_closure() -> (Int) -> Void {
return { (x: Int) -> Void in
print(x)
if x > 4 {
return
} else {
badger(x + 1)
}
}
}
badger(1)
This doesn't with circular reference errors:
let badger = get_closure()
let get_closure = { () -> (Int) -> Void in
return { (x: Int) -> Void in
print(x)
if x > 4 {
return
} else {
badger(x + 1)
}
}
}
badger(1)
Why? I thought the func syntax is just sugar for the second more explicit syntax.
The fact that the first one works is a long-standing bug/quirk in the compiler, not an intended feature. It's based on undefined behavior that the compiler has trouble detecting. You can demonstrate that it's a quirk of top-level declarations by wrapping your working code in a function and see that it fails very similarly to your second example:
func f() {
let badger = get_closure()
func get_closure() -> (Int) -> Void { // ERROR: Closure captures 'badger' before it is declared
return { (x: Int) -> Void in
print(x)
if x > 4 {
return
} else {
badger(x + 1)
}
}
}
badger(1)
}
The key feature of top-level, global declarations like badger is that they're lazily evaluated, so the assignment actually happens after the declaration of the function. In your second example, both variables are lazy, so the order comes back in. But it's dangerous to rely on this fact, as discussed in the linked forum posts.
When exploring subtle Swift behavior, I always recommend building a commandline app and placing your test code inside of a function to eliminate the weirdness of both Playgrounds and top-level executable code. (Playgrounds isn't relevant in this case, but it comes up a lot.)
It's hard to understand where this approach should be used, but if you really want to, then try this:
let get_closure: (Int) -> Void = { x in
print(x)
if x > 4 {
return
} else {
badger(x + 1)
}
}
let badger = get_closure
badger(1)
The order of initialization of dependent variables is usually important. If you assign a value after the closure, there will be no error:
let get_closure = { () -> (Int) -> Void in
return { (x: Int) -> Void in
print(x)
if x > 4 {
return
} else {
badger(x + 1)
}
}
}
let badger = get_closure()
badger(1)
Related
I want to write a general-purpose Swift function that serves the following simple purpose:
Take any function as argument
Take a Bool argument
If the bool argument is TRUE, invoke the input function with its args. Otherwise No-op.
The purpose is to eliminate a lot of clumsy if statements in the code that meet a specific criteria.
Something like:
typealias ClosureType = (Any...) -> Any. // Notice the variable argument of any kind
func invokeIfConditionIsTrue(closure: Closure, condition: Bool) {
if condition {
if let myFunc = closure as? ClosureType {
myFunc()
print("executed")
} else {
print("not executed")
}
}
}
func testIntToInt(i: Int) -> Int {
return i*i
}
func testIntToDouble(i: Int) -> Double {
return Double(i*i)
}
invokeIfConditionIsTrue(testIntToInt, true). // executed
invokeIfConditionIsTrue(testIntToDouble, false). // not executed
However, I am struggling to come up with syntax that will enable the argument passing to the input myFunc() func.
The example is pretty basic, and my input function closure could be accepting and emitting any type of input/outputs, including structs, classes and objective c stuff.
I have a hunch this is possible via a mechanism called function object, but I am not familiar enough with it.
Should I reinvent the wheel, or is there already a library/known way which is doing it successfully, and I am missing out?
I have no idea why you think
invokeIfConditionIsTrue(testIntToInt, condition)
is somehow superior to
if condition { result = testIntToInt(n) }
Or
result = condition ? testIntToInt(n) : 0
but what you want is pretty much impossible unless you wrap the function in a closure because there is no way to express "function with any arguments" in Swift as a type. The best you can do is wrap your function in a closure with known argument types. There's also no general Closure type that represents any closure.
func invokeIfConditionIsTrue(closure: () -> (), condition: Bool) {
if condition {
closure()
print("executed")
}
}
invokeIfConditionIsTrue(closure: { result = testIntToInt(n) }, condition: true)
But, as you can see, that's not really any better than an if statement. In fact, it's much worse.
Another possibility is to define a function that returns a function, but it still needs to know the argument types.
func invokeIfConditionIsTrue(closure: (Int) -> Int, condition: Bool) -> (Int) -> Int?
{
if condition {
return closure
}
else
{
return { _ in 0 } // A dummy function
}
}
invokeConditionIfTrue(closure: testIntToInt, condition: true)(n)
After some haggling and searching for syntax (I wasn't good in FP, as I mentioned in the beginning), I was able to compile and run the below solution in my XCode 14.2 playground.
My desired function:
func executeIfCondition(function: (#escaping (Any...) -> Any), condition: Bool) {
if condition {
function()
}
}
This was supposed to replace following types of calls across my codebase:
if condition {
function()
}
Test functions whom I want to invoke, using executeIfCondition if condition = true.
func printStringAndReturnInt(i: Int, s: String) -> Int {
print(s)
return i
}
func printInt(i: Int) -> Void {
print("\(i)")
}
func printArray(arr: [Int]) -> Void {
arr.forEach({ print("\($0)") })
}
struct Struct1 {
var value1: Int
var value2: Int
}
func printStruct(t: Struct1) {
print("Struct1: \(t.value1) - \(t.value2)")
}
class Class1 {
var value1: Double = 100.0
var value2: Double = 200.0
}
func printClass(c: Class1) {
print("Class1: \(c.value1) - \(c.value2)")
}
Example Usage:
Instead of:
if (true) {
printStringAndReturnInt(i: 5, s: "Wow!")
}
I will now use:
executeIfCondition(function: { _ in printStringAndReturnInt(i: 5, s: "Wow!") }, condition: true)
The rest:
executeIfCondition(function: { _ in printInt(i: 61) }, condition: false)
executeIfCondition(function: { _ in printArray(arr:[9,10,11,12]) }, condition: true)
executeIfCondition(function: { _ in printStruct(t: Struct1(value1: 100, value2: 200)) }, condition: true)
executeIfCondition(function: { _ in printClass(c: Class1()) }, condition: true)
This isn't exhaustive still, but enough for a start.
Can someone explain why I receive this error. It seems like a scope issue, but all my cases are covered by the else statement right?
func testNumber(number: Int) throws -> Int {
for i in 1...100 {
let x: Int = i * i
if x == number {
return x
} else {
return 0
}
}
}
I just want to understand why this error is occurring. The function is not important.
The example is extremely silly because you are going to return 0 on the very first iteration of the loop, unless number happens to be 1. You're not checking any other members of the loop sequence to see whether any of them satisfy the test. That's kind of ridiculous; your loop is completely pointless. You might as well have written:
func testNumber(number: Int) throws -> Int {
return (number == 1) ? 1 : 0
}
And that is the reason for the compiler's behavior as well. No one would ever return on every wing of a condition inside a for loop, because the loop would always exit after the first iteration. So the compiler doesn't even bother to consider that possibility, because why should it?
If there had been no for loop, all would have been well, because the condition is at top level:
func testNumber(number: Int) throws -> Int {
if number == 7 {
return 7
} else {
return 0
}
}
But since the condition is buried in a for loop, the compiler just doesn't bother to dive in and study the return logic. Even the simplest example fails to compile:
func testNumber(number: Int) throws -> Int {
for i in 1...100 {
return 1
}
} // error
Because, again, that is something no one would ever say.
If you are determined to keep the code you have, just quiet the compiler's uncertainty with a fatal error:
func testNumber(number: Int) throws -> Int {
for i in 1...100 {
let x: Int = i * i
if x == number {
return x
} else {
return 0
}
}
fatalError("shouldn't get here")
}
But a more realistic rewrite of your (unrealistic) example would be this:
func testNumber(number: Int) throws -> Int {
for i in 1...100 {
let x: Int = i * i
if x == number {
return x
}
}
return 0
}
That makes the compiler happy and gives the output that your original code probably intended.
I was having fun while practicing a Kata.
I wanted to implement one of the functions in my solution as an extension of Int, because I liked the idea of having pretty syntax. The function, in itself, looks like this:
func decomposed(_ n: Int) -> [Int] {
if n > 10 {
return n.decomposed(n / 10) + [n % 10]
} else {
return [n]
}
}
Now, I've tried to implement it as an extension. Due to the fact that I would like to use it like 420.decomposed(), I figured that I would need the instance as the default argument. I proceeded with:
extension Int {
func decomposed(_ n: Int = self) -> [Int] {
if n > 10 {
return n.decomposed(n / 10) + [n % 10]
} else {
return [n]
}
}
}
This is the part, however, in which it gets trickier. I'm being told by the compiler that error: cannot find 'self' in scope.
Having read about the methods in the docs, I've resorted to using Int? default argument. Now, the extension is implemented as:
extension Int {
func decomposed(_ n: Int? = nil) -> [Int] {
var _n = n ?? self
if _n > 10 {
return _n.decomposed(_n / 10) + [_n % 10]
} else {
return [_n]
}
}
}
I don't like the look of the _n, though.
I would love to use self as the default argument. Is that possible? Or are we stuck with hackity hacks until the judgement day?
Wrote a quick implementation to highlight the usage of extensions:
import Foundation
func main() {
print(2.test()) // prints 2
print(12.test()) // prints 3
print(42.test()) // prints 6
}
extension Int {
func test() -> Int {
if self <= 10 {
return self
}
return (self % 10) + (self/10).test();
}
}
main()
You don't need to pass self as an argument, since that will not work. By default, self will be available for use inside the extension methods (since that follows the object oriented paradigm).
You need to remove n as a parameter, replace every use of n in the function with self (since that's what's being operated on), and convert the function syntax to the method syntax in the recursion:
extension Int {
func decomposed() -> [Int] {
if self > 10 {
return (self / 10).decomposed() + [self % 10]
} else {
return [self]
}
}
}
In your function decomposed, you aren't using self and n anywhere together you can either make your decomposed method accept no parameter and use self from the function:
extension Int {
func decomposed() -> [Int] {
if self > 10 {
return (self / 10).decomposed() + [self % 10]
} else {
return [self]
}
}
}
or if you plan to pass any parameter then you can just define a top level function instead of using extension:
func intDecomposed(_ n: Int) -> [Int] {
if n > 10 {
return n.decomposed(n / 10) + [n % 10]
} else {
return [n]
}
}
You can use the above method inside the decomposed extension method providing self as parameter:
extension Int {
func decomposed() -> [Int] {
return intDecomposed(self)
}
}
Is there a simple and definite way in Swift to check whether something is a callable block / function? In some languages it's a trivial thing, but perhaps I'm looking at this from a wrong perspective in Swift? Consider the following.
func foo(){ print("foo") }
var bar: () -> () = { print("bar") }
var baz: () -> (Bool) = { print("baz"); return true }
print(foo) // (Function)
print(bar) // (Function)
print(baz) // (Function)
print(foo is () -> ()) // true
print(bar is () -> ()) // true
print(baz is () -> ()) // false
print(baz is () -> (Bool)) // true
Swift knows that they are all functions, though there is no such data type. I can check by using a solid signature, but there might be a situation where I don't care about the signature* and simply want to invoke it. For example:
func call(callable: () -> ()) {
callable()
}
call(foo) // foo
call(bar) // bar
call(baz) // error: cannot convert value of type '() -> (Bool)' to expected argument type '() -> ()'
I can rewrite it like this, which will work for Void and Bool return types, but doing this for every type is crazy, especially since I don't care about it, but compiler does…
func call(callable: Any) {
if let block: () -> () = callable as? () -> () {
block()
} else if let block: () -> (Bool) = callable as? () -> (Bool) {
block()
}
}
call(foo) // foo
call(bar) // bar
call(baz) // truely baz
* Agree, not caring about the signature is a sin. For the argument sake let's just not care about the return type.
You can check the String representation of .dynamicType of the callable for existence of substring ->. Not super-elegant, but it works:
func isAClosure<T>(foo: T) -> Bool {
return String(foo.dynamicType).containsString("->")
}
var a : () -> () = { print("Foobar") }
var b : (Double) -> (Bool) = { $0 > 0 }
var c : Int = 1
isAClosure(a) // true
isAClosure(b) // true
isAClosure(c) // false
Of course, as Marcus Rossel points out in the comment above, you still wouldn't know anything about the parameters of the callable (but perhaps that could be next step to find out, given that you know it's a callable).
Addition with regard to OPs questions below: just a technical discussion, and not recommended techniques.
You use the same approach as above to check if the function argument is a closure without arguments (() -> (...)) or one with neither arguments nor return type (() -> ()), and so on. Using this approach, you can define a generic function that call the argument sent to the function only if it is of a certain closure type. For this "in-function-call", you'll have to make use of type conversion to expected closure type, much as you've described in your Q above. It'll probably be difficult to circumvent this "non-generic" approach w.r.t. calling the closures. A few examples follow below.
/* Example functions */
func isAVoidParamClosure<T>(foo: T) -> Bool {
let bar = String(foo.dynamicType).componentsSeparatedByString(" -> ")
return bar.count > 1 && (bar.first?.characters.count ?? 0) == 2
}
func callIfVoidVoidClosure<T>(foo: T) {
let bar = String(foo.dynamicType).componentsSeparatedByString(" -> ")
if bar.count > 1 && !(bar.map{ $0 == "()" }.contains(false)) {
if let foo = foo as? () -> () {
foo()
}
}
}
func isASingleDoubleReturnTypeClosure<T>(foo: T) -> Bool {
let bar = String(foo.dynamicType).componentsSeparatedByString(" -> ")
return bar.count > 1 && bar[1] == "Double"
/* rhs of '&&' lazily evaluated: [1] ok */
}
func printTwoTimesResultOfVoidDoubleClosure<T>(foo: T) {
if isAVoidParamClosure(foo) && isASingleDoubleReturnTypeClosure(foo) {
if let foo = foo as? () -> Double {
let a: Double = 2*foo()
print(a)
}
}
}
Example calls:
/* Example calls */
let a : () -> () = { print("Foobar") }
let b : (Double) -> (Bool) = { $0 > 0 }
let c : () -> Double = { 21.0 }
let d : Int = 1
isAVoidParamClosure(a) // true
isAVoidParamClosure(b) // false
isAVoidParamClosure(c) // true
isAVoidParamClosure(d) // false
callIfVoidVoidClosure(a) // Prints "Foobar"
callIfVoidVoidClosure(b)
callIfVoidVoidClosure(c)
callIfVoidVoidClosure(d)
printTwoTimesResultOfVoidDoubleClosure(a)
printTwoTimesResultOfVoidDoubleClosure(b) // Prints "42.0"
printTwoTimesResultOfVoidDoubleClosure(c)
printTwoTimesResultOfVoidDoubleClosure(d)
I'm trying to write a function literal in swift with a recursive body - in this case it's simply to add all the values in a list. I'm getting an error that "Variable used within it's own initial value". Any thoughts on what might be wrong here? Also I'm aware that what I'm doing here is a simple reduce and that it's build into Array, I'm just using this as an illustrative example of what I'm seeing elsewhere.
let list: Slice = [1,2,3,4,5,6,7,8,9,10]
var closure = { (memo: Int, list: Slice<Int>) -> Int in
if (list.count == 0) {
return memo
} else {
return closure(memo + list[0], list[1..<list.count])
}
}
let value = closure(0,list)
Try this:
let list: Slice = [1,2,3,4,5,6,7,8,9,10]
var closure:((Int, Slice<Int>) -> Int)!
closure = { (memo, list) in
if (list.count == 0) {
closure = nil // remove retain cycle
return memo
} else {
return closure(memo + list[0], list[1..<list.count])
}
}
let value = closure(0, list)
EDIT:
see this video: Advanced Swift at WWDC14. from around 41:00. it shows the down side of this method, and better workaround.
I know this is quite old, but I've found another alternative:
let list : ArraySlice<Int> = [1,2,3,4,5,6,7,8,9,10]
let closure = { (Void) -> ((Int, ArraySlice<Int>) -> Int) in
func f(memo: Int, list: ArraySlice<Int>) -> Int {
if (list.count == 0) {
return memo
} else {
return f(memo + list[list.startIndex], list: list[(list.startIndex + 1)..<list.endIndex])
}
}
return f
}()
let value = closure(0, list)