Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@hanny24
Copy link
Contributor

@hanny24 hanny24 commented Sep 22, 2019

Partially solves #1182, missing raceAll and forkAll.
Inspired by #1424

(for {
size <- UIO.effectTotal(as.size)
todo <- Ref.make(size)
buffer <- UIO.effectTotal(new Array[Any](size).asInstanceOf[Array[B]])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this not cause a java.lang.ClassCastException ? A quick experiment from the scala repl:

scala> new Array[Any](100).asInstanceOf[Array[String]]
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
  ... 36 elided

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the trick here is not to cast it to specific type, but only to a generic one.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't get how this works at runtime though, because the array will need to get a concrete type at some stage right?

scala> def test[A](a:A):Array[A] = new Array[Any](1).asInstanceOf[Array[A]]
test: [A](a: A)Array[A]

scala> test(1)
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [I
  ... 28 elided

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't is caused by specialization? There is even a test that uses foreachPar on a List[Int]: https://github.com/hanny24/scalaz-zio/blob/35bd04f28cedeb3c988a83af206c37d48d3b58ad/core-tests/shared/src/test/scala/zio/ZIOSpec.scala#L70

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah saw that test now. I have to admit that I don't understand how this works. It clearly does though :)

c <- ZIO.uninterruptibleMask { restore =>
for {
as <- ZIO.traverse(as)(a => ZIO.interruptible(fn(a)).fork)
_ <- as.view.zipWithIndex.foldLeft[ZIO[R, E, _]](ZIO.unit) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The call to .view here seems unnecessary, or am I missing something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not want to materialize it. But if we change to ZIO.traverse we will need to materialize it anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rewritten in a way that uses ZIO.traverse_

as <- ZIO.traverse(as)(a => ZIO.interruptible(fn(a)).fork)
_ <- as.view.zipWithIndex.foldLeft[ZIO[R, E, _]](ZIO.unit) {
case (io, (f, idx)) =>
io *> f.await.flatMap(arbiter(as, promise, buffer, todo, idx)).fork
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An alternative to this is to combine this step with the ZIO.traverse(as)( ...) call above, so that one less fiber per element in as is created.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it actually that easy to combine it: we need to be able to interrupt all fibers from arbiter helper method.

else ZIO.unit
}
.refailWithTrace
final def foreachPar_[R, E, A](as: Iterable[A])(f: A => ZIO[R, E, _]): ZIO[R, E, Unit] = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is duplicating a lot from the foreachPar method, maybe this could be rewritten to leverage that function instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it's possible, I just wanted to avoid allocation of the extra array with results.

@LGLO LGLO changed the title issue-1182: forachPar, forachPar_, collectAllPar, reduceAllPar issue-1182: foreachPar, foreachPar_, collectAllPar, reduceAllPar Sep 22, 2019
(for {
size <- UIO.effectTotal(as.size)
todo <- Ref.make(size)
buffer <- UIO.effectTotal(new Array[Any](size).asInstanceOf[Array[B]])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
buffer <- UIO.effectTotal(new Array[Any](size).asInstanceOf[Array[B]])
buffer <- UIO.effectTotal(Array.ofDim[B](size))

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, it's probably best to move the buffer into a Ref.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is gain of keeping mutable array in Ref? I think that reference will never change, so Ref#modify will never discover that value was changed.
Secondly - we know that each fiber operates only on it's index, so that are really independent operations.
What can go wrong?

Re: ofDim - I understand this gives better performance for primitive types but requires ClassTag[B] - do we want to change method signatures?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps answer for my first question is memory barrier.

promise <- Promise.make[E, List[B]]
c <- ZIO.uninterruptibleMask { restore =>
for {
as <- ZIO.traverse(as)(a => ZIO.interruptible(fn(a)).fork)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 Can we look for a solution that only traverses the structure once and forks one fiber per element?

ZIO.traverse(as.zipWithIndex){ case (a, i) => fn(a).flatMap(arbiter(...)).fork }

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For sure!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, in #1937

e => promise.halt(e).unit *> Fiber.interruptAll(fibers),
a =>
todo.modify { t =>
buffer.update(idx, a)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's an effect which should be captured, making buffer a Ref[Array[B]] will help here.

@regiskuckaertz
Copy link
Member

@hanny24 hi 👋 gonna close this as all these combinators have recently be refactor into stack-safe array-backed versions. Check out #2590

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants