Proposal: Achieving Full FP Capabilities in C# Through Expression-Based Operators #9817
-
Operators in C#: Toward a Complete Functional Expression Model1. Definition of an OperatorIn this document, we define an operator as:
In other words, an operator is a semantic subset of a function. // Simple operator
Func<int, int> square = x => x * x;
// Operators can also be assigned to variables
var inc = (int x) => x + 1;
// Expression-bodied members also qualify
int AddOne(int x) => x + 1;There is no technical difference between an operator and a regular function— 2. The Current FP Capabilities of C#As of C# 13 / .NET 9, C# already provides nearly all functional programming fundamentals:
The last two items are the final missing pieces for a fully functional C#. 3. Example: Expression-based Operator Flow via Type FlowBelow is a minimal functional composition example. using System;
public interface Ix
{
int Value { get; }
}
// Concrete implementation
public readonly struct X : Ix
{
public int Value { get; }
public X(int value) => Value = value;
}
// Operator definitions — all expression-bodied (no imperative code)
public static class XOps
{
// Add operator
public static Ix Add(this Ix x, int v) => new X(x.Value + v);
// Multiply operator
public static Ix Mul(this Ix x, int v) => new X(x.Value * v);
// Recursive operator (pure expression form, non-optimized recursion)
public static Ix Pow(this Ix x, int n) =>
n switch
{
<= 0 => new X(1),
1 => x,
_ => x.Mul(x.Pow(n - 1).Value)
};
}
public class Program
{
public static void Main()
{
Ix result = new X(2)
.Add(3) // => 5
.Mul(4) // => 20
.Pow(2); // => 400
Console.WriteLine(result.Value); // Output: 400
}
}4. What if We Had
|
| Aspect | Current Status | Future Direction |
|---|---|---|
| Lambda expressions | ✅ Complete | Fully expressive |
| Type flow mechanism | ✅ Complete | Enabled by extension methods |
| Tail recursion optimization | ❌ | Should be enabled for lambdas |
| Generic function pointer | delegate<T1..Tn> proposal |
|
| Operator semantics | ✅ | Can be standardized as a subset of functions |
Once these two missing mechanisms are implemented,
C# will become a fully functional-capable language—
where all logic can be represented as composable operators without imperative statements.
Would you like me to help you add a short proposal header section (like “Motivation”, “Proposal”, “Benefits”, “Open Questions”) in the GitHub issue style before you post it to dotnet/csharplang? It’ll make it match their official RFC tone (like other language proposals).
Beta Was this translation helpful? Give feedback.
Replies: 8 comments 10 replies
-
|
With complete FP support, system libraries designed on this foundation for parallelization can be more targeted. Like the current Plinq system library, it can be seen as an important interface to FP. We can leverage FP's inherent concurrency-friendly advantages to better encourage developers to naturally switch to a concurrency perspective. This can help address specific business needs requiring concurrency. It can seamlessly integrate with C#'s object-oriented programming, each leveraging its own strengths. This creates a truly complementary, rather than chaotic, multi-paradigm language. You should notice that many new C# features are now expression-based. This is an optional mechanism. From a narrow perspective, I could say it increases confusion and unnecessary complexity. For example, |
Beta Was this translation helpful? Give feedback.
-
.... I strongly recommend you restart this discussion. Clearly layout the real world problems that the existing language isn't suitable for, and why and how these language ideas are critical to address them. |
Beta Was this translation helpful? Give feedback.
-
The || (or) operator short-circuits the evaluation of its parameters. An equivalent normal Or function would evaluate all parameters first and then perform the logic. |
Beta Was this translation helpful? Give feedback.
-
Proposal:
|
| Concept | Description |
|---|---|
<type-flow> |
The identity of the flow type used in FP composition |
| Goal | Enable automatic return and chaining of the same flow type |
| Mechanism | Use a this-like marker to define flow-carrier parameters |
| Benefit | Makes FP-style algorithms (e.g., sorting) fully composable and declarative |
| Compatibility | Does not alter existing imperative semantics |
Conclusion
C# already has most of the ingredients for functional expression — operators, lambdas, and compositional syntax. What we need now is a formal mechanism for type flow continuity — a <type-flow> identity that makes expression-based algorithms both natural and composable.
This proposal keeps FP purity, enables declarative design, and integrates smoothly with the existing type system.
Discussion welcome on signature semantics, return-type enforcement, and the definition of a "flow operator" in the C# type system.
Beta Was this translation helpful? Give feedback.
-
## Extending the Proposal: FP-Specific Compilation and Parallelization Semantics
### 1. Functional Operator Metadata at Compile Time
If C# can recognize certain methods as *functional operators*,
then part of their **first-level compile-time representation** could carry *operator-level metadata* —
that is, automatic annotations or attributes generated by the compiler.
This metadata could help the compiler and runtime distinguish FP-composable methods
from ordinary imperative methods, thereby isolating the new rules
and preventing excessive system-wide complexity.
**Example Concept:**
```csharp
[FunctionalOperator]
int[] Swap(this int[] source, int i, int j) => ...Such annotations could allow the compiler to:
In short, this mechanism keeps the FP behavior self-contained 2. Business-Level Parallelism and FP IntegrationFrom the previous sorting example, we observed a path-dependency problem — However, some operations are naturally parallel-friendly. int Sum(this int[] source);This kind of operator can safely execute in parallel since 3. Introducing Lightweight Parallel DirectivesFor such FP-friendly operations, we could introduce The goal is to make parallel invocation expressive and declarative, Possible idea (syntax to be discussed): [Parallel]
int Sum(this int[] source);or even more dynamically: int Sum(this int[] source) parallel => ...This lets developers declare business-level knowledge — 4. Contextual Parallelization from Business SemanticsSome operations — like element-wise addition — are also semantically parallel. For example: int[] AddElements(this int[] a, int[] b);A type-level or keyword-based mechanism could make it trivial This naturally fits into the FP world, 5. Summary of This Section
6. Discussion Points
Conclusion for this Part The core idea is to give C# developers fine-grained FP-level control over parallel execution, This continues the spirit of |
Beta Was this translation helpful? Give feedback.
-
|
In the earlier sorting example, the algorithm shows a path dependency, which is not suitable for parallel execution. However, consider another case: int Sum(this int[] source); This kind of operation is naturally parallelizable. So, I’d like to propose a more flexible parallel instruction mechanism that allows such operators to run in parallel without relying on a fixed framework like PLINQ. I don’t have a full syntax design yet — open to discussion. Business-Driven Parallelism In many real-world cases, the caller can determine whether an operation is parallelizable — int[] AddVector(int[] a, int[] b); If we design something like: a op[parallel] b then such operations could automatically execute in parallel. The op[] syntax could even accept parameters, for example: a op[parallel, simd] b This allows the developer to express that the operation should run with parallelization and SIMD acceleration — The emphasis here is not to force parallelism, but to make it natural and accessible at the operator level, where the business context already knows what’s safe to parallelize. Summary To summarize, this proposal explores: Extending FP composability via a concept. Allowing operator functions to auto-return their flow type via a this-like marker. Restricting FP extensions to isolated compile-time scopes. Enabling intuitive and business-driven parallelism via operator-level syntax (op[parallel], op[simd]). These extensions could help C# move toward a more expressive, composable, and performance-friendly FP model — without compromising existing imperative semantics. |
Beta Was this translation helpful? Give feedback.
-
This already exists: public delegate TResult Op<T1, T2, TResult>(T1 a, T2 b);It's slightly more verbose that your request, but it already exists. Not to mention that |
Beta Was this translation helpful? Give feedback.
-
|
If C# undergoes a more standardized functional programming reform, its functional programming support will shift from the current imitation and template-based approach to a rule-based approach. Clearly, these two approaches offer vastly different levels of freedom. Imitation and templates attempt to confine functional programming (FP) applications to pre-defined, fixed mechanisms using a finite level of abstraction, while rule-based approaches allow for greater creative freedom. This would result in a more bounded, rule-based, and purer implementation of FP in C#. Users would build their required business modules using their own operators, designing their own |
Beta Was this translation helpful? Give feedback.
Can you provide a block of "before" and "after" C# code to show what exactly your proposal is? As Cyrus and I have mentioned, everything (we've inferred) you're asking for already exists. So what are you saying?