Add receiver-stack function types

This commit is contained in:
sergeych committed 2026-04-29 12:59:24 +03:00
1 parent 1bababa058
commit e107296bca
11 files changed
+415 -46

No files matched your search

+25
View File
@@ -454,6 +454,31 @@ Key rules and features:
- For arbitrary receivers, use casts: `(expr as Type).member(...)` or `(expr as? Type)?.member(...)`.
- Qualified access does not relax visibility.
### Receiver-stack lambdas
Qualified `this@Type` is also used outside inheritance when a lambda has multiple visible receivers.
This is common in DSL-style builders.
- `A & B` means one receiver value that implements both types.
- `context(A, B) C.()->R` means a receiver stack:
- primary `this` is `C`
- outer/context receivers are `A`, then `B`
- Unqualified lookup checks the primary receiver first.
- If the primary receiver does not define a member and several outer/context receivers do, Lyng reports a compile-time ambiguity. Use `this@Type` to select one explicitly.
Example:
```lyng
class Html { fun title() = "html" }
class Head { fun title() = "head" }
class Body
val block: context(Html, Head) Body.()->String = {
// title() // compile-time ambiguity: Html vs Head
this@Html.title()
}
```
- Field inheritance (`val`/`var`) and collisions
- Instance storage is kept per declaring class, internally disambiguated; unqualified read/write resolves to the first match in the resolution order (leftmost base).
- Qualified read/write (via `this@Type` or casts) targets the chosen ancestor’s storage.
+6
View File
@@ -164,7 +164,12 @@ Primary sources used: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/{Parser,T
- unions `A | B`
- intersections `A & B`
- function types `(A, B)->R` and receiver form `Receiver.(A)->R`
- receiver-stack function types via `context(A, B) Receiver.(P)->R`
- variadics in function type via ellipsis (`T...`)
- `A & B` means one value implementing both types.
- `context(A, B) Receiver.(P)->R` is different: it declares an ordered implicit-receiver stack where `Receiver` is primary `this`, then `A`, then `B`.
- Nested receiver lambdas keep outer receivers in scope; unqualified lookup prefers the innermost receiver, and `this@Type` can select an outer/context receiver explicitly.
- If the primary receiver does not provide a member and multiple outer/context receivers do, the lookup is a compile-time ambiguity and must be disambiguated with `this@Type`.
- Generics:
- type params on classes/functions/type aliases
- bounds via `:` with union/intersection expressions
@@ -217,6 +222,7 @@ Primary sources used: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/{Parser,T
- Disambiguation helpers are supported:
- qualified this: `this@Base.member()`
- cast view: `(obj as Base).member()`
- In nested receiver lambdas, `this@Type` can target any receiver visible through the receiver stack, not just inheritance ancestors.
- On unknown receiver types, compiler allows only Object-safe members:
- `toString`, `toInspectString`, `let`, `also`, `apply`, `run`
- Other members require known receiver type or explicit cast.
+34
View File
@@ -352,6 +352,40 @@ Sets `this` to the first argument and executes the block. Returns the value retu
assertEquals(3, sum)
>>> void
Receiver lambdas can also keep outer receivers in scope. The primary receiver wins for unqualified lookup, and `this@Type`
selects an outer receiver explicitly:
class Html { fun lang() = "en" }
class Body { fun lang() = "body" }
fun html(block: Html.()->String) = with(Html()) { block(this) }
fun body(block: Body.()->String) = with(Body()) { block(this) }
val result = html {
body {
lang() + ":" + this@Html.lang()
}
}
assertEquals("body:en", result)
>>> void
You can declare the same requirement in a function type:
val block: context(Html) Body.()->String = {
lang() + ":" + this@Html.lang()
}
If the primary receiver does not define a member and multiple outer/context receivers do, Lyng reports an ambiguity instead of picking one silently:
class A { fun title() = "a" }
class B { fun title() = "b" }
class C
val block: context(A, B) C.()->String = {
// title() // compile-time ambiguity
this@A.title()
}
## run
Executes a block after it returning the value passed by the block. for example, can be used with elvis operator: