I am learning Arrow in a little command-line proje...
# arrow
m
I am learning Arrow in a little command-line project. I want to handle a single argument and, using a context receiver, trying this function:
Copy code
context(Raise<NoSuchElementException>)
fun onlyArg(args: Array<String>): String = args.first()
How do I call this function and handle the exception? (Does this approach make sense?)
I tried using
effect
but am not sure how to use it:
Copy code
fun main(args: Array<String>) {
    val arg = effect {
        onlyArg(args)
    }.fold(/* what here? */, /* what here? */)
}
Or is this the wrong approach?
s
That doesn’t really make sense since the function doesn’t know how to translate from the exception from first to Raise
You could use a custom domain and use
ensureNotNull
.
Copy code
context(Raise<String>)
fun onlyArg(args: Array<String>): String =
  ensureNotNull(args.firstOrNull()) { “Expected single argument, but found none” }
fold
takes 2, or 3 lambdas. The handler for
String
or your error type, an optional handler for uncaught exceptions, and finally a handler for your result of the program.
You might also want to use
recover
if you simple want to recover from an error (
String
).
y
Should there be a bridging function between the throwing world and the raising world for this use case? Something like
Copy code
inline fun <reified T: Throwable> Raise<T>.catchAndRaise(block: () -> A): A = catch<T>(block) { raise(it) }
Although I can imagine that
Raise<Throwable>
would be quite a rare sight, but it can help the transition of code from throw to raise
s
I would almost be more in favor of
runCatching { }.bind()
for that, and take into account fatal / cancellation exceptions
y
Issue is that
Result
doesn't have a typed Throwable, and so the bind here wouldn't really work for something like
Raise<NoSuchElementException>
as in the original use-case. I guess
catch<NoSuchElementException>({ args.first() }) { raise(it) }
is good enough, but it might make the transition harder
s
Doesn’t
runCatching
also swallow cancellation exceptions? Or does arrow provide some other runCatching that I am not aware of?
s
You’re absolutely right @Youssef Shoaib [MOD], something to consider indeed 🤔 @Stylianos Gakis my original idea was to resolve that in
bind
by only putting non-fatal exceptions in
Raise
m
Thanks, @simon.vergauwen and others for your comments. There are better ways to check for only one CLI argument than try to catch
NoSuchElementException
but I am still not clear on the best way to call a function like this:
Copy code
context(Raise<String>)
fun onlyArg(args: Array<String>): String =
    ensureNotNull(args.firstOrNull()) { "Expected single argument, but found none" }
What is the best way to call it?
y
Depends on how you want to handle the error. One way is:
Copy code
fold({ /* program goes here */ }, { /* handle success */ }, { /* handle failure */ })
Or
Copy code
recover({ /* program */ }) { /* handle error */ }
There's also the various raise builders like
either
and
effect
m
Thanks @Youssef Shoaib [MOD] I will experiment with those.