# C# Language Design Meeting for July 15th, 2026

## Agenda

- [Declarative UI construction](#declarative-ui-construction)

## Quote of the Day

- "Then you graduate to the \"dysfunctional geek\" phase"

## Discussion

### Declarative UI construction

Exploration: https://github.com/microsoft/microsoft-ui-reactor/blob/04d6618c099a5e69d63638b08d89a1e1b8b50518/docs/specs/019-collection-initializer-api-exploration.md

We met with the Microsoft.UI.Reactor team, which has been prototyping MVU-centric APIs for WinUI, to review its
exploration and consider what language features it might motivate in C#. Reactor provides a C#-centric Model-View-Update
programming model in which application code constructs an immutable graph of elements and the framework reconciles
successive graphs to update the native UI. This is broadly the coarse-grained model used by React: the application
rebuilds a description of the view, rather than tracking dependencies at the level of every individual value.

We contrasted that approach with the fine-grained dependency tracking used by frameworks such as SwiftUI and Jetpack
Compose. Fine-grained tracking can avoid rebuilding an entire graph, but it generally depends on language or compiler
support to identify dependencies and propagate signal-like values. The Reactor team chose graph rebuilding as a
pragmatic fit for the general structure of C# today, allowing unrestricted C# during construction, including arbitrary
methods, LINQ queries, conditionals, and imperative control flow. That current implementation choice does not constrain
the language-design exploration. We are also open to features that could enable SwiftUI- or Compose-like expressiveness
in C#, including features that could support a finer-grained model while still integrating coherently with the language.

Reactor currently constructs immutable records through factory methods and configures them with fluent modifiers. This
works well for short examples and preserves useful factory names, especially where the natural record type name would
conflict with a framework or BCL type. It also allows modifiers to be composed and applied conditionally. At scale,
however, call sites use three different forms: fluent methods for common properties, `with` expressions for properties
exposed directly by records, and `.Set()` callbacks as an escape hatch for native properties. The library must generate a
large modifier API, and each modifier can allocate an intermediate record. Setting several properties directly during
construction has produced meaningful performance improvements in experiments.

This led us to ask what the ideal syntax would be if we were not initially constrained by today's C#. The desired shape
should make three things work together: selecting how an element is constructed, setting its properties, and providing
its children. Improving only one of these dimensions risks moving complexity elsewhere. For example, collection
expressions provide concise children and support spread elements, but do not by themselves provide a way to configure
the containing element. Object initializers group properties clearly, but ordinarily require `new`, and classic
collection initializers require mutable `Add` behavior that sits awkwardly with immutable records.

One option is ordinary object creation with object and collection initializers. This gives properties and children a
declarative, brace-delimited structure, avoids modifier chains, and sets properties directly during construction. Mixed
object and collection initializers would allow member assignments and child elements to appear together in one
initializer. That is attractive for UI nodes, but does not remove repeated `new` expressions, and classic collection
initialization still relies on mutation. Using an explicit `Children = [...]` property avoids mutation and gains
collection-expression spread, at the cost of additional ceremony at every nesting level.

Factory initializers take the complementary approach. They allow initializer syntax after an opted-in factory call. This
preserves useful factory names and positional arguments while allowing properties to be grouped in a trailing block. A
current prototype deliberately requires an explicit `Children = [...]` assignment rather than allowing bare child
elements alongside properties. We discussed whether the feature should remain narrowly focused on enabling initializers
after factory calls, or should solve the complete UI-tree shape. A narrow feature is easier to understand and may be
useful outside UI construction, but the motivating scenario remains noticeably incomplete without ergonomic children.

We also explored trailing lambdas with an implicit receiver, similar to Kotlin. Such a block could execute arbitrary
statements against a newly constructed value, naturally supporting property assignments, loops, and helper calls. It
could therefore cover more than object initializer grammar. However, implicit receivers introduce name-lookup and
readability concerns, particularly when blocks are nested. We would need clear rules for which receiver supplies an
unqualified name, how members of enclosing receivers are accessed, and whether a block is primarily an ordinary lambda
or a privileged initialization construct.

Another variation revisited `init` methods, including extension `init` methods, from a previous LDM discussion. An
initialization method could package a reusable set of assignments and apply it as part of construction, providing a way
to express styles without generating a modifier for every property. This raised broader questions about whether
constructors and factory methods should be more uniform, and whether the language needs a general way to apply a block
of initialization behavior to a value.

We considered nested member assignment in initializers and `with` expressions as another useful building block. This
would permit a path such as `Layout.Padding.Left = value` in an initializer, reducing temporary objects and nesting for
grouped properties. It also follows the correspondence between construction and pattern matching: if patterns can
inspect a nested path concisely, construction should have a similarly concise way to provide one. More generally, we
want to preserve useful symmetry between the forms used to construct, inspect, and copy values.

Composability is at least as important as the syntax of a complete tree. It must remain easy to extract an arbitrary
subtree into a method without changing how callers use it. The same applies to extracting a group of property settings
into a reusable style. We do not want a syntax that looks excellent only when the entire tree is written inline but
becomes awkward at abstraction boundaries. Lazy construction may also matter for large trees, although that is separable
from the surface syntax.

We agreed that future examples must emphasize non-golden paths. Real UI code uses LINQ, spread elements, nullable and
conditional children, switch expressions, loops that build lists imperatively, and calls to arbitrary helper methods.
It also needs escape hatches for framework-specific properties. Comparing only small static trees would favor syntax
that does not survive realistic application code. We should compare before-and-after versions of complete applications
and account for generated API size, allocations, discoverability, refactorability, and readability.

#### References

- [Factory initializers](https://github.com/dotnet/csharplang/discussions/10207)
- [Mixed object and collection initializers](https://github.com/dotnet/csharplang/issues/10185)
- [Nested members in `with` and object creation](https://github.com/dotnet/csharplang/issues/9003)
- [`init` methods LDM discussion](https://github.com/dotnet/csharplang/blob/3c8559f186d4c5df5d1299b0eaa4e139ae130ab6/meetings/2020/LDM-2020-04-20.md)

#### Conclusion

We did not select or approve any individual language feature. We will form a working group to explore the space
holistically. The group will first describe an idealized syntax without limiting itself to features already available in
C#, survey relevant languages and frameworks such as Kotlin, and then prototype promising combinations as experimental
compiler features. Those experiments will be applied to real Reactor applications so we can compare representative code
and identify which language changes provide the most value.

The working group will bring experimental results and candidate designs back to the full LDM for feedback before asking
for detailed specification review.
