What about if you wanted an Either for something t...
# arrow
s
What about if you wanted an Either for something that isn’t really suspendable, like say parsing a string to an int? In that case Either.catch wouldn’t work ?
p
if your code can throw then it side-effects, so it’s impure and has to be wrapped on suspension
that’s our assurance
as a library
you can write a non-suspend version, the same way you can write a getLeft that throws on Right
s
Are you leaving it as an exercise to the reader to right the non-suspend versions ?
p
but it won’t be on the library
no, we had them and decided to remove them. We’re betting on keeping everything as pure as possible on the library
❤️ 1
s
We’ve had this discussion before. I think it means tons of users won’t be able to commit to using a pure only library. You don’t even see that many pure-only apps in Scala. What about an add on module: arrow-impure then ?
p
like alley-cats in Scala
s
yeah kind of
although that’s for non lawful stuff
p
Either.catch isn’t lawful
hahah
s
lol
p
non-suspend I mean
s
is purity a law 😉
p
a test
that you have to pass to be in the library
😄
s
But my personal opinion is loads of people won’t be able to fully commit to purity everywhere, so offering another module is really important
Unless of course your aim is to be the pure-only library. Personally I think it’s a win if everyone can do a bit of FP even if they’re not doing 100% FP
p
I don’t think we’ve opposed it if someone owned it, even if it was in the repo. We don’t want to own it ourselves.
r
I like the idea of the library maintaining purity. It’s easy enough to write little extensions that allow for impurity.
❤️ 2
s
I agree it’s easy
I have tons of extensions for arrow already for cases that aren’t covered by default.
r
For example, I don’t think this should be in the core, but it was useful for a project I was working in that used
Either
all over the place and didn’t make proper use of IO
Copy code
/**
 * Execute some effect like logging
 */
inline fun <L, R> Either<L, R>.effectLeft(f: (L) -> Unit): Either<L, R> =
    when(this) {
        is Either.Right -> this
        is Either.Left -> {
            f(this.a)
            this
        }
    }

/**
 * Execute some effect like logging
 */
inline fun <L, R> Either<L, R>.effectRight(f: (R) -> Unit): Either<L, R> =
    when(this) {
        is Either.Right -> {
            f(this.b)
            this
        }
        is Either.Left -> this
    }
