How to invoke any swift function conditionally (without if block) - swift

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.

Related

Wrap a higher-order function in some context and apply it

Similar, I believe, to an applicative, I would like to wrap a higher-order function in some context and apply it to a sequence.
protocol Foo { func a() -> Void }
class Bar: Foo { func a() { } }
let seq = [Bar(), Bar(), Bar()]
Concretely, Give the above three definitions, I'd like to be able to call dispatch(event) where dispatch wraps up a forEach over a sequence of protocol instances, and event is a function defined by that protocol.
private func dispatch(event: () -> Void) -> Void {
DispatchQueue.main.async { // context that the forEach mapping should happen inside of
seq.forEach($0.event())
}
}
let _ = dispatch(Foo.a)
Obviously, this doesn't work with Swift's type system (I'm used to Clojure's apply()). As a possible alternative, is there a way to wrap the sequence into a partial that I can forEach on?
let dispatch() -> [Foo] {
DispatchQueue.main.async {
seq.forEach // 🤷🏻‍♂️
}
}
let _ = dispatch { $0.a() }
Perhaps dispatch should be thought of as a constrained extension to Sequence?
extension Sequence where Iterator.Element == Foo {
func dispatch() -> [Foo] {
DispatchQueue.main.async {
return self.forEach // 🤷🏾‍♀️
}
}
}
While not as elegant as could have been with key paths to instance methods, you could also create a dispatch function that takes in a closure with each element as its parameter:
func dispatch(_ handler: #escaping (Foo) -> Void) {
DispatchQueue.main.async {
seq.forEach(handler)
}
}
And invoke it like so:
dispatch { $0.a() }
While you can't use KeyPath for instance methods, you can use it for properties, if it works for you.
You can change your protocol and implementation to something like this:
protocol Foo {
var a: () -> Void { get }
}
class Bar: Foo {
lazy var a = {
print("Bar")
}
}
Then you can define your dispatch function to take a KeyPath as parameter:
extension Sequence where Iterator.Element: Foo {
func dispatch(keyPath: KeyPath<Foo, () -> Void>) {
DispatchQueue.main.async {
forEach { $0[keyPath: keyPath]() }
}
}
}
and pass the properties KeyPath as parameter:
let seq = [Bar(), Bar(), Bar()]
seq.dispatch(keyPath: \Foo.a)

How to use buildExpression in Swift 5.2 Function Builders?

I understand that it's a draft proposal. I tried to implement a simple DSL for building a string, like so:
#_functionBuilder
struct StringBuilder {
static func buildExpression(_ string: String) -> [String] {
[string]
}
static func buildBlock(_ children: [String]...) -> [String] {
children.flatMap{ $0 }
}
}
func s(separator: String = "", #StringBuilder _ makeString: () -> [String]) -> String {
makeString().joined(separator: separator)
}
let z = s(separator: " ") {
"this"
"is"
"cool"
}
However, the compiler complains that "'String' is not convertible to '[String]'". This leads me to believe that buildBlock is the only part of the proposal currently implemented. (This is understandable given that in SwiftUI they are building a hierarchy of views, so that's all they need.)
Is this correct or am I doing something wrong? What is the correct way to use buildExpression?
ielyamani's answer shows how to build a working string builder such as I used in my example above. However, that does not solve the actual problem. I'm not trying to build a string builder. I'm trying to figure out function builders. The string builder is just an example. For example, if we wish to have a string builder that accepts integers, we could in theory do the following:
#_functionBuilder
struct StringBuilder {
static func buildExpression(_ int: Int) -> [String] {
["\(int)"]
}
// The rest of it implemented just as above
}
In this case, when the compiler encountered an Int, it would call buildExpression to then spit out our component type, in this case [String]. But as Martin R said in a comment to this question, buildExpression is not currently implemented.
I encountered the same issue today, it seems that buildExpression isn't implemented. I ended up making a workaround by using a protocol "ComponentProtocol" and then creating "Expression: ComponentProtocol" and "Component: ComponentProtocol". That works for me for now. I am hoping it'll be implemented later.
protocol ComponentProtocol: ExpressibleByIntegerLiteral, ExpressibleByStringLiteral {
var value: String { get }
}
struct Expression: ComponentProtocol {
let _value: String
var value: String { _value }
init(_ value: String) { _value = value }
init(integerLiteral value: Int) { self.init(value) }
init(stringLiteral value: String) { self.init(value) }
init<E: CustomStringConvertible>(_ value: E) {_value = String(describing: value) }
}
struct Component: ComponentProtocol {
let _values: [String]
var value: String { _values.joined(separator: ", ") }
init(integerLiteral value: Int) { self.init(value) }
init(stringLiteral value: String) { self.init(value) }
init<E: CustomStringConvertible>(_ value: E) { _values = [String(describing: value)] }
init<T: ComponentProtocol>(_ values: T...) { _values = values.map { $0.value } }
init<T: ComponentProtocol>(_ values: [T]) { _values = values.map { $0.value } }
}
#_functionBuilder struct StringReduceBuilder {
static func buildBlock<T: ComponentProtocol>(_ components: T ...) -> Component { Component(components) }
static func buildEither<T: ComponentProtocol>(first: T) -> Component { Component(first.value) }
static func buildEither<T: ComponentProtocol>(second: T) -> Component { Component(second.value) }
static func buildOptional<T: ComponentProtocol>(_ component: T?) -> Component? {
component == nil ? nil : Component(component!.value)
}
}
func stringsReduce (#StringReduceBuilder block: () -> Component) -> Component {
return block()
}
let result = stringsReduce {
Expression(3)
"one"
Expression(5)
Expression("2")
83
}
let s2 = stringsReduce {
if .random () { // random value Bool
Expression(11)
} else {
Expression("another one")
}
}
Since buildBlock(_:) takes a variadic number of arrays of strings, this would work:
let z = s(separator: " ") {
["this"]
["is"]
["cool"]
}
But that's still clunky. To take strings instead of Arrays of strings, add this function to StringBuilder which takes a variable number of strings:
static func buildBlock(_ strings: String...) -> [String] {
Array(strings)
}
And now you can do this:
let z = s(separator: " ") {
"Hello"
"my"
"friend!"
}
print(z) //Hello my friend!

Swift: How to re-run a function based on the results within a completion block?

This is a rough example (the actual use-case I am working on has to do with the internals of PHImageManager's requestImageForAsset: function), but I'm looking for a pattern that allows you to re-run a function based on the results in the completion block.
Playground code:
private func subtractTen (value: Int, completion: (success: Bool, newVal: Int, [NSObject: AnyObject]?) -> Void) -> Int {
// This is private to represent a black box.
// In my personal use-case, it's actually a Foundation method
completion(success: (value - 10 >= 0), newVal: value - 10, nil)
return value - 10
}
func alwaysPositive (value: Int) -> Int {
var foo: Int
foo = subtractTen(value) { success, newVal, _ in
if success {
print ("yay")
} else {
// I need subtractTen re-run with a new input: newVal
// and I need the resulting value in the calling function
// (in this case, "foo")
print ("re-run subtractTen with newVal, and return the value to the parent function")
}
return
}
return foo
}
alwaysPositive(10)
You can run alwaysPositive with values such as 1, 2, 9, 10, 11, etc. to see when the "re-run" message gets displayed.
Is there a common Swift pattern for what I'm trying to do?
Look at this code, maybe it will help what you are looking for:
override func viewWillAppear(animated: Bool) {
rerunThis(0, callback: { (number_returned: Int, returned: Bool) in
println("returned: \(number_returned), \(returned)");
self.rerunThis(number_returned, callback: { (new_number_returned: Int, returned: Bool) in
println("new returned: \(new_number_returned), \(returned)");
});
});
}
func rerunThis(last_number: Int, callback: ((Int, Bool)->Void)) {
let new_value: Int = last_number+1;
var return_this: Bool = false;
if (new_value <= 1){
return_this = true;
}
callback(new_value, return_this);
}

What is the Swift equivalent of C#/.NET/LINQ's Enumerable.All method?

I want a function that applies a given function to a sequence and returns true iff the given function returns true for every element of the sequence, like Enumerable.All from the C#/.NET/LINQ world.
Building up on Jon's answer: You can use contains()
instead of an (explicit) loop:
extension SequenceType {
func all(#noescape predicate: (Self.Generator.Element) throws -> Bool)
rethrows -> Bool {
return !(try contains { !(try predicate($0)) })
}
}
There isn't a built-in function to do this, but you can easily add your own as a protocol extension method:
extension SequenceType {
func all(#noescape predicate: (Self.Generator.Element) throws -> Bool)
rethrows -> Bool {
for i in self {
if !(try predicate(i)) { return false }
}
return true
}
}
and then use it on a sequence like:
let allPositive = [1, 2, 3].all { $0 > 0 }
Not sure if this helps, but you can achieve the same outcome using reduce. Here's a quick playground I put together to prove the concept:
let nums = [2, 2, 3, 4]
// Will only evaluate to true if all numbers are even.
let allEven = nums.reduce(true) {
if !$0 || $1 % 2 != 0 {
return false
}
return true
}

Swift function with a function as parameter

I have a question about why I get a compilation error of "Missing return in a function". I am following the examples in the "The Swift Programming Language" book and there is a section about passing a function as a parameter of another function.
Here is the book's example which compiles fine:
func hasAnyMatches(list: [Int], condition: Int -> Bool) -> Bool {
for item in list {
if condition (item) {// anonymous function call
return true
}
}
return false
}
func lessThanTen(number: Int) -> Bool {
return number < 10
}
I understand this, but I thought I could make a subtle change, because I felt the if condition(item){ } was redundant.
Here is my alteration:
func hasAnyMatches(list: [Int], condition: Int -> Bool) -> Bool {
for item in list {
return condition(item)
}//error here with "Missing return in a function expected to return bool"
}
I'm returning a bool because I return the result of the function. There is no case where I would not return a bool during the for-in loop.
I don't understand why this does not compile, could someone explain why?
First, your change doesn't do what the old code did. Your version returns the result of testing the first element in the list, not whether any of the elements pass the test.
The reason for the error is that your code isn't guaranteed to execute return at all. If the list is empty, then you'll drop to the end of the function without calling return. The compiler is telling you that.
func hasAnyMatches(list: [Int], condition: Int -> Bool) -> Bool {
for item in list {
if condition(item) {
return true
}
}
return bool
}