Idle thought on Saturday: coming from Haskell, one...
# functional
u
Idle thought on Saturday: coming from Haskell, one thing that I'm missing in Kotlin is the let..in syntax Am I the only one that would like having something like this? (I've called it LETTING for clarity) A ideal constrain would be that the function should be pure, that is no access to external scope, like singletons, objects, public functions, java System. etc. etc.
s
is that an apostrophe in your function name, without backticks?
Or maybe the prime symbol?
u
a unicode symbol, to make it compile in haskell fashion 🙂
U+02C8
s
that seems a little difficult to work with but all right lol
Would you mind expanding upon what, exactly
LETTING
is doing here?
and how it would work as an lhs expression
playing around with the idea a bit in Idea, you could roughly approximate this with a kinda verbose lambda, or more likely a
Pair
or
Triple
-based solution
something like
Copy code
fun pythagoras(a: Double, b: Double): Double =
    (a * a to b * b).let {
      (x, y) -> Math.sqrt(x+y)
    }
but ultimately I feel like it’s kind of an awkward shim for a programming language not geared towards writing code in this style
actually, using an anonymous class, you can do something like this:
Copy code
fun pythagoras(a: Double, b: Double): Double =
    object {
      val x = a * a
      val y = b * b
    }.run {
      Math.sqrt(x + y)
    }
