35

I'm using some functional stuff in C# and keep getting stuck on the fact that List.Add doesn't return the updated list.

In general, I'd like to call a function on an object and then return the updated object.

For example it would be great if C# had a comma operator:

((accum, data) => accum.Add(data), accum) 

I could write my own "comma operator" like this:

static T comma(Action a, Func<T> result) { a(); return result(); } 

It looks like it would work but the call site would ugly. My first example would be something like:

((accum, data) => comma(accum.Add(data), ()=>accum)) 

Enough examples! What's the cleanest way to do this without another developer coming along later and wrinkling his or her nose at the code smell?

1
  • 1
    List.Add doesn't return a new list but just modifies it in-place. In this sense, it's not functional. Commented Jan 10, 2018 at 15:32

7 Answers 7

21

I know this as Fluent.

A Fluent example of a List.Add using Extension Methods

static List<T> MyAdd<T>(this List<T> list, T element) { list.Add(element); return list; } 
Sign up to request clarification or add additional context in comments.

6 Comments

Good call on including the Fluent definition.
But why wouldn't you just use LINQ for this sort of thing?
@KevinRoche: No one is suggesting that you not use linq. In fact this method appears to integrate well with linq.
@recursive LINQ (or any "functional" code) operations should not mutate the data, which this does. The LINQ way of doing this would be .Concat(new []{element})
@novaterata: Yes, this isn't really "pure", in the functional sense, but the question specifically asks about mutation.
|
14

I know that this thread is very old, but I want to append the following information for future users:

There isn't currently such an operator. During the C# 6 development cycle a semicolon operator was added, as:

int square = (int x = int.Parse(Console.ReadLine()); Console.WriteLine(x - 2); x * x); 

which can be translated as follows:

int square = compiler_generated_Function(); [MethodImpl(MethodImplOptions.AggressiveInlining)] private int compiler_generated_Function() { int x = int.Parse(Console.ReadLine()); Console.WriteLine(x - 2); return x * x; } 

However, this feature was dropped before the final C# release.

2 Comments

Can you provide a source for the discussion?
@ZevSpitz I was able to find github.com/dotnet/csharplang/issues/377, github.com/dotnet/roslyn/issues/6182, github.com/dotnet/roslyn/issues/2136 (you have to go through the various comments), and github.com/dotnet/csharplang/issues/973#event-2626832700. Other blogs/sites only suggest, that the feature may be added: i-programmer.info/news/89-net/7160-c-60-features.html, entwickler.de/online/… (German). You can find various discussion by searching google for roslyn "comma operator" github.
7

You can do almost exactly the first example naturally using code blocks in C# 3.0.

((accum, data) => { accum.Add(data); return accum; }) 

Comments

5

This is what Concat http://msdn.microsoft.com/en-us/library/vstudio/bb302894%28v=vs.100%29.aspx is for. Just wrap a single item in an array. Functional code should not mutate the original data. If performance is a concern, and this isn't good enough, then you'll no longer be using the functional paradigm.

((accum, data) => accum.Concat(new[]{data})) 

1 Comment

seemed the LINQ-y-est of the answers to me. shrug
5

Another technique, straight from functional programming, is as follows. Define an IO struct like this:

/// <summary>TODO</summary> public struct IO<TSource> : IEquatable<IO<TSource>> { /// <summary>Create a new instance of the class.</summary> public IO(Func<TSource> functor) : this() { _functor = functor; } /// <summary>Invokes the internal functor, returning the result.</summary> public TSource Invoke() => (_functor | Default)(); /// <summary>Returns true exactly when the contained functor is not null.</summary> public bool HasValue => _functor != null; X<Func<TSource>> _functor { get; } static Func<TSource> Default => null; } 

and make it a LINQ-able monad with these extension methods:

[SuppressMessage("Microsoft.Naming", "CA1724:TypeNamesShouldNotMatchNamespaces")] public static class IO { public static IO<TSource> ToIO<TSource>( this Func<TSource> source) { source.ContractedNotNull(nameof(source)); return new IO<TSource>(source); } public static IO<TResult> Select<TSource,TResult>(this IO<TSource> @this, Func<TSource,TResult> projector ) => @this.HasValue && projector!=null ? New(() => projector(@this.Invoke())) : Null<TResult>(); public static IO<TResult> SelectMany<TSource,TResult>(this IO<TSource> @this, Func<TSource,IO<TResult>> selector ) => @this.HasValue && selector!=null ? New(() => selector(@this.Invoke()).Invoke()) : Null<TResult>(); public static IO<TResult> SelectMany<TSource,T,TResult>(this IO<TSource> @this, Func<TSource, IO<T>> selector, Func<TSource,T,TResult> projector ) => @this.HasValue && selector!=null && projector!=null ? New(() => { var s = @this.Invoke(); return projector(s, selector(s).Invoke()); } ) : Null<TResult>(); public static IO<TResult> New<TResult> (Func<TResult> functor) => new IO<TResult>(functor); private static IO<TResult> Null<TResult>() => new IO<TResult>(null); } 

and now you can use the LINQ comprehensive syntax thus:

