Updated documentation to clarify property syntax, closure resolution rules, and override usage
This commit is contained in:
parent
91e6ae29ce
commit
7ab439d949
@ -23,11 +23,11 @@ There are a lo of ways to construct a buffer:
|
|||||||
assertEquals( 5, Buffer("hello").size )
|
assertEquals( 5, Buffer("hello").size )
|
||||||
|
|
||||||
// from bytes, e.g. integers in range 0..255
|
// from bytes, e.g. integers in range 0..255
|
||||||
assertEquals( 255, Buffer(1,2,3,255).last() )
|
assertEquals( 255, Buffer(1,2,3,255).last )
|
||||||
|
|
||||||
// from whatever iterable that produces bytes, e.g.
|
// from whatever iterable that produces bytes, e.g.
|
||||||
// integers in 0..255 range:
|
// integers in 0..255 range:
|
||||||
assertEquals( 129, Buffer([1,2,129]).last() )
|
assertEquals( 129, Buffer([1,2,129]).last )
|
||||||
|
|
||||||
// Empty buffer of fixed size:
|
// Empty buffer of fixed size:
|
||||||
assertEquals(100, Buffer(100).size)
|
assertEquals(100, Buffer(100).size)
|
||||||
|
|||||||
24
docs/OOP.md
24
docs/OOP.md
@ -119,8 +119,9 @@ Properties allow you to define member accessors that look like fields but execut
|
|||||||
|
|
||||||
### Basic Syntax
|
### Basic Syntax
|
||||||
|
|
||||||
Properties are declared using `val` (read-only) or `var` (read-write) followed by a name and `get()`/`set()` blocks:
|
Properties are declared using `val` (read-only) or `var` (read-write) followed by a name and a `get` (and optionally `set`) accessor. Unlike fields, properties do not have automatic storage and must compute their values or delegate to other members.
|
||||||
|
|
||||||
|
The standard syntax uses parentheses:
|
||||||
```lyng
|
```lyng
|
||||||
class Person(private var _age: Int) {
|
class Person(private var _age: Int) {
|
||||||
// Read-only property
|
// Read-only property
|
||||||
@ -136,21 +137,17 @@ class Person(private var _age: Int) {
|
|||||||
if (value >= 0) _age = value
|
if (value >= 0) _age = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val p = Person(15)
|
|
||||||
assertEquals("Minor", p.ageCategory)
|
|
||||||
p.age = 20
|
|
||||||
assertEquals("Adult", p.ageCategory)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Laconic Expression Shorthand
|
### Laconic Syntax (Optional Parentheses)
|
||||||
|
|
||||||
For simple accessors and methods, you can use the `=` shorthand for a more elegant and laconic form:
|
For even cleaner code, you can omit the parentheses for `get` and `set`. This is especially useful for simple expression shorthand:
|
||||||
|
|
||||||
```lyng
|
```lyng
|
||||||
class Circle(val radius: Real) {
|
class Circle(val radius: Real) {
|
||||||
val area get() = π * radius * radius
|
// Laconic expression shorthand
|
||||||
val circumference get() = 2 * π * radius
|
val area get = π * radius * radius
|
||||||
|
val circumference get = 2 * π * radius
|
||||||
|
|
||||||
fun diameter() = radius * 2
|
fun diameter() = radius * 2
|
||||||
}
|
}
|
||||||
@ -159,15 +156,16 @@ fun median(a, b) = (a + b) / 2
|
|||||||
|
|
||||||
class Counter {
|
class Counter {
|
||||||
private var _count = 0
|
private var _count = 0
|
||||||
var count get() = _count set(v) = _count = v
|
var count get = _count set(v) = _count = v
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Key Rules
|
### Key Rules
|
||||||
|
|
||||||
- **`val` properties** must have a `get()` accessor and cannot have a `set()`.
|
- **`val` properties** must have a `get` accessor (with or without parentheses) and cannot have a `set`.
|
||||||
- **`var` properties** must have both `get()` and `set()` accessors.
|
- **`var` properties** must have both `get` and `set` accessors.
|
||||||
- **Functions and methods** can use the `=` shorthand to return the result of a single expression.
|
- **Functions and methods** can use the `=` shorthand to return the result of a single expression.
|
||||||
|
- **`override` is mandatory**: If you are overriding a member from a base class, you MUST use the `override` keyword.
|
||||||
- **No Backing Fields**: There is no magic `field` identifier. If you need to store state, you must declare a separate (usually `private`) field.
|
- **No Backing Fields**: There is no magic `field` identifier. If you need to store state, you must declare a separate (usually `private`) field.
|
||||||
- **Type Inference**: You can omit the type declaration if it can be inferred or if you don't need strict typing.
|
- **Type Inference**: You can omit the type declaration if it can be inferred or if you don't need strict typing.
|
||||||
|
|
||||||
|
|||||||
@ -103,6 +103,11 @@ scope.addVoidFn("log") {
|
|||||||
println(items.joinToString(" ") { it.toString(this).value })
|
println(items.joinToString(" ") { it.toString(this).value })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When adding a member function to a class, you can use isOverride = true
|
||||||
|
// myClass.addFn("toString", isOverride = true) {
|
||||||
|
// ObjString("Custom string representation")
|
||||||
|
// }
|
||||||
|
|
||||||
// Call them from Lyng
|
// Call them from Lyng
|
||||||
scope.eval("val y = inc(41); log('Answer:', y)")
|
scope.eval("val y = inc(41); log('Answer:', y)")
|
||||||
```
|
```
|
||||||
@ -122,6 +127,9 @@ myClass.createField("version", ObjString("1.0.0"), isMutable = false)
|
|||||||
// Add a mutable field with an initial value
|
// Add a mutable field with an initial value
|
||||||
myClass.createField("count", ObjInt(0), isMutable = true)
|
myClass.createField("count", ObjInt(0), isMutable = true)
|
||||||
|
|
||||||
|
// If you are overriding a field from a base class, use isOverride = true
|
||||||
|
// myClass.createField("someBaseField", ObjInt(42), isOverride = true)
|
||||||
|
|
||||||
scope.addConst("MyClass", myClass)
|
scope.addConst("MyClass", myClass)
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -153,6 +161,16 @@ myClass.addProperty(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// You can also create an ObjProperty explicitly
|
||||||
|
val explicitProp = ObjProperty(
|
||||||
|
name = "hexValue",
|
||||||
|
getter = statement { ObjString(internalValue.toString(16)) }
|
||||||
|
)
|
||||||
|
myClass.addProperty("hexValue", prop = explicitProp)
|
||||||
|
|
||||||
|
// Use isOverride = true when overriding a property from a base class
|
||||||
|
// myClass.addProperty("baseProp", getter = { ... }, isOverride = true)
|
||||||
|
|
||||||
scope.addConst("MyClass", myClass)
|
scope.addConst("MyClass", myClass)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -226,13 +226,11 @@ Future work: introduce thread‑safe pooling (e.g., per‑thread pools or confin
|
|||||||
|
|
||||||
Closures executed by `launch { ... }` and `flow { ... }` resolve names using the `ClosureScope` rules:
|
Closures executed by `launch { ... }` and `flow { ... }` resolve names using the `ClosureScope` rules:
|
||||||
|
|
||||||
1. Closure frame locals/arguments
|
1. **Current frame locals and arguments**: Variables defined within the current closure execution.
|
||||||
2. Captured receiver instance/class members
|
2. **Captured lexical ancestry**: Outer local variables captured at the site where the closure was defined (the "lexical environment").
|
||||||
3. Closure ancestry locals + each frame’s `this` members (cycle‑safe)
|
3. **Captured receiver members**: If the closure was defined within a class or explicitly bound to an object, it checks members of that object (`this`), following MRO and respecting visibility.
|
||||||
4. Caller `this` members
|
4. **Caller environment**: Falls back to the calling context (e.g., the caller's `this` or local variables).
|
||||||
5. Caller ancestry locals + each frame’s `this` members (cycle‑safe)
|
5. **Global/Module fallbacks**: Final check for module-level constants and global functions.
|
||||||
6. Module pseudo‑symbols (e.g., `__PACKAGE__`)
|
|
||||||
7. Direct module/global fallback (nearest `ModuleScope` and its parent/root)
|
|
||||||
|
|
||||||
Implications:
|
Implications:
|
||||||
- Outer locals (e.g., `counter`) stay visible across suspension points.
|
- Outer locals (e.g., `counter`) stay visible across suspension points.
|
||||||
|
|||||||
@ -8,16 +8,13 @@ Name lookup across nested scopes and closures can accidentally form recursive re
|
|||||||
## Resolution order in ClosureScope
|
## Resolution order in ClosureScope
|
||||||
When evaluating an identifier `name` inside a closure, `ClosureScope.get(name)` resolves in this order:
|
When evaluating an identifier `name` inside a closure, `ClosureScope.get(name)` resolves in this order:
|
||||||
|
|
||||||
1. Closure frame locals and arguments
|
1. **Current frame locals and arguments**: Variables defined within the current closure execution.
|
||||||
2. Captured receiver (`closureScope.thisObj`) instance/class members
|
2. **Captured lexical ancestry**: Outer local variables captured at the site where the closure was defined (the "lexical environment").
|
||||||
3. Closure ancestry locals + each frame’s `thisObj` members (cycle‑safe)
|
3. **Captured receiver members**: If the closure was defined within a class or explicitly bound to an object, it checks members of that object (`this`). This includes both instance fields/methods and class-level static members, following the MRO (C3) and respecting visibility rules (private members are only visible if the closure was defined in their class).
|
||||||
4. Caller `this` members
|
4. **Caller environment**: If not found lexically, it falls back to the calling context (e.g., the DSL's `this` or the caller's local variables).
|
||||||
5. Caller ancestry locals + each frame’s `thisObj` members (cycle‑safe)
|
5. **Global/Module fallbacks**: Final check for module-level constants and global functions.
|
||||||
6. Module pseudo‑symbols (e.g., `__PACKAGE__`) from the nearest `ModuleScope`
|
|
||||||
7. Direct module/global fallback (nearest `ModuleScope` and its parent/root scope)
|
|
||||||
8. Final fallback: base local/parent lookup for the current frame
|
|
||||||
|
|
||||||
This preserves intuitive visibility (locals → captured receiver → closure chain → caller members → caller chain → module/root) while preventing infinite recursion between scope types.
|
This ensures that closures primarily interact with their defining environment (lexical capture) while still being able to participate in DSL-style calling contexts.
|
||||||
|
|
||||||
## Use raw‑chain helpers for ancestry walks
|
## Use raw‑chain helpers for ancestry walks
|
||||||
When authoring new scope types or advanced lookups, avoid calling virtual `get` while walking parents. Instead, use the non‑dispatch helpers on `Scope`:
|
When authoring new scope types or advanced lookups, avoid calling virtual `get` while walking parents. Instead, use the non‑dispatch helpers on `Scope`:
|
||||||
|
|||||||
@ -1494,7 +1494,7 @@ Typical set of String functions includes:
|
|||||||
| s1 += s2 | self-modifying concatenation |
|
| s1 += s2 | self-modifying concatenation |
|
||||||
| toReal() | attempts to parse string as a Real value |
|
| toReal() | attempts to parse string as a Real value |
|
||||||
| toInt() | parse string to Int value |
|
| toInt() | parse string to Int value |
|
||||||
| characters() | create [List] of characters (1) |
|
| characters | create [List] of characters (1) |
|
||||||
| encodeUtf8() | returns [Buffer] with characters encoded to utf8 |
|
| encodeUtf8() | returns [Buffer] with characters encoded to utf8 |
|
||||||
| matches(re) | matches the regular expression (2) |
|
| matches(re) | matches the regular expression (2) |
|
||||||
| | |
|
| | |
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
|
|||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
|
||||||
group = "net.sergeych"
|
group = "net.sergeych"
|
||||||
version = "1.2.-SNAPSHOT"
|
version = "1.2.0-SNAPSHOT"
|
||||||
|
|
||||||
// Removed legacy buildscript classpath declarations; plugins are applied via the plugins DSL below
|
// Removed legacy buildscript classpath declarations; plugins are applied via the plugins DSL below
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user