-
Notifications
You must be signed in to change notification settings - Fork 50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: ScalaJS's KyoApp #807
Conversation
It's quite late in my time zone, I'll put some descriptions tomorrow |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for taking this on!
kyo-core/jvm/src/main/scala/kyo/KyoAppBasePlatformSpecific.scala
Outdated
Show resolved
Hide resolved
kyo-core/jvm/src/main/scala/kyo/KyoAppBasePlatformSpecific.scala
Outdated
Show resolved
Hide resolved
kyo-core/js/src/main/scala/kyo/KyoAppBasePlatformSpecific.scala
Outdated
Show resolved
Hide resolved
kyo-core/js/src/main/scala/kyo/KyoAppBasePlatformSpecific.scala
Outdated
Show resolved
Hide resolved
kyo-core/js/src/main/scala/kyo/KyoAppBasePlatformSpecific.scala
Outdated
Show resolved
Hide resolved
kyo-core/js/src/main/scala/kyo/KyoAppBasePlatformSpecific.scala
Outdated
Show resolved
Hide resolved
kyo-core/js/src/main/scala/kyo/KyoAppBasePlatformSpecific.scala
Outdated
Show resolved
Hide resolved
|
||
final def main(args: Array[String]) = | ||
this._args = args | ||
if initCode ne null then initCode() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we could throw an exception in case the App
has no run
calls. I imagine some users might try to define def run
like in other effect systems and it'd just not execute anything silently.
kyo-core/js/src/main/scala/kyo/KyoAppBasePlatformSpecific.scala
Outdated
Show resolved
Hide resolved
kyo-core/jvm/src/main/scala/kyo/KyoAppBasePlatformSpecific.scala
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mind adding a test with multiple run
s if we don't have one already?
I think it can be as simple as building a list of runs
val x = ListBuffer...
object TestApp extends KyoApp:
run { IO(x += 1) }
run { IO(x += 2) }
TestApp.main(Array.empty)
assert(x == Seq(1, 2, 3....)
66133fd
to
53ae543
Compare
Updated the code a bit, and added a small test for KyoApp in js |
87e52f1
to
60a85a5
Compare
Updated, fix and add one more test for KyoApp on JS |
60a85a5
to
535a895
Compare
Updated, squash commits |
@@ -30,6 +31,16 @@ class KyoAppTest extends Test: | |||
runs <- ref.get | |||
yield assert(runs == 3) | |||
} | |||
"multiple runs on JS" taggedAs jsOnly in run { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both JVM and JS should pass this test. I notice we have a similar test, but it doesn't check ordering which is important. You can merge them if you want.
"multiple runs on JS" taggedAs jsOnly in run { | |
"multiple ordered runs" in run { |
@@ -30,6 +34,17 @@ class KyoAppTest extends Test: | |||
runs <- ref.get | |||
yield assert(runs == 3) | |||
} | |||
"multiple runs on JS" taggedAs jsOnly in { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test is necessary for JVM and JS. We can delete the other one.
"multiple runs on JS" taggedAs jsOnly in { | |
"ordered runs" in { |
|
||
final protected def args: Array[String] = _args | ||
|
||
private var _args: Array[String] = null | ||
|
||
private val initCode = new ListBuffer[() => Unit] | ||
protected var maybeProc: Maybe[() => Unit] = Absent |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be Iterable[() => Unit]
? Or IterableOnce
? I think the changes in this PR break multiple runs on JVM.
val result = IO.Unsafe.run(Abort.run(Async.runAndBlock(timeout)(handle(v)))).eval | ||
printResult(result) | ||
) | ||
if maybeProc.isEmpty then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Won't this only be true
once? I think we can make the maybeProc
a generic Iterable:
private var initCode: Iterable[() => Unit] = Nil
Then we can just always update it. Both versions should be able to conform to this signature.
Scala JS:
initCode = maybeProc.toList
jvm
initCode = procListBuffer.toList
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I understand what you're trying to say now, I'll try to update this.
535a895
to
bd2fb4a
Compare
|
||
final override protected def run[A: Flat](v: => A < (Async & Resource & Abort[Throwable]))(using Frame): Unit = | ||
import AllowUnsafe.embrace.danger | ||
initCode += (() => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JVM side, code is pretty much the same
@@ -30,20 +34,47 @@ class KyoAppTest extends Test: | |||
runs <- ref.get | |||
yield assert(runs == 3) | |||
} | |||
"effects" taggedAs jvmOnly in { | |||
"orderred runs" in { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
update this test for both platforms
bd2fb4a
to
855f8ec
Compare
@@ -30,20 +34,47 @@ class KyoAppTest extends Test: | |||
runs <- ref.get | |||
yield assert(runs == 3) | |||
} | |||
"effects" taggedAs jvmOnly in { | |||
"orderred runs" in { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"orderred runs" in { | |
"ordered runs" in { |
final override protected def run[A: Flat](v: => A < (Async & Resource & Abort[Throwable]))(using Frame): Unit = | ||
import AllowUnsafe.embrace.danger | ||
val currentAsync: Unit < (Async & Abort[Throwable]) = | ||
Async.timeout(timeout)(handle(v)).map(value => printResult(Result.success(value))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you put the timeout
within the initiation of the Unit function, (when you update initCode
) you will avoid some sleep fibers and potentially racy behavior.
I think it makes sense to timeout the whole application anyways.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mimicked the code in JVM (timeout for each run
s). Will updated this
maybePreviousAsync = maybePreviousAsync match | ||
case Absent => Present(currentAsync) | ||
case Present(previousAsync) => Present(previousAsync.map(_ => currentAsync)) | ||
if initCode.isEmpty then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was initially wondering why this would work, when only the first fiber would be initialized, but then I realized the first fiber would begin the execution of the next...
In my testing, I think this code also causes repeatWithDelay
to run forever. The run
fiber is already complete when the timeout
expires.
Regardless, I re-wrote this a bit which I think is a simplification:
package kyo
import kyo.Maybe.Absent
import kyo.Maybe.Present
abstract class KyoAppPlatformSpecific extends KyoApp.Base[Async & Resource & Abort[Throwable]]:
private var previous: Maybe[Unit < (Async & Abort[Throwable])] = Absent
final override protected def run[A: Flat](v: => A < (Async & Resource & Abort[Throwable]))(using Frame): Unit =
import AllowUnsafe.embrace.danger
val current: Unit < (Async & Abort[Throwable]) =
Abort.run(handle(v)).map(result => IO(printResult(result)).andThen(Abort.get(result)).unit)
previous = previous match
case Absent => Present(current)
case Present(p) => Present(p.map(_ => current))
initCode = previous.map { async => () =>
val raced = Clock.repeatWithDelay(1.hour)(()).map { fiber =>
val race = Async.race(fiber.get, async)
Async.timeout(timeout)(race)
}
val _ = IO.Unsafe.run(Async.run(raced)).eval
}.toList
end run
end KyoAppPlatformSpecific
In KyoApp
I made initCode
a variable with a List for ease of conversion with Maybe.
protected var initCode: List[() => Unit] = List.empty
You can check if the Clock fiber is still running by changing ()
to a Console.println
and increase the frequency of heartbeat.
run { IO(x += 1) } | ||
run { IO(x += 2) } | ||
run { IO(x += 3) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's mix in Async
just for posterity:
run { Abort.delay(10.millis)(IO(x += 1)) }
run { Async.delay(10.millis)(IO(x += 2)) }
run { Async.delay(10.millis)(IO(x += 3)) }
folks, anything pending to merge this? We can follow up with improvements if needed. It'd be nice to get the issue fixed. |
I'll try to finalize this in the upcoming days, I think there aer only some little code conventions/styles left to adjust |
36ea6a1
to
169cded
Compare
@fwbrasil @hearnadam I've updated the code, it should be good now |
169cded
to
114f224
Compare
|
||
import scala.collection.mutable.ListBuffer | ||
|
||
abstract class KyoAppPlatformSpecific extends KyoApp.Base[Async & Resource & Abort[Throwable]]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The build is failing in Native. I think it's just a matter of moving this file to kyo-core/jvm-native/src/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated and tested, should work now
114f224
to
2d9f6ca
Compare
@HollandDM congrats on your first contribution 🙌 |
resolve #784