How to Reference Instance Function When Calling Sequencetype.Foreach

Is there a way to reference instance function when calling SequenceType.forEach?

There are two different problems here. The trailing closure syntax
can be used when calling a function and the last parameter is a closure,
so

let b1 = someFoos.contains({ $0.isBaz })
let b2 = someFoos.contains { $0.isBaz }

are fully equivalent. However, the trailing closure syntax can be problematic in the condition of an if-statement:

if someFoos.contains({ $0.isBaz }) { }  // OK
if someFoos.contains { $0.isBaz } { } // Compiler error
if (someFoos.contains { $0.isBaz }) { } // OK, as noted by R Menke

We can only speculate why the second one does not work. It could be that the compiler
takes the first { as the start of the if-body. Perhaps this will
change in a future version of Swift but probably it is not worth
the effort.


The other problem is about curried functions.

someFoos.forEach(bar2)

compiles because bar2 has the type Foo -> Void, and that is exactly
what the forEach() method expects. Foo.bar, on the other hand,
is a curried function (see http://oleb.net/blog/2014/07/swift-instance-methods-curried-functions/) which takes the instance as the first
argument. It has the type Foo -> () -> (). So

Foo.bar(someFoo)

is a closure with type () -> (), and

Foo.bar(someFoo)()

calls the bar method on the someFoo instance.

(Note: The following is not meant as an actual recommendation,
but only as a demonstration about curried functions and fun
with closures!)

To pass Foo.bar directly as an argument to forEach() we need to
"swap" the order of the parameters. Haskell has a "flip" function for that purpose,
and it is also possible in Swift (see e.g. How to write a flip method in Swift?):

func flip<A, B, C>(f: A -> B ->C) -> B -> A ->C {
return { b in { a in f(a)(b) } }
}

Then flip(Foo.bar) has the type () -> Foo -> (), so
the void argument of the bar method can be applied

flip(Foo.bar)()

to get a Foo -> () closure, and

flip(Foo.bar)()(someFoo)

calls the bar method on the someFoo instance.
And now we can call

someFoos.forEach (flip(Foo.bar)())

without using a closure expression { .. } !!

If isBaz were a method instead of a property

func isBaz() -> Bool { return false }

then you
could do the same in the if-expression:

if someFoos.contains(flip(Foo.isBaz)()) { 
// ...
}

Again, this is only meant as a demonstration. Also properties
are not curried functions, so this cannot be done with
your isBaz property.

Calling instance method on each object in array

What you are doing in

items.forEach { $0.instanceMethod() }
let mappedItems = items.map { $0.mappingInstanceMethod() }

is a clean and Swifty way. As explained in Is there a way to reference instance function when calling SequenceType.forEach?, the first statement cannot be reduced
to

items.forEach(Item.instanceMethod)

There is one exception though: It works with init methods
which take a single argument. Example:

let ints = [1, 2, 3]
let strings = ints.map(String.init)
print(strings) // ["1", "2", "3"]

Swift, use function UIView.removeFromSuperview as an argument in forEach function over array of views

It's easy enough to just call the method on $0:

views.forEach {
$0.removeFromSuperview()
}

(And you can name the argument if you like.)

But you could also wrap it into a method:

extension Sequence {
func onEach(_ invoke: (Iterator.Element) -> () -> Void) {
self.forEach { invoke($0)() }
}
}

This works because instance methods can be represented as functions that take an instance of their type and return a function that has their "top-level" signature.

One downside here is that you can't include the rethrows annotation that's present on forEach, because rethrows only applies if the function argument itself is throwing.

How to use a swift Instance method with flatMap?

With instance method, you cannot.

Instance method is a curry method, the type (String) -> () -> Array<String> mean "a method take string and return a function which takes nothing and return array of string".

So you can do like this, but not as you write.

print(animals.flatMap{ String.double($0)() }) // ["Ant", "Ant", "Bear", "Bear", "Cat", "Cat"]

What you need is a static method. It simply take string and return array of string.

extension String {
static func double(_ string: String) -> [String] {
return [string, string]
}
}

print(animals.flatMap(String.double)) // ["Ant", "Ant", "Bear", "Bear", "Cat", "Cat"]

Calling a function on a single instance?

You can introduce a dismiss callback in PopupViewController and implement it inside the presenter i.e, TableViewController as below and get rid of the static approach.

class PopupViewController: UIViewController {

public var onDismiss: (() -> Void)?

// Using dismiss callback to notify the subscriber
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
super.dismiss(animated: flag, completion: self.onDismiss)
}

// rest of your code
}

Implement onDismiss callback in TableViewController

class TableViewController {

func showPopup() {
let popup : PopupViewController = self.storyboard?.instantiateViewController(withIdentifier: "PopupViewController") as! PopupViewController
popup.onDismiss = {[weak self] in
self?.test()
}
self.presentOnRoot(with: popup)
}

// rest of your code
}

Better way to check the instance is kind from list of classes?

If I think about it logically, it would make a lot more sense to define a superclass / protocol for them, something like this:

class MyClassNumber { }

class MyClassOne: MyClassNumber { }
class MyClassTwo: MyClassNumber { }
class MyClassLetter { }

let one = MyClassOne()
let two = MyClassTwo()
let letter = MyClassLetter()

if one is MyClassNumber {
// TRUE
}

if two is MyClassNumber {
// TRUE
}

if letter is MyClassLetter {
// FALSE
}

Don't see any use case for yours

1 global function or many instance functions

I would suggest neither.

You should have a data model class and the upvote function should be part of that class.

class Post {
var postID: String
public private(set) var votes: Int

...

func upvote() {
self.votes += 1
}
}

Then you would call it as

 somePost.upvote()

1 global function or many instance functions

I would suggest neither.

You should have a data model class and the upvote function should be part of that class.

class Post {
var postID: String
public private(set) var votes: Int

...

func upvote() {
self.votes += 1
}
}

Then you would call it as

 somePost.upvote()

Calling a function inside itself

That's called recursion, and it's perfectly legal:

var count = 0
func theFunc() {
print(count)
count += 1
if count < 4 {
theFunc()
}
}
theFunc() // 0 1 2 3

The only trick is not to recurse too deeply, as you risk running out of resources, and don't forget to put some sort of "stopper" (such as your if count < 4), lest you recurse forever, resulting in (oh the irony) a stack overflow.

[Extra for experts: there are some languages, such as LISP, that are optimized for recursion, and where recursion is actually preferred to looping! But Swift is not really one of those.]

Better way to write this if condition with a force-unwrap?

There are many ways to solve this situation.

The simplest one is to extract the condition into a separate function/closure and check

func fileExists(defaultUrl: URL?) -> Bool {
guard let defaultUrl = defaultUrl else { return false }
return FileManager.default.fileExists(atPath: defaultUrl.path)
}

The same can be done using a variable:

let fileExists: Bool

if let defaultUrl = defaultUrl {
fileExists = FileManager.default.fileExists(atPath: defaultUrl.path)
} else {
fileExists = false
}

Or, you can use Optional.map:

let fileExists = defaultUrl.map { FileManager.default.fileExists(atPath: $0.path) } ?? false


Related Topics



Leave a reply



Submit