s
IO is such a contentious concept. In Scala people can’t decide between IO or ZIO, so now we’re abstracting the effect abstraction. I will maintain that Jetbrains are promoting coroutines so heavily that it will be important to have a FP library that doesn’t use effects. It will limit the audience of Arrow if its aim is to be pure only. That’s my 2p.
r
I mean, I think there will still be tons of people that find Arrow useful for certain abstractions that don’t want to commit to 100% FP. Those that do want to commit to 100% FP probably value purity.
s
Yes exactly.
p
I’ve been working on JS/PHP (Hack)/Python this past couple of years and having all the effects under async/suspend isn’t that bad
fx
here are the building blocks on top of it
for parallelism, cancellation, races
s
There are people who value 100% purity, and that’s great. Then there’s people who don’t (which I would argue are the majority, rightly or wrong), and it would be a real shame if they weren’t served by what is, without doubt, the best library in the kotlin ecosystem.
Maybe 2nd best after KotlinTest 😉
p
KotlinTest is generally more useful, regardless of your choice of paradigm, so I don’t disagree there ❤️
there was some ad-hoc thing they had in Scala (or Maybe Haskell) around what goes in a library that’s not core, basic stuff
s
I was joking, I think arrow is more useful than kotlintest. KotlinTest makes things much easier, but you could use Junit. Without arrow I’d have to go and write my own arrow.
But anyway, consider arrow-impure please begging
r
I see Arrow’s job as educating people on purity and FP rather than compromising in order to meet the masses in the middle.
arrow 1
👍🏻 1
p
and basically any implementation that’s not self-obvious at first sight. For example, that effectLeft is easily implemented as mapLeft
👍 1
But anyway, consider arrow-impure please begging
Considered it is 😄 VVVV
I don’t think we’ve opposed it if someone owned it, even if it was in the repo. We don’t want to own it ourselves.
s
lolol
d
In my opinion, purity is a commitment. You don't get much benefit from partially applying it.
s
I’m saying that you still improve your code base by using Eithers, Trys, Validateds, Options etc, even if you’re still using coroutines or futures. It’s not all or nothing.
d
Right, but those facilities are tools that make purity _practical_; they're not "functional" on their own.
Rather they come out of decades of research figuring out how you take functional purity and make it easy to use. And they happen to be useful in non-FP contexts too.
s
Sure, I’m not disagreeing. I’m just trying to show that Arrow will be useful outside of people who want purity in their apps.
d
So what's stopping you from grabbing just the data classes and writing your code how you want?
s
You mean making my own copy of Arrow ?
d
Nah, just putting
arrow-data
in your
build.gradle
s
Nothing, I am pushing arrow extensively at my current employment
However if in 0.10 or whatever, Either becomes suspendable in things like Either.catch, it reduces the usefulness to people not writing in a pure wa.y
d
There's nothing wrong with putting your effects in
suspend
functions
Regardless of purity constraints
s
there’s everything wrong with it, if you don’t want to go down the path of a pure app
d
it's a useful delineation and helps you make guarantees
s
let me tell my developers: you want to parse a string into an int - make the entire call stack suspend.
Like I said, I’m not arguing against a pure paradigm, or the benefits of it, but it’s impractical unless the entire stack will be built that way, with people who are skilled enough to write code in that style.
d
But purity isn't some lofty ideal accessible only to Senior Devs And Above, it's a demonstrably safer paradigm with measurable benefits
s
Oh come on
Many developers still struggle with how not to “test the mock”.
d
OOP is a broken paradigm, what's your point?
r
🍿
🍿 1
s
My point is you’re dismissing the complexity of pure fp code.
The complexity of learning how to use it.
If you think it’s trivial to pop over to Scala-land and knock out a ZIO based app using no side effects, then you’re a smarter man than most people I know.
d
I'm not talking about learning category theory and exploiting typeclasses. Look, here's "pure" fizzbuzz.
Copy code
fun main() {
    generateSequence(1) { it+1 }
        .map { n ->
            when {
                n % 15 == 0 -> "FizzBuzz"
                n % 5 == 0 -> "Buzz"
                n % 3 == 0 -> "Fizz"
                else -> n.toString()
            }
        }
        .take(100)
        .forEach { msg -> println(msg) }
}
No arcane types, no Ph.D in Computer Science, all I did was move the
println
to the edge of the program
s
great a toy example
can I have a log statement in the middle of my http request function ?
d
honestly yes. Unless you try and create a feedback loop based on your logs, then logs are a guaranteed black hole
s
can i send out some metrics to data dog ?
d
Are the metrics coming back into your program in any way?
s
nope
Lots of people would say it’s still impure though
d
Then I say yes. I believe you don't need to track "Black hole effects"
s
Anyway I’m not going to get into an argument of pure vs impure code because I don’t disagree with you. What I do disagree with you on is that it’s easy for people to pick up this style.
d
But marking something
suspend
as a delineator of purity isn't massively complex
especially considering you have
suspend fun
at your disposal
s
What’s complex is explaining why everything that calls it must be suspend, and why, from their spring mvc handler, they can’t invoke the function at all
There is a learning curve for people coming from a spring-java background that must be appreciated.
d
Ah, so the issue is integrating it with an existing framework
s
The issue is, in general, you can’t just wash away these concerns as trivial. That’s all.
d
Spring MVC endpoints don't support
Mono
?
s
what is mono ?
d
It's in the Java stdlib
s
not written Java in 10 years.
d
Yeah but kotlin still uses the Java stdlib
s
Can’t see it on the classpath
d
It's the jvm's version of async
Hang on...
I thought project reactor made it into the stdlib, I guess I'm wrong.
No, it's CompletableFuture, that's right
s
That’s about as impure as you can get
I'm saying a suspend function can be turned into a
CompletableFuture
in a 4-liner
With the
kotlinx.coroutines.jdk8
library
s
the number of lines isn’t the issue
explaining why and when to do it is
d
1. "put your effects in a suspend function" 2. "this is how you give suspend functions to Spring"
s
I see you’re firmly in the camp of “everyone can learn pure fp it’s dead easy”. I don’t agree with you, I’m clearly not as smart as you, maybe one day I’ll get there 🙂
d
🙄
No, what I'm saying is
suspend
is not something to fear
s
I know that.
d
and using it as a delineation of pure/impure is not that huge a leap
s
What are we even arguing about here.
d
I'm not sure
I missed lunch 😂
I think a request handler should be
suspend
s
ktor can support suspendable functions for handlers
spring doesn’t (currently)
d
Well so see above, you can convert a suspendable function to a function that returns a CompletableFuture
And back again
🤷
It's friction having to call an adapter, but Spring is pretty dedicated to Kotlin support so I wouldn't be surprised if it showed up sooner or later
s
👍🏻
d
To your point, I'm thinking this is not too awful:
try { s.toInt().right() } catch(e: NumberFormatException) { NotANumber(s,e.message).left() }
Then again I'm not grasping why that must be considered effectful. If I call that a million times with the same string it's going to return the same Either
s
🙂
d
So maybe we agree 😂
s
lol
That’s why I picked that example
It’s not really effectful as you say
But you can do that inside Either.catch, but I can’t call Either.catch from outside an effect.
Anyway, we’ll see where it goes, it might not be too bad if it’s just the odd thing that’s effectful and it can be easily worked around with a few ext functions.
d
I agree that pure functions shouldn't throw... but I think that's different from a pure function that catches a deterministic exception and converts it to a result type before returning
Especially if you're running on the JVM
s
Right, I’m not advocating allowing Either.catch { file.read } or something
d
But maybe the problem is there's no good way to statically guarantee that the thing the developer is trying to catch is deterministic?
s
I guess not. Let the developer decide?
d
That's how we get Python
🙈 1
🧌
s
My company is 50% python.
d
Mine is more... I hate it 😂
s
lol
I don’t like the language either.
But this is why I keep saying things that seem simple to someone versed in it, are not that simple to people who don’t even work in languages with types or threads. That’s not to dismiss them, just to understand that they need more gradual steps to grasp things.
My big push at the moment has been functional error handling, which I believe is orthogonal to purity, and so having things like Either’s and Try’s not be available in a side-effecting app would be a real blow.
I hope that makes more sense where I’m coming from.
d
So - the complexities of coroutines are around the scheduling aspect. The CoroutineContext and CoroutineScope crap.
That really takes some work to understand.
But!
Because Spring provides its own scheduler for CompletableFuture, you can just convert
suspend
to one of those and let Spring do it
suspend
itself is a pretty simple concept - don't call
suspend
from non-
suspend
s
I’m saying something even more basic - functional error handling ideally would work without effects at all (and does at the moment). No threads, no coroutines, that’s for the future (no pun intended)
d
Which is isomorphic to "don't call
impure
from `pure`"
(Really gotta stop skipping lunch)
I think that's true for deterministic errors.
s
What’s better:
fun connect(): Either<DatabaseError, Connection>()
or
fun connect(): Connection // throws exception
(or pick another example along those lines). The answer can’t be: neither, because really what you should have is
fun connect(): IO<Connection>
<-- that will come later as skills increase.
d
The former, for sure. But
suspend fun connect()
gives you an extra layer of protection. Some idiot can't go into (non-suspending)
fun Employee.isPartTime()
and call
connect()
s
Sure, the suspend one is better. 100% agreement between us.
d
Yeah, I'm not even thinking of introducing
IO
here
s
well suspend or io are similar
both effects
so given that I can’t introduce coroutines, nor IO, you agree the better option is to just use Either.
d
Given those constraints it's still good to use an explicit type.
s
exactly
And that’s all I’m saying.
d
But in that case I'm not against writing my own factory
s
a factory to generate eithers from effectful functions ?
d
Basically yeah.
s
If it comes down to it, that’s what I’ll do. I’m just advocating that I think a lot of people will end up doing it, so there’s a market for us.
d
In this case I think it's probably wise for arrow to be opinionated.
s
If we go back to the top of this thread, and the reply is, the library is focusing on pure only, that’s fine. I’ll just have to make these factories. I was just hoping to explain why that’s not what I would do, whilst appreciating it’s not for me to decide where the library is going.
The arrow boys and girls put a lot of work into it, and even if I can ultimately only use 10% of it, I will use that 10% and be very grateful.
d
Hell, I've got a good idea. One second, coding.
s
Well I need to bow out for now, as I have actual work to finish 😂
d
Copy code
object Unsafe {
    inline fun <E, reified X: Throwable, T> attempt(handle: (X) -> E, action: () -> T) =
        try {
            action().left()
        } catch(e: Throwable) {
            if (e is X) {
                handle(e.right())
            } else {
                throw e
            }
        }
}

