Will Arrow implement Typeclasses? or does that hav...
# arrow
k
Will Arrow implement Typeclasses? or does that have to come from the language?
s
Hey @Kristian Nedrevold, Kotlin has Context Receivers on the roadmap, and you can already try them on the JVM experimentally. Not sure if you've looked into them. They'll allow for constraint based programming as you would do with typeclasses. The typical example
Copy code
context(Monoid<A>)
fun List<A>.combineAll(): A =
  fold(empty()) { acc, a ->
    acc.combine(a)
  }
The part that most people then still miss is the "injection" piece, which is not part of typeclasses perse and won't be part of Kotlin.
listOf(1, 2, 3).combineAll()
won't compile, and you will have to do.
Copy code
with(IntMonoid) {
  listOf(1, 2, 3).combineAll()
}
There are some ongoing experiments to solve this piece in a compiler plugin within the Arrow organisation, but compiler plugins won't be stable until Kotlin 1.9.
I think context receivers solve most of our problems in Kotlin, and the requirement to make them explicit at the edge is no big issue. Since you can implement
with
for multiple arguments.
Copy code
interface Service
interface Persistence
object Error
object User

context(
  Service,
  Persistence,
  Raise<Error>
)
suspend fun fetch(): User {
  // service, persistence available
  // suspend (IO) available
  // Can raise typed errors of Error
  return User
}

fun main() = runBlocking {
  with(service(), persistence()) {
    either {
      fetch()
    }.let(println) // Right(User)
  }
}
k
This seems incredibly useful. I have looked into context receivers some, but have not had the chance to use it really. Thanks for the detailed reply! 🙂
e
compiler plugins won't be stable until Kotlin 1.9.
Looks like later than that, even. https://kotlinlang.slack.com/archives/C7L3JB43G/p1668103693929499
k
Copy code
interface Shape<T> {
    fun perimeter(shape: T): Double
    fun area(shape: T): Double
}
context(Shape<T>)
fun <T>prettyPrint(shape : T) = "Perimeter: ${perimeter(shape)} Area: ${area(shape)})})}"

object SquareShape : Shape<Square> {
    override fun perimeter(shape: Square): Double = shape.side * 4.0
    override fun area(shape: Square): Double = shape.side * shape.side.toDouble()
}

object CircleShape : Shape<Circle> {
    override fun perimeter(shape: Circle): Double = 2 * Math.PI * shape.radius
    override fun area(shape: Circle): Double = Math.PI * shape.radius * shape.radius
}


data class Square(val side: Int)
data class Rectangle(val width: Int, val height: Int)
data class Cube(val side: Int)
data class Cuboid(val width: Int, val height: Int, val depth: Int)
data class Circle(val radius: Int)


fun main() {
    
    val square = Square(10)
    val circle = Circle(10)
    val rectangle = Rectangle(10, 20)
   
   
    with(SquareShape) {
        println(prettyPrint(square))
    }

    with(CircleShape) {
        println(prettyPrint(circle))
    } 
    
    println(prettyPrint(rectangle)) // Will not compile until we add a RectangleShape context
}
So this would be a way to use context right?
Am I understanding things right by saying that
Copy code
interface Shape<T> {
    fun perimeter(shape: T): Double
    fun area(shape: T): Double
}
context(Shape<T>)
fun <T>prettyPrint(shape : T) = "Perimeter: ${perimeter(shape)} Area: ${area(shape)})})}"
is the type class equivalent in this case?
And I can then assume that the envisioned compiler plugin would remove the need to add with(context) in the function call. So it would basically be like Scala implicits?
And this would also work on data classes I don't own right?
@simon.vergauwen How would you implement with for multiple receivers, I am struggling a bit here.
Copy code
@OptIn(ExperimentalContracts::class)
inline fun <A, B, R> with(receiver1: A, receiver2: B, block: A.(B) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver1.block(receiver2)
}
but that captures receiver2 so it doesn't work.
s
It currently requires a work around. https://github.com/nomisRev/ktor-arrow-example/pull/35/files/e5dc95af7862872f90a9d14ca5edafb4d6f8b1ab#diff-10b44990006bfee7[…]babc096a714a79f6b48R15
Copy code
inline fun <A, B, R> with(a: A, b: B, block: context(A, B) (TypePlacedHolder<B>) -> R): R {
     contract { callsInPlace(block, EXACTLY_ONCE) }
     return block(a, b, TypePlacedHolder)
 }


 sealed interface TypePlacedHolder<out A> {
     companion object : TypePlacedHolder<Nothing>
 }
k
Thanks a lot 🙂