I use it a lot. Please don’t get rid of it.
Ex: aBunchaViewsToHide.forEach { $0.isHiddden = true }
I use it a lot. Please don’t get rid of it.
Ex: aBunchaViewsToHide.forEach { $0.isHiddden = true }
The discussion is not about whether to get rid of it, it's about whether to add an AsyncAlgorithms version of it.
That said, I don't understand what you're gaining from that. Isn't that exactly the same as a for loop, except with a less clear variable name ($0)?
It's more than a less clearly named for in ...
loop. It's useful and flowy at the end of a long chain of higher-order functions such as filter, map, ..., as Wil described. I believe it's actually more clear than assigning the result to a throwaway variable if all you're doing is iterating over the result.
If anything, I'd be in favor of giving .forEach
more power by having it return its input sequence as a discardable result, so .forEach
could be used in the middle of such a transformation chain as well.
I meant the specific case in the post I was replying to… but I see Discourse hid the reply relationship because they were adjacent. Unhelpful of it in this case, ah well.
I'm not sure what you're asking here. Why is wrapping in a Task
relevant here? I would expect this forEach
to be equally applicable whether wrapped in a Task
or not, but I would think most use would eventually (in the medium term) be in naturally async
methods.
Personally, I think we should have forEach
. Not just for parity with Sequence
, but because it lends itself well to one of the fundamental ways of using Swift, chaining. You can make the same arguments used against forEach
against pretty much the entire rest of Swift's collection APIs. Doesn't make them bad, just means users need to know you use them differently than raw loops. So far that doesn't seem to have been an issue. That a tool isn't useful in 100% of cases isn't a reason not to have the tool.
In fact, I'd go farther. I'd say forEach
's signature should change to forEach(...) -> Self
, so that later operations can be chained. This is a nice increase in utility for a very small change.
The reasoning was more so to understand how folks intend or see its use. It was a pattern that I have been seeing emerge when using some of these things in app contexts and I was wondering if some of the use cases folks were seeing were similar to that of .sink
(but perhaps a better name).
How would that even work? AsyncSequence
is not double-pass capable. So unless it is an escaping "diagnostic window for side effects" (which I have much stronger objections that Ben's objection to .forEach
on... mainly because it makes thread safety and self consistency virtually impossible because it breaks the monad's encapsulation), im not sure I follow on how that could be achieved.
forEach
was useful with UIKit due to the abundance of classes, but it will continue on the trip towards complete worthlessness unless two things are fixed:
Cannot reference 'mutating' method as function value
.I.e. it's only good when you already have an existing named closure to use with it.
[1, 2, 3, 4].forEach(&myStruct.add)
?
Ah, I see what you're getting at. Yeah, I've been using Task
's to start stream observer callbacks, but I've just been using raw loops since there is no forEach
. Doesn't seem like there's any other option yet. forEach
makes that slightly nicer for more complex chains.
You're right, I didn't think that through for the async case. In that case it seems easy to just return a custom type like all of the other async operators, one that just passes elements through.
Yes, it would be nice to lift those language limitations, but those are hardly forEach
's fault.
This is why I would like to create a way for return
, break
, and continue
to work in closures so that functions like forEach
can behave and look more like for
loops.
I've met this feature in Ruby, and I loved it (and I guess it's initially a Smalltalk thing). In Ruby, we distinguish procs from lambdas. Procs affect control flow one-level up. Lambdas can not.
# prints
# lambda 1: foo
# lambda 2: foo
# proc 1: bar
# f: bar
def f
l1 = lambda do 'foo' end
puts "lambda 1: #{l1[]}"
l2 = lambda do return 'foo' end
puts "lambda 2: #{l2[]}"
p1 = proc do 'bar' end
puts "proc 1: #{p1[]}"
p2 = proc do return 'bar' end
puts "proc 2: #{p2[]}"
puts "end"
end
puts "f: #{f()}"
There are other funny Ruby features, such as breaking with a value:
def f
[1, 2, 3].each do |x|
break x * 2 if x == 2
end
end
f # 4
Now of course, changing Swift's forEach
behavior would be a breaking change:
func f() {
[1, 2, 3].forEach { x in
guard x != 2 else {
// Returning from f would be a rubyism, and a breaking change
return
}
print(x)
}
}
Why else would you want this features? A popular request is for SwiftUI’s ForEach, but that could be addressed with a lazy for-loop build method in result builders.
forEach
helps to avoid declaring a variable where it's not needed. Does not have to be a long chain, can be take this, filter, do that:
let items = [...]
let filteredItems = items.map { ... }.filter { ... }
for item in filteredItems {
// ...
}
vs.
let items = [...]
items.map { ... }
.filter { ... }
.forEach { item in
// ...
}
In fairness, you can still do it without an extra variable:
let items = [...]
for item in (items.map { ... }.filter { ... }) {
// ...
}
although it is neither nice nor scaleable:
items
.map { ... }
.filter { ... }
.sorted { ... }
.compactMap { ... }
.something { ... }
.somethingElse { ... }
.and { ... }
.so { ... }
.on { ... }
.and { ... }
.so { ... }
.forth { ... }
.forEach { .... }
vs
for item in (items.map { ... }.filter { ... }.sorted { ... }
.compactMap { ... }.something { ... }.somethingElse { ... }
.and { ... }.so { ... }.on { ... }.and { ... }
.so { ... }.forth { ... }) {
// ...
}
perhaps formatted like so to make it ok-ish?
for item in (
items
.map { ... }
.filter { ... }
.sorted { ... }
.compactMap { ... }
.something { ... }
.somethingElse { ... }
.and { ... }
.so { ... }
.on { ... }
.and { ... }
.so { ... }
.forth { ... }
) { ... }
This formatting is not acceptable to me within the for in
body. I also tend to avoid the same with guard
statements, for example:
Ugly:
guard let item = items.first(where: { item
// ...
}) else {
// ...
return
}
Good:
let item = items.first {
// ...
}
guard let item = item else {
// ...
return
}
I use both extensively in my code, depending on context, to express emphasis and focus the readers attention: forEach
to emphasize the collection as a whole, for in ...
to focus attention on what is happening to each element.
That formatting is not nice, I agree. BTW, this is one of the reasons I'm using a custom postfix not
operator:
let isNotSomething = items
.map { ... }
.filter { ... }
.sorted { ... }
.compactMap { ... }
.something { ... }
.not
vs
let isNotSomething = !(items
.map { ... }
.filter { ... }
.sorted { ... }
.compactMap { ... }
.something { ... })
One thing I don't see mentioned here is the fact that forEach
is also very convenient to use when you have optional collections. Is the collection nil
? Then simply don't execute the block.
When using for ... in ...
, you have to have unwrap your collection first ¯\_(ツ)_/¯
What then about for item in sequence? { … }
? Would it be a good idea to have that?
…But I certainly like forEach at the end of a possibly long chain.
And forEach can fetch the type of the items of e.g. Sequence<Int>
, the for loop currently cannot do that.
This is probably not doing what you think it does. And, it could be accomplished with a for in
loop.