It’s a follow-up to the @diesalbla topic PRE-SIP: Comprehensive Function Applications. and a set of discussions arrounf PRE-SIP: Suspended functions and continuations - #135 by MateuszKowalewski and Pre SIP: `for` with Control Flow (an alternative to Scala Async, Scala Continuations and Scala Virtualized)
Next sequence of steps/language changes are possible:
Step1 /SIP NN-1: allow \leftarrow to be used outside for loop:
The definition should have next form:
def <- [F[_],A, …U_i ](a:A)(using …u_j): A
where U_i
, u_j
– optional additional type parameters and given arguments.
The expression: val a <- b
is the syntax sugar for val a = <-(b)
. and a <- b
— for a = <-(b)
.
By conventions, ← should mean extracting from F[_], and another usage should be forbidden.
This can be implemented now with relatively low effort.
The drawback is that developers can ignore conventions and use ← for other purposes. Introducing language import can mitigate this.
Step2 /SIP NN-2: assume that ← is defined in the lexical scope of for loop with the usual semantics of [await/reflect/unlift].
This will allow expressions like:
for
x ← fetchData
c <- isFine(x)
y <- if (c) then
val defatils <- fetchDetails(x)
pure (details + localDetails(x))
else
failure RuntimeException(s"$x is not fine")
yield y
This will require implementing cps-transform inside expressions in for loop. The area is relatively well-known, for scala3 exists at least two user-level implementations.
Step 3/SIP NN-3: Make val
keyword optional when mutable variables are absent in the current scope.
(motivation: why we need val
at all (?) – to prevent mixing value definition and assignments. If we have no mutable values, we have no assignments.)
This will allow:
for
x ← fetchData
c <- isFine(x)
y <- if (c) then
defatils <- fetchDetails(x)
pure (details + localDetails(x))
else
failure RuntimeException(s"$x is not fine")
yield y
This will give us better-then-haskell for for functional effect lowers.
Step 4/SIP NN-4: Link ← with the capabilities and merge direct and monadic-dsl styles.
Let us have the capability ‘cps’ or ‘suspend’ and provide a way to define ← in terms of that capability for library authors. In this way, we can solve the coloring problem:
def doSomething[F[_]](input:Inout)(using ml:MonadLift[F]): { ml } Output =
data <- f(input)
…
Or even:
def doSomething[F[_]:MonadLift](input: Input): { suspend } Output =
data <- f(input)
….
where suspend
is some global capacity.
It can be used the same as the other form without capacity:
def doSomethingM[F[_]:MonadLift](input: Input): F[Output] = async[F]{
data <- f(input)
….
}
The capability elimination step can be runtime or compile-time, depending on monads and runtime properties.
For compile-time, we should have a compiler pass that selective does cps transformation when an appropriative capacity is used.
And now direct and monadic styles are the same, for
become a special kind of async/reify/lift
with restrictions for children’s expressions.
Of course, the last is a very broad description connected to an open research theme with many unknowns, but I think the idea is understandable.