OK, riddle me this. I have a class in file/namesp...
# getting-started
l
OK, riddle me this. I have a class in file/namespace A. It has methods on it, which are public, as well as a companion object. In file/namespace B, I am able to see the class, I am able to call method son the companion object, but I cannot call methods on objects of the class itself. … Why? What obvious newbie mistake am I making? 🙂
c
…are you trying to call methods on the class and not on instances? what's your code on the callsite?
l
Call site:
Copy code
val test = Result.fail("Bad")
        
test.<my methods are not available in the IDE>
Class i question (yes, my own result monad object, largely for educational purposes):
Copy code
sealed class Result<out T, out E> {

    data class Success<T>(val value: T) : Result<T, Nothing>()
    data class Error<E>(val error: E) : Result<Nothing, E>()

    fun <T, E> Result<T, E>.then(block: () -> T) = when(this) {
        is Result.Success -> Result.of { block() }
        else -> this
    }

    fun <T, E> Result<T, E>.error(block: () -> T) = when(this) {
        is Result.Error -> Result.fail(block())
        else -> this
    }

    companion object {

        fun <T> ok(value: T): Result<T, Nothing> = Result.Success(value)

        fun <E> fail(error: E): Result<Nothing, E> = Result.Error(error)

    }
}
c
what's the type of
test
? (CTRL SHIFT P in IDEA)
l
Result<Nothing, String>
c
my own result monad object, largely for educational purposes
If it's for educational purposes, I recommend naming it something else to avoid import weirdness 🙂 But yeah, in the real world please use #arrow
l
Which… seems odd, as it should be an instance of Result.Error?
c
I don't see anything specific that's wrong with this example
can you reproduce on https://play.kotlinlang.org/?
l
Well that gives me a 404…
c
fixed, I can never remember the URL for it, sorry
👍 1
(I’m trying to add a result object to a project I’m on at work, where I’m the Jr Lead of two, because I’m still new to Kotlin. I’m not sure if the other lead would be comfortable adding another library for it. I’m told in another thread that Arrow’s Either will be a tiny package for Kotlin 2, but we’re a few versions behind. Hence why I figured I’d just write my own and try to learn int he process.)
c
we’re a few versions behind
a few versions of what? Are you already using Arrow? Arrow 2.0 is really soon, probably a few weeks
As someone who rolled up their own implementation, I can guarantee you, the cost is much more than importing a lib, because you'll have to reimplement all utilities, and you'll never have a DSL that's as well thought out
l
A few versions behind of Kotlin, Spring Boot, and the JVM. 😅
Would their Result be in the core package?
c
Yes, it's called Either
h
The error is caused by defining extension functions In your Result class. Either move them or remove the receiver.
c
your problem is you're defining extension functions in Result
ah, got beat
😎 1
this is the correct signature:
Copy code
fun <T, E> then(block: () -> T) = when(this) {
        is Result.Success -> Result.of { block() }
        else -> this
    }

    fun <T, E> error(block: () -> T) = when(this) {
        is Result.Error -> Result.fail(block())
        else -> this
    }
(I removed
Result.
from both of them)
l
Ah! I was following an article that had small code snippets, not full examples, so I probably misread the snippets.
Bingo, that fixed it.
c
If you want to keep the code as-is, you can move
then
and
error
to the top-level
Are you coming from Java?
Ah, you said Spring, so I guess so
l
No, my background is mainly PHP. 🙂 My new company is mostly Kotlin-on-Spring.
c
I see
l
(So far I’m finding Kotlin to be mostly fine, but not liking Spring.)
c
So,
Result<>.
means "this takes an implicit parameter of type `Result`". But being in the brackets of the
Result
class
means you have to call it as
somethingOfTypeResult.error
. You end up with requiring result two times which is why the compiler didn't let you call the function (you only provided once)
Most often than not, you will see extension functions (what we call the
Result<>.
syntax) at the top-level of files. There are very useful cases for putting them inside of classes, but those are usually quite advanced so I wouldn't recommend beginners to think about the implications too much
l
Yeah, I don’t know when I’d deliberately put extension functions inside a class. I just got confused by context in this case.
c
I won't give examples for now 🙂 but if you're curious in the future when the simple cases make sense, don't hesitate to ask again
👍 1
l
I’ll see if the other lead is OK with pulling in Arrow.
c
Simon Vergauwen (maintainer of Arrow) is doing a talk in two weeks at KotlinConf, he's very good at giving a simple overview of concepts, I highly recommend it
l
If they call theirs Either, does that cause confusion between Left and Right? (That’s why I usually prefer a more concrete Result.)
c
(it'll be streamed for free on youtube)
👍 1
l
I’ll have to see if I can catch that, thanks.
c
Failure is always on the
Left
, it's really quick to get used to it
If you think about it, when you write
Result<Foo, Unit>
you also have to remember than the failure is on the left when you're reviewing code in GitHub or whatever, so you already know that left is the failure case
l
Except in most Result objects, it’s
Result<Success, Error>
. So error is on the right.
c
Ah? I don't remember seeing one in this order
l
That’s how Rust’s result type works: https://doc.rust-lang.org/std/result/
c
Anyway, worst case:
Copy code
typealias Success = Either.Right
typealias Failure = Either.Left
Oh, I should know this, I've written Rust in the past
l
🤔
I’ve only dabbled in Rust, but I feel like I’d like it if I actually took the time to learn it properly.
c
The point is (but Simon will explain it better if you watch the talk), error handling is much much more than just the monadic type. The real value is in all the utilities and helpers that are around it, and those are what's costly to maintain and why you need a library specifically for it
I really like Rust, but I don't have the need for a low-level language, so… I don't really use it
l
Copy code
/Users/lg9549/.gradle/caches/modules-2/files-2.1/io.arrow-kt/arrow-annotations-jvm/1.2.4/f81ec8df8fe610b774a8a9100060c46f7f028347/arrow-annotations-jvm-1.2.4.jar!/META-INF/arrow-annotations.kotlin_module: Module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.9.0, expected version is 1.7.1.
I read that as “you’re on Kotlin 1.7, Arrow requires 1.9, sucks to be you”. Correct?
c
Yes, looks like it
Though, the Kotlin standard library never makes backwards-incompatible changes, and the Kotlin language rarely does (in 1.6
when
expressions had to be exhaustive so you had to add a bunch of
else
, but I don't remember anything in recent versions), so you should be able to upgrade just Kotlin without touching your other dependencies, if you're worried about upgrading too much stuff at once