I'm sure there's a way to improve this code (in th...
# arrow
d
I'm sure there's a way to improve this code (in the thread)... any comments (from my experiments with Arrow and Ktor)?
Copy code
inline fun <reified T : Any> KtorCtx.ensureParameter(name: String): Either<ResponseError, T> = either {
    val value = call.parameters[name]
    ensureNotNull(value) { ResponseError.MissingQueryParameter(name) }

    when(T::class) {
        String::class ->
            value as T

        Int::class ->
            ensureNotNull(value.toIntOrNull()) { ResponseError.QueryParameterCastNotPossible(name, T::class.qualifiedName ?: "") } as T

        else ->
            raise(ResponseError.QueryParameterCastNotPossible(name, T::class.qualifiedName ?: ""))
    }
}

inline fun <reified T : Any, R : Any> KtorCtx.ensureParameter(
    name: String, factory: (T) -> Either<Error, R>
): Either<ResponseError, R> = ensureParameter<T>(name).flatMap { value ->
    factory(value).mapLeft { ResponseError.InvalidParameter("$name: $value") }
}

inline fun <reified T : Any, R : Any> KtorCtx.ensureParameter(
    name: String, factory: (T) -> R
): Either<ResponseError , R> = either { 
    ensureParameter<T>(name).let { value ->
        withError({ ResponseError.InvalidParameter("$name: $value") }) {
            factory(value.bind())
        }
    }
}
The last two are used to put values into value classes... the first for value classes that have some kind of validation code in the companion object, the second for those that don't.
Copy code
val param1 = ensureParameter("param1", ParamType::invoke) // Invoke returns Either<SomeDomainError, ...>
val param2 = ensureParameter("param2", ::ParamType2)
a
I personally try to avoid using
let
and
flatMap
when using with this kind of code
Copy code
inline fun <reified T : Any, R : Any> KtorCtx.ensureParameter(
    name: String, factory: (T) -> R
): Either<ResponseError , R> = either { 
  val param = ensureParameter<T>(name).bind()
  withError({ ResponseError.InvalidParameter("$name: $value") }) {
    factory(param)
  }
}
👍🏼 1
d
Good point... but for avoiding flatMap, it's a bit messier?
Copy code
@JvmName("ensureParameter_either")
inline fun <reified T : Any, R : Any> KtorCtx.ensureParameter(
    name: String, factory: (T) -> Either<Error, R>
): Either<ResponseError, R> = either { 
    factory(ensureParameter<T>(name).bind()).mapLeft { ResponseError.InvalidParameter("$name: $value") }.bind()
}
The extra either needs to be added + two binds...
a
Copy code
either { 
    val param = ensureParameter<T>(name).bind()
    withError({ ResponseError.InvalidParameter("$name: $value")  }) {
      factory(param).bind()
    }
}
my suggestion is to break it in several lines (I like my `bind`s at the end of the line) and use
withError
instead of
mapLeft
👍🏼 1
d
Do you ever use mapLeft then?
a
my general rule is that if I'm using the Raise DSL, then try not to manipulate `Either`s,`Option`s and so on directly, but
bind
and use the DSL operations
1
d
So when don't you use the Raise DSL? My case was originally not using it, and we made it use it, and now that it's using it, we need to replace everything with binds... 😵‍💫
It's been a while I've been using Arrow, but I never really got this point clear when to use what... there's a bunch of ways to do the same thing 🤯
a
honestly, nowadays Raise has become the standard for me
It's been a while I've been using Arrow, but I never really got this point clear when to use what... there's a bunch of ways to do the same thing 🤯
yep, this is one rough problem to solve. On the one hand, we want to give people who are used to Either from other ecosystems the tools they're used to work with. On the other hand, we want to have a powerful Raise DSL because we think it embodies the Kotlin-idiomatic approach better. So we end up with two ways to do the same.
2
☝️ 1
e
It's a lot nicer to read if you use the snippet function 😉
👍🏼 1
d
> my suggestion is to break it in several lines (I like my `bind`s at the end of the line) and use
withError
instead of
mapLeft
Now that I look at it more, I think I see why that's better now thanks! I'll try to keep in mind to prefer Raise from now on.
l
The documentation on Raise seemed a lot weaker, so I still don’t know what that is or how to use it.
a
I'm sorry to hear that; we're happy to improve the docs. What information were you missing? Or maybe, what blocked you to start using it?
l
Well, I am familiar with Either from other languages (though I prefer the more specialized Result, a la Rust). So I was looking for docs on how to use that. And I kept seeing it shown alongside a Raise thing, but had no idea what that was. I still don’t. 🙂 If it is, as I gather from the above thread, an alternate approach to Either-style error handling, then I’d expect to see that with its own top-level page under Typed Errors. Either has one, but Raise does not, so I’m not clear where to start looking for information on “WTF is Raise?”
d
image.png
@Larry Garfield
It's a bit concise, I agree... but it does give most of the information there.
I think if you look at examples, it might also make it easier to catch on
a
one thing that it's especially difficult for me, when writing docs, is to have enough "points of entry" for people with different background
some people may know Either from other ecosystems and want the differences between them and Arrow's; some people are completely new to the concept and may be better served by a general tutorial; ...
l
Yeah, I used to be the docs lead at my former company (PaaS). I get the challenge. 🙂
d
Yeah... sometimes it's like speaking another language to someone that's just starting to learn it... you have to guess what they know and what they don't. I was also struggling in the beginning... (and still am in certain things...) but examples helped a lot. Like Simon's video on using Arrow with Ktor...
l
My thinking is “I’ve heard of Raise, I want to know how to use it”, which means Raise should have a dedicated page. For “I don’t know what I’m doing, what’s this type-based error handling stuff?” just have the overview but link over to the dedicated pages for Raise, Either, etc. That may have a bit more redundancy, but it has more natural entry points.
d
That's the thing, this is all about catching the general idea behind all this and applying it in other places... the current docs are trying to get that through... but it's just too concise right now. I think a few more elaborate examples and comparisons (or maybe even a tutorial or cookbook like was previously mentioned), would be more helpful than just a technical documentation of all the features.
l
Quite possibly. Are you familiar with the four-quadrant style of docs? (I’m only lukewarm on it myself, but it does have interesting points.)
For me the piece missing is “I’ve heard of thing X, I want to learn about X, where do I go to start my X journey?” For Either, there’s an obvious page for it. For Raise, there is not.
d
Interesting, it seems to be this: https://docs.divio.com/documentation-system/?
l
Yeah, that thing. I’ve seen it presented from a number of sites lately under slightly different names.
a
based on this thread, I've started writing a guide "From
Either
to `Raise`". Would somebody like to give it a read, and provide some feedback? https://github.com/arrow-kt/arrow-website/pull/307 The main audience is people who know Either, but want to switch to the Raise DSL.
❤️ 14
🚀 3
l
Will look after my next meeting, thanks!
n
I use strictly
Either
in an app I wrote and have been interested in what benefits using
Raise
could possibly give me. I'll take a look and let you know what I think as someone who understands
Either
extensively and does not know much about
Raise
(your target audience).