# What's New in Lyng

This document highlights the latest additions and improvements to the Lyng language and its ecosystem.

## Language Features

### Class Properties with Accessors
Classes now support properties with custom `get()` and `set()` accessors. Properties in Lyng do **not** have automatic backing fields; they are pure accessors.

```lyng
class Person(private var _age: Int) {
    // Read-only property
    val ageCategory get() = if (_age < 18) "Minor" else "Adult"

    // Read-write property
    var age: Int
        get() = _age
        set(v) {
            if (v >= 0) _age = v
        }
}
```

### Private and Protected Setters
You can now restrict the visibility of a property's or field's setter using `private set` or `protected set`. This allows members to be publicly readable but only writable from within the declaring class or its subclasses.

### Refined Protected Visibility
Ancestor classes can now access `protected` members of their descendants if it is an override of a member known to the ancestor. This enables base classes to call protected methods that are implemented or overridden in subclasses.

```lyng
class Counter {
    var count = 0
        private set  // Field with private setter
        
    fun increment() { count++ }
}

class AdvancedCounter : Counter {
    var totalOperations = 0
        protected set // Settable here and in further subclasses
}

let c = Counter()
c.increment() // OK
// c.count = 10 // Error: setter is private
```

### Late-initialized `val` Fields
`val` fields in classes can be declared without an immediate initializer, provided they are assigned exactly once. If accessed before initialization, they hold the special `Unset` singleton.

```lyng
class Service {
    val logger
    
    fun check() {
        if (logger == Unset) println("Not initialized yet")
    }

    init {
        logger = Logger("Service")
    }
}
```

### Named Arguments and Named Splats
Function calls now support named arguments using the `name: value` syntax. If the variable name matches the parameter name, you can use the `name:` shorthand.

```lyng
fun greet(name, greeting = "Hello") {
    println("$greeting, $name!")
}

val name = "Alice"
greet(name:)                  // Shorthand for greet(name: name)
greet(greeting: "Hi", name: "Bob")

let params = { name: "Charlie", greeting: "Hey")
greet(...params)              // Named splat expansion
```

### Multiple Inheritance (MI)
Lyng now supports multiple inheritance using the C3 Method Resolution Order (MRO). Use `this@Type` or casts for disambiguation.

```lyng
class A { fun foo() = "A" }
class B { fun foo() = "B" }

class Derived : A, B {
    fun test() {
        println(foo())              // Resolves to A.foo (leftmost)
        println(this@B.foo())       // Qualified dispatch to B.foo
    }
}

let d = Derived()
println((d as B).foo())             // Disambiguation via cast
```

### Singleton Objects
Singleton objects are declared using the `object` keyword. They provide a convenient way to define a class and its single instance in one go.

```lyng
object Config {
    val version = "1.2.3"
    fun show() = println("Config version: " + version)
}

Config.show()
```

### Object Expressions
You can now create anonymous objects that inherit from classes or interfaces using the `object : Base { ... }` syntax. These expressions capture their lexical scope and support multiple inheritance.

```lyng
val worker = object : Runnable {
    override fun run() = println("Working...")
}

val x = object : Base(arg1), Interface1 {
    val property = 42
    override fun method() = this@object.property * 2
}
```

Use `this@object` to refer to the innermost anonymous object instance when `this` is rebound.

### Unified Delegation Model
A powerful new delegation system allows `val`, `var`, and `fun` members to delegate their logic to other objects using the `by` keyword.

```lyng
// Property delegation
val lazyValue by lazy { "expensive" }

// Function delegation
fun remoteAction by myProxy

// Observable properties
var name by Observable("initial") { n, old, new -> 
    println("Changed!") 
}
```

The system features a unified interface (`getValue`, `setValue`, `invoke`) and a `bind` hook for initialization-time validation and configuration. See the [Delegation Guide](delegation.md) for more.

### User-Defined Exception Classes
You can now create custom exception types by inheriting from the built-in `Exception` class. Custom exceptions are real classes that can have their own fields and methods, and they work seamlessly with `throw` and `try-catch` blocks.

```lyng
class ValidationException(val field, m) : Exception(m)

try {
    throw ValidationException("email", "Invalid format")
}
catch(e: ValidationException) {
    println("Error in " + e.field + ": " + e.message)
}
```

### Assign-if-null Operator (`?=`)
The new `?=` operator provides a concise way to assign a value only if the target is `null`. It is especially useful for setting default values or lazy initialization.

```lyng
var x = null
x ?= 42          // x is now 42
x ?= 100         // x remains 42 (not null)

// Works with properties and index access
config.port ?= 8080
settings["theme"] ?= "dark"
```

The operator returns the final value of the receiver (the original value if it was not `null`, or the new value if the assignment occurred).

### Transient Attribute (`@Transient`)
The `@Transient` attribute can now be applied to class fields, constructor parameters, and static fields to exclude them from serialization.

```lyng
class MyData(@Transient val tempSecret, val publicData) {
    @Transient var cachedValue = 0
    var persistentValue = 42
}
```

Key features:
- **Serialization**: Transient members are omitted from both Lynon binary streams and JSON output.
- **Structural Equality**: Transient fields are automatically ignored during `==` equality checks.
- **Deserialization**: Transient constructor parameters with default values are correctly restored to those defaults upon restoration.

### Value Clamping (`clamp`)
A new `clamp()` function has been added to the standard library to limit a value within a specified range. It is available as both a global function and an extension method on all objects.

```lyng
// Global function
clamp(15, 0..10)    // returns 10
clamp(-5, 0..10)   // returns 0

// Extension method
val x = 15
x.clamp(0..10)      // returns 10

// Exclusive and open-ended ranges
15.clamp(0..<10)    // returns 9
15.clamp(..10)      // returns 10
-5.clamp(0..)       // returns 0
```

`clamp()` correctly handles inclusive (`..`) and exclusive (`..<`) ranges. For discrete types like `Int` and `Char`, clamping to an exclusive upper bound returns the previous value.

## Tooling and Infrastructure

### CLI: Formatting Command
A new `fmt` subcommand has been added to the Lyng CLI.

```bash
lyng fmt MyFile.lyng             # Print formatted code to stdout
lyng fmt --in-place MyFile.lyng  # Format file in-place
lyng fmt --check MyFile.lyng     # Check if file needs formatting
```

### IDEA Plugin: Autocompletion
Experimental lightweight autocompletion is now available in the IntelliJ plugin. It features type-aware member suggestions and inheritance-aware completion.

You can enable it in **Settings | Lyng Formatter | Enable Lyng autocompletion**.

### Kotlin API: Exception Handling
The `Obj.getLyngExceptionMessageWithStackTrace()` extension method has been added to simplify retrieving detailed error information from Lyng exception objects in Kotlin. Additionally, `getLyngExceptionMessage()` and `raiseAsExecutionError()` now accept an optional `Scope`, making it easier to use them when a scope is not immediately available.