using Xunit; [Fact] public static void IOTest() { bool isExecuted1 = false; bool isExecuted2 = false; bool isExecuted3 = false; bool isExecuted4 = false; IO<int> one = new IO<int>( () => { isExecuted1 = true; return 1; }); IO<int> two = new IO<int>( () => { isExecuted2 = true; return 2; }); Func<int, IO<int>> addOne = x => { isExecuted3 = true; return (x + 1).ToIO(); }; Func<int, Func<int, IO<int>>> add = x => y => { isExecuted4 = true; return (x + y).ToIO(); }; var query1 = ( from x in one from y in two from z in addOne(y) from _ in "abc".ToIO() let addOne2 = add(x) select addOne2(z) ); Assert.False(isExecuted1); // Laziness. Assert.False(isExecuted2); // Laziness. Assert.False(isExecuted3); // Laziness. Assert.False(isExecuted4); // Laziness. int lhs = 1 + 2 + 1; int rhs = query1.Invoke().Invoke(); Assert.Equal(lhs, rhs); // Execution. Assert.True(isExecuted1); Assert.True(isExecuted2); Assert.True(isExecuted3); Assert.True(isExecuted4); } 

When one desires an IO monad that composes but returns only void, define this struct and dependent methods:

public struct Unit : IEquatable<Unit>, IComparable<Unit> { [CLSCompliant(false)] public static Unit _ { get { return _this; } } static Unit _this = new Unit(); } public static IO<Unit> ConsoleWrite(object arg) => ReturnIOUnit(() => Write(arg)); public static IO<Unit> ConsoleWriteLine(string value) => ReturnIOUnit(() => WriteLine(value)); public static IO<ConsoleKeyInfo> ConsoleReadKey() => new IO<ConsoleKeyInfo>(() => ReadKey()); 

which readily allow the writing of code fragments like this:

from pass in Enumerable.Range(0, int.MaxValue) let counter = Readers.Counter(0) select ( from state in gcdStartStates where _predicate(pass, counter()) select state ) into enumerable where ( from _ in Gcd.Run(enumerable.ToList()).ToIO() from __ in ConsoleWrite(Prompt(mode)) from c in ConsoleReadKey() from ___ in ConsoleWriteLine() select c.KeyChar.ToUpper() == 'Q' ).Invoke() select 0; 

where the old C comma operator is readily recognized for what it is: a monadic compose operation.

The true merit of the comprehension syntax is apparent when one attempts to write that fragment in the flunt style:

( Enumerable.Range(0,int.MaxValue) .Select(pass => new {pass, counter = Readers.Counter(0)}) .Select(_ => gcdStartStates.Where(state => _predicate(_.pass,_.counter())) .Select(state => state) ) ).Where(enumerable => ( (Gcd.Run(enumerable.ToList()) ).ToIO() .SelectMany(_ => ConsoleWrite(Prompt(mode)),(_,__) => new {}) .SelectMany(_ => ConsoleReadKey(), (_, c) => new {c}) .SelectMany(_ => ConsoleWriteLine(), (_,__) => _.c.KeyChar.ToUpper() == 'Q') ).Invoke() ).Select(list => 0); 

Comments

3

The extension method is arguably the best solution, but for completeness' sake, don't forget the obvious alternative: a wrapper class.

public class FList<T> : List<T> { public new FList<T> Add(T item) { base.Add(item); return this; } public new FList<T> RemoveAt(int index) { base.RemoveAt(index); return this; } // etc... } { var list = new FList<string>(); list.Add("foo").Add("remove me").Add("bar").RemoveAt(1); } 

Comments

3

I thought it would be interesting to make a version of my wrapper class answer that doesn't require you write the wrapper methods.

public class FList<T> : List<T> { public FList<T> Do(string method, params object[] args) { var methodInfo = GetType().GetMethod(method); if (methodInfo == null) throw new InvalidOperationException("I have no " + method + " method."); if (methodInfo.ReturnType != typeof(void)) throw new InvalidOperationException("I'm only meant for void methods."); methodInfo.Invoke(this, args); return this; } } { var list = new FList<string>(); list.Do("Add", "foo") .Do("Add", "remove me") .Do("Add", "bar") .Do("RemoveAt", 1) .Do("Insert", 1, "replacement"); foreach (var item in list) Console.WriteLine(item); } 

Output:

foo replacement bar 

EDIT

You can slim down the syntax by exploiting C# indexed properties.

Simply add this method:

public FList<T> this[string method, params object[] args] { get { return Do(method, args); } } 

And the call now looks like:

list = list["Add", "foo"] ["Add", "remove me"] ["Add", "bar"] ["RemoveAt", 1] ["Insert", 1, "replacement"]; 

With the linebreaks being optional, of course.

Just a bit of fun hacking the syntax.

3 Comments

I'd never have thought of using an index property like this. At the same time amazing and most ugly ;) (for 2 reasons: changing the object in a get{} property, and using magic strings)
@devio, I won't dispute the ugliness (but it's kind of cool too :)), but these aren't magic strings. Magic strings are something that produces special/unique results. But using hardcoded literals like this of course bypasses the compile-time checking of both method names (no symbols!) and types. It kinda turns C# into a smooshy weakly typed dynamic language (not to mention hell slow!). Fun :)
You could use an Enum for the method names (convert to string at runtime) - it doesn't make any techincal difference but it would be neater and much less error-prone. (Or you could use consts)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.