fun <E,T> unsafe(action: Unsafe.() -> Either<E,T>): Either<E,T> =
    Unsafe.action()

fun main() {
    val str = "a"
    val result = unsafe {
        attempt({e: NumberFormatException -> "Not a number: $str"}) {
            str.toInt()
        }
    }
    println(result)
}
Kotlin builders to the rescue!!
That way you can't just go around attempting potentially breaky stuff without marking it "unsafe"
But you still have an escape hatch
s
I like the naming there with unsafe, still have to write it myself though, which is easy, but the whole point of what I was saying 🙂
I’ll maintain the arrow-impure module in a separate repo for idiots like me 😁
It would be cool actually if you could have a compiler warning like for experimental
d
arrow-escape-hatch 😄
😂 1
s
then whenever you do Either.catch { throwing function here } you have to do @AllowUnsafe
d
The arrow guys are doing a lot of good research into compiler plugins. A warning might be possible with minimal effort. See #arrow-meta
s
I don’t think they want me in that room.
d
lol
s
I already go on enough in here 🙂
d
But I will point out that propagating
@AllowUnsafe
up your call stack is pretty similar to propagating
suspend
s
Only in the one place you do it
That’s what I meant
d
So a true escape hatch
s
yes
Just another alternative of your unsafe function
But you did agree that “qwe”.toInt() is not an effect
yet it will throw
d
I see how throwing functions are impure.
Yet I don't see how a function that catches a thrown deterministic error (e.g. from a parsing operation that has no side-effects) and turns the result into an Either is impure.
I'm willing to be educated.
s
That makes two of us.
d
My guess is there's no good compiler-level constraint to tell the difference between
"abc".toInt()
and
db.connect()
s
“Abc”.toIntOpt() I guess solves it
d
Well it's 5:45 my time, I best be getting home
s
Same.
p
I’m still in 1h, just want to say “Right, I’m not advocating allowing Either.catch { file.read } or something” <--- that is actually how we intend to use it
suspend already gives you purity, and it doesn’t allow thread jumps or cancellation. You need IO for that
s
Yeah I know, sorry I was meaning in the context of a non suspend version of it
p
so use side-effects and Either for anything that’s simple
gotcha
back to 45 minutes ago in the conversation
s
haha.
p
my real superpower is that I’m a fast reader
anyway, in our vision I believe there are two isomorphic options
suspend fun connect(): Either<DatabaseError, Connection>() fun connect(): IO<DatabaseError, Connection>
s
Yep, that’s what I guess you’re doing.
p
they’re one and the same, IO.effect { connect() } and async { connect().suspended() }
s
I was saying that they’re the same, and that the middle way while people become more functional, is just
fun connect(): Either<DatabaseError, Connection>()
Ie, can I use functional error handling without needing to be aware of what “Effects” are
a gateway drug
p
Agreed. What I’ve found regardless of the language is that at some point you’ll need to go into the async world and then you have to pick your IO/Deferred/CompletableFuture choice. And once you’re there, tracking effects is easier than not doing it 😄 But yeah, step by step.
s
step by step is all I am saying in these long rants
Part of my job is to introduce more functional programming to the developers around me, very capable developers who are learning fast. But I can’t go whole hog with effects day 1.
Right I’m done with this thread now, must be a record even for me.