Perchance I'm wanting to do things that are not in...
# language-evolution
m
Perchance I'm wanting to do things that are not intended for context receivers, so maybe someone can help me with what I want to do: Question in 🧵
Let's say I have an interface called "Incrementable" defined like so:
Copy code
interface Incrementable<T> {
    fun T.increment(): T
}
Now I can define a class like so:
Copy code
context(Incrementable<T>)
class Interval<T>(
    val toIncluding: T
) : Interval<T> {
    val to: T = toIncluding.increment()
}
and then, using a custom object I can do something like this:
Copy code
object IncrementableLocalDate : Incrementable<LocalDate> {
	fun LocalDate.increment() = plusDays(1)
	
	infix fun LocalDate.toIncluding(other: T) = Interval(this, other)
}
this is all fine and dandy for extending usage for existing types, since I don't own LocalDates and typing
date toIncluding otherDate
will prompt an import specifically for the IncrementableLocalDate class.
But now, I want to use this interval class for types I do own:
Copy code
class MinuteTime(time: LocalDateTime) : Incrementable<MinuteTime> {
    val time = time.truncatedTo(ChronoUnit.MINUTES)
    override fun MinuteTime.increment() = MinuteTime(time.plusMinutes(1))
}
Sadly, the class existing and spefically creating a context for incrementable, doesn't work for calling the constructor of Interval:
Copy code
val minuteTime = MinuteTime(LocalDateTime.now())

    Interval(minuteTime) // No required context receiver found: Incrementable<T>: Ctx { class Interval }
So my question now becomes; how do I fix this so it works for both owned and unowned types?
y
You could simply have another fake constructor like so:
Copy code
fun <T: Incrementable<T>> Interval(toIncluding: T) = with(toIncluding) { Interval(toIncluding) }
m
I know that is a possibility, but it 'feels' wrong that in order to work with context receivers this way, you are cheated out of the 'normal' method. MinuteTime provides the context of being an incrementable by itself. So it feels like things like:
with(x){ x }
should be implicitly possible
y
Because contexts are fundementally different from just a typeclass basically. They're standalone objects that just so happen to be able to emulate that typeclass-like behaviour.
T: Incrementable<T>
and
context(IncrementableT>)
are fundementally different. One easy thing you can at least do is have an
object MinuteTimeIncrementable: Incrementable<MinuteTime>
and at the start of your calls do
with(MinuteTimeIncrementable)
d
I always think of contexts as secret hidden parameters which you can refer to by using 'this' and which are passed along anything that is called from that function.