https://kotlinlang.org logo
Title
n

Norbi

03/28/2023, 6:15 PM
Two more noob questions 🙂 : 1. Why is there a need for separate
recover()
and
catch()
functions? Why isn't everything
fold()
? 2. When is the type parameter
B
and parameter
transform
of
fold()
is useful? Wouldn't it be enough to have
// Just an example, this function is not present is Arrow
inline fun <Error, A> fold(
  @BuilderInference block: Raise<Error>.() -> A,
  recover: (error: Error) -> A,
  catch: (throwable: Throwable) -> A = { throw it }
)
and handle the transformation after the
fold()
call? The above is very similar to the actual
recover()
function:
public inline fun <Error, A> recover(
  @BuilderInference block: Raise<Error>.() -> A,
  @BuilderInference recover: (error: Error) -> A,
  @BuilderInference catch: (throwable: Throwable) -> A,
): A = fold(block, catch, recover, ::identity)
These questions popped up because I don't like the naming of the
recover()
and
catch()
functions. I almost always use
recover()
but it is a bit strange that it also has a parameter named
recover
. So although the core function is
fold()
, I never use it because
recover()
with the
transform = ::identity
argument seems to be appropriate always... Thanks.
+1: is the name
fold
a well-known FP terminology? (So why not
run()
or
execute()
or
perform()
or ...?)
s

simon.vergauwen

03/28/2023, 6:49 PM
Hey @Norbi, Okay, question 1 & 2 seem to go hand-in-hand 😅
fold
is useful when you need to align types after user code, so when building custom DSLs. I've only really needed it in one place, ior. So
recover
is probably sufficient in 99,99% of the cases. It's also the natural operation for
Raise
. Where you
raise
and
recover
recover
with 3 parameters, is basically just an alias for
fold
with
::identity
.
fold
is indeed a common name in FP for this kind-of operation, but seems foreign to use all-over code so
recover
seems to make more sense. Where you normally only need
recover
or
catch
in regular code, and even that only occasionally. Where typically most code exists out of
flatMap
most of the time, but in this case it's just regular code and
flatMap
is not needed.
recover
with a
recover
parameter is perhaps a bit unfortunate, but I am not sure what a better name would be. At least at this point it's consistent with all other functions and parameter names. In the end like you said everything is just
fold
but with different combinations of arguments. The reason for
transform
is just that you can align the value returned from
block
,
recover
and
catch
to a different type such as shown in
Ior
.
Actually,
ior
can be rewritten with
recover
😅 So the
transform
parameter could be dropped but I would be in favor of
recover
rather than
fold
and that still brings the question of parameter name 🤔
n

Norbi

03/28/2023, 7:03 PM
Thanks for the quick answer, now I have some more insight to the design decisions. I'd better get used to the name of
recover()
, it seems that I was the only one not completely satisfied with it 🤔 🙂
s

simon.vergauwen

03/28/2023, 7:04 PM
Why are you not liking
recover
? From your answer it sounds like you're not fan of the parameter name
recover
resulting in:
fun Raise<String>.example(): Int = 1

recover(
  block = { example() },
  recover = { str -> str.length }
)
n

Norbi

03/28/2023, 7:08 PM
Yes, the main reason is that the parameter name is the same as the function name 🙂 Really insignificant "issue", so don't waste your time with it 🙂 Thanks again for the detailed answers!
Besides,
effect {
    ...
}.recover {
    ...
}.bind()
can also be used which has a nice syntax imho 🙂
"which has a nice syntax imho" - the problem is that I always forget to call
bind()
😕
s

simon.vergauwen

03/29/2023, 6:37 PM
There is this project that leverages Detekt, but I still need to PR an update for the new DSL. https://github.com/woltapp/arrow-detekt-rules