t
I always hated it. What I'd like to have is where syntax like in haskell
u
Where or let syntax is not a big difference to me, what I miss is the guarantee that function is pure and not calling anything else
e
Copy code
fun pythagora(a: Double, b: Double): Double {
    val x = a * a
    val y = b * b
    return Math.sqrt(x + y)
}
What am I missing?
r
@Uberto Barbini in the upcoming version of Arrow Fx the linter can verify function purity and effect tracking if you denote effectual functions as
suspend
which is what the Kotlin community already does for the most part when they use the
coroutines-core
lib. The linter will verify that expressions returning
Unit
that are not inside
suspend
functions fail to compile in the same way today the Kotlin compiler verifies that
suspend
functions can't be applied unless in the presence of a continuation. This is the same concept as Haskell's
IO
. Also this can be implemented with no additional syntax in Kotlin, it already supports all this natively thanks to the core continuation intrinsics being part of the std lib.
coroutines-core
implements the runtime for an effect system that tracks
suspend
like Arrow Fx also does but geared more toward guaranteeing purity at compile time vs async concurrency. //@aballano is going to help us with the linter rules, the rest the Kotlin compiler and @RestrictsSuspension already takes care of. This is often ignored in Kotlin because it does not really have the FP names you'd expect but @RestrictsSuspension already allows you to group effects in different areas and prevent compilation unless you explicitly encode how a foreign suspend function is accepted within a restricted context. If interested more info here https://arrow-kt.io/docs/effects/fx/#pure-functions-side-effects-and-program-execution Additionally if this was interesting to the compiler and the general community you could enable an experimental import that disallows returning
Unit
unless you are inside
suspend
. That is all the compiler is missing today to guarantee pure functions.
u
Thanks @raulraja, I'll look into it
đź‘Ť 1
@elizarov you are not missing anything, but the problem is that you can write any code in that pythagora fun, calling db, asking google etc.
Now one nice thing in Haskell (there are many not so nice) is that you are sure that functions just compute on input, and you can compose on them safely.
Frankly as much as I admire Arrow cunning efforts 🙂 to port monads and such in Kotlin, I don't miss them in my day-to-day work.
Instead I miss that sense of safety in writing and calling functions without side-effects.
Note that I still prefer Kotlin as general purpose language over Haskell (to say nothing of Java) but that is the feature that I miss most from Haskell.
e
@Uberto Barbini
but the problem is that you can write any code in that pythagora fun, calling db, asking google etc.
Can you, please, clarify how that problem is related to the “letting” syntax?
val x = ... ; val y = ... ; return ...
is just the Kotlin syntax for
let x = ... in let y = ... in ...
construct and the choice of syntax is completely orthogonal to the purity of your functions. Kotlin is not a pure language and will never become one (due to JVM interop goals) and so any Kotlin syntactic construct allows you to call both pure and non-pure functions. So, programming with pure functions is a style issue in Kotlin — it is something you choose to do to improve your code, not something that is being enforced.
t
I think the issue here is declarative vs imperative and not purity
đź‘Ť 1
e
There is nothing imperative in
val x = ... ; val y = ... ; return ...
. It is a sequence of declarations, one building on another. Is it a
val
name that confuses people? You can call it
let
(like in Swift) when you read it, if it makes it more declarative for you
To make sure we are on the same page with respect to the terms we use, here is what Wikipedia says (and I agree):
In computer science, imperative programming is a programming paradigm that uses statements that change a program’s state.
If you use pure functions there is no changing state, so you cannot be imperative
r
You can achieve imperative syntax with pure functions.
suspend
+ function application is still pure because
suspend
can't run without the continuation.
(suspend() -> A).invoke
is the same as
IO.flatMap
but not stack safe.
!
here is the same as
()
but stack safe.
Copy code
val x: IO<Int> = fx {
  val n = !IO { 1 }
  val y = !IO { 1 }
  x + y
}
Copy code
val x : IO<Int> = IO { 1 }.flatMap { n ->  IO { n + 1 } }
both versions are suspending the same value.
!
is conceptually the imperative version of
flatMap
where the rest of the program down below gets nested
This is why in Kotlin if you are working with effectful programs like the ones we write in coroutines we don't need to use
just
,
map
or
flatMap
all those are gonne if you have the ability to non blocking go from
F<A> -> A
This is more flexible than haskell because kotlin is not restricted to do this in special
do
blocks. In Kotlin you can have do notation at any level a la carte as long as you are in suspended continuation because you can always collapse the wrapper and extract the value so there is no need to work in a wrapped style with
flatMap
etc.
This is why I believe Kotlin is more approachable to beginners than other langs. In Kotlin you can create entire pure programs with imperative syntax which most java devs already know thanks to Coroutines.
The only thing preventing us to track purity is that Kotlin allows function that produce effects outside
suspend
. For example
println
or anything that returns
Unit
or calls something that returns
Unit
e
Kotlin is not a pure programming language. Kotlin function’s return type (unit/non-unit) is not related to the purity of the corresponding function or expression. It is considered good style in Kotlin to write mostly pure functions but it is not enforced in any way — it is up to developer to follow this practice.
r
Yes, we are not proposing this changes. The idea for the language proposal is that we enable that tracking locally with a compiler flag or experimental import feature because Kotlin is already doing side effect tracking with suspend. In whatever case my point is that imperative is unrelated to functions being pure because this is already pure:
Copy code
suspend fun save(): Unit {
  doSave()
  etc()
}
Pure just means that when invoked it does not compile unless in IO or produces no effects. That is exactly what suspend does. It does not compile unless you have a continuation. We can't call that function directly on main or other places that are not suspended, that makes it pure and therefore is as valid as callback chains or wrappers for FP. FP style is fine with imperative style, what I believe is frowned upon is calling effects when the function ir not suspended or the datatypes like IO does not delay evaluation once it wraps the effect it is supposed to control.
u
@elizarov as for my concern: I agree that Kotlin is not a pure functional language and it shouldn't be one. My experience with Haskell is that forcing Functional everywhere creates as many problems as it solves, so I think the future will be to hybrid languages. Still I would find useful to mark pure functions, or bit of functions, as such. In the same way that you mark a suspendable function in Kotlin or a synchronized function in Java. The reasoning is kind of similar to the one about making a sealed class: you are protected from mis-use of your code. Since the compiler can verify that only pure calls are done inside a pure function, you can safely use some library calls knowing that you are not risking side-effects (where 90% of bugs stay). As for a silly example, the notorious SimpleDataFormat in Java is not thread safe because it is not immutable and there are several class in the jdk with similar problems. It would be nice for the compiler to verify if a method is pure/immutable/safe to use. Of course this is only my opinion, but for what it's worth I don't think it would betray the nature of the language having such a construct. Btw Haskell can be as imperative as you want with the do syntax so the issue is definitely not imperative vs declarative but pure vs impure.