-
Notifications
You must be signed in to change notification settings - Fork 30
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
first draft of OxApp with extension companion traits #157
Conversation
@adamw CI crash is unrelated to this code 😮 |
|
||
def run(args: Vector[String])(using Ox): ExitCode = Success | ||
|
||
Main1.main(Array.empty) |
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.
we could also assert that the result is 0 here
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.
sure
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.
but main is Unit
, we check ec to be equal 0 below
|
||
private[ox] def printStackTrace(t: Throwable): Unit = t.printStackTrace() | ||
|
||
private[OxApp] final def handleRun(args: Vector[String])(using Ox): ExitCode = |
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.
is Ox
needed here?
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.
ah we need it for run
, ok
core/src/main/scala/ox/OxApp.scala
Outdated
case Left(fatalErr) => throw fatalErr | ||
case Right(exitCode) => exit(exitCode.code) | ||
|
||
private[ox] def exit(code: Int): Unit = System.exit(code) |
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.
maybe add a comment that these are private[ox]
vs private
because of testing
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.
sure
|
||
private[ox] def mountShutdownHook(thread: Thread): Unit = | ||
try Runtime.getRuntime.addShutdownHook(thread) | ||
catch case _: IllegalStateException => () |
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.
shouldn't we re-throw the exception if mounding the hook fails?
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.
no, it's thrown only if we're already in shut down of the vm so there's nothing more to do
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.
for reference: CE IOApp only shuts down it's thread pools when it gets ISE here
core/src/main/scala/ox/OxApp.scala
Outdated
mountShutdownHook(interruptThread) | ||
|
||
cancellableMainFork.joinEither() match | ||
case Left(iex: InterruptedException) => exit(0) |
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.
is this success, if the app was interrupted?
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.
highly debatable, some sources treat interruption as failure and return 1 or 130 (128+SIGINT(2)), some return 0 if application was able to interrupt itself and shut down cleanly (as there were no issues during shutdown), it's up to us to decide. JVM itself returns 130 on unhandled sigint.
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.
ok, so here we would treat this as "handled sigint"?
but that's great info, can you put the above in a comment in the code? for future readers :)
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.
ok, I'll even make this configurable
core/src/main/scala/ox/OxApp.scala
Outdated
|
||
def run(using Ox): Unit | ||
|
||
trait WithErrors[E] extends OxApp: |
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 we use an arbitrary error mode WithErrorMode
, and specialise to eithers with WithEither
?
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.
sure
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.
is something like:
trait WithErrorMode[E, F[_]](em: ErrorMode[E, F]) extends OxApp:
override def run(args: Vector[String])(using ox: Ox): ExitCode =
val result = run(args.toList)
if em.isError(result) then ExitCode.Failure(1)
else ExitCode.Success
def run(args: List[String])(using Ox): F[ExitCode]
abstract class WithEitherErrors[E] extends WithErrorMode(EitherMode[E]()):
type EitherScope[Err] = Label[Either[Err, ExitCode]]
override final def run(args: List[String])(using ox: Ox): Either[E, ExitCode] =
either[E, ExitCode](label ?=> run(args.toVector)(using ox, label))
def run(args: Vector[String])(using Ox, EitherScope[E]): ExitCode
what you had in mind?
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.
mind the change of Vector to List in args because we have conflicting signatures so there's that, we could do a disambiguation by always-provided implicit but that's even more... arcane
also without handleErrors there's no translation of error into exit code
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.
it could be also:
trait WithErrorMode[E, F[_]](em: ErrorMode[E, F]) extends OxApp:
override def run(args: Vector[String])(using ox: Ox): ExitCode =
val result = run(args.toList)
if em.isError(result) then
try handleError(em.getError(result))
catch
case NonFatal(e) =>
e.printStackTrace()
ExitCode.Failure(1)
else ExitCode.Success
def handleError(e: E): ExitCode
def run(args: List[String])(using Ox): F[ExitCode]
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.
and F[ExitCode]
doesn't make much sense in case where we have Label[Either[E, Nothing]]
in scope as it means that errors are to be delivered with .ok()
/ break()
so unwrapped ExitCode
as retval
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.
hm the Vector/List is also hacky. Maybe we simply need a different name. We've got fork
& forkError
for the fork-with-error-mode variant, maybe here we could keep a similar convention? (run
& runError
). Or come up with better names for both :)
another nitpick: I'd avoid using "scope" in names, as we already have concurrency scopes etc. Maybe rename EitherScope
-> EitherError
?
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.
also, does : F[ErrorCode]
and ErrorCode
have the same erasure? Isn't the first erased to Object
, making the signatures different?
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.
[error] -- [E120] Naming Error: /Users/lbialy/Projects/foss/ox/core/src/main/scala/ox/OxApp.scala:97:8
[error] 97 | def run(args: Vector[String])(using Ox): F[ExitCode]
[error] | ^
[error] |Double definition:
[error] |override def run(args: Vector[String])(using ox: ox.Ox): ox.ExitCode in trait WithErrorMode at line 90 and
[error] |def run(args: Vector[String])(using x$2: ox.Ox): F[ox.ExitCode] in trait WithErrorMode at line 97
[error] |have matching parameter types.
Unfortunately this is illegal.
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.
Ah right, signature is only name+parameters, not the return type
Looks great! :) Some docs would be good :) |
core/src/main/scala/ox/OxApp.scala
Outdated
case Left(fatalErr) => throw fatalErr | ||
case Right(exitCode) => exit(exitCode.code) | ||
|
||
private[ox] def exit(code: Int): Unit = System.exit(code) |
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.
Doesn't it make sense for this function to accept ExitCode
, not Int
?
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.
yeah, it does
core/src/main/scala/ox/OxApp.scala
Outdated
case Failure(exitCode: Int = 1) extends ExitCode(exitCode) | ||
|
||
trait OxApp: | ||
def main(args: Array[String]): Unit = |
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.
final
?
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.
true
Looks good! I think we're just missing the documentation. Only thing I think I would change, is renaming the |
The signature that requires |
@lbialy yeah this should work fine, also will be consistent with |
77c9b41
to
eb9c897
Compare
@adamw this is now ready :) |
core/src/main/scala/ox/OxApp.scala
Outdated
/** This value is returned to the operating system as the exit code when the app receives SIGINT and shuts itself down gracefully. | ||
* Default value is `ExitCode.Success` (0). JVM itself returns code `130` when it receives `SIGINT`. | ||
*/ | ||
gracefulShutdownExitCode: ExitCode = ExitCode.Success |
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 generally prefer to avoid default parameters for case classes - they are hard to change later. But doesn't seem likely to change.
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.
Yes, since we have the default instance let's move this there
No description provided.