Server Bug Fix: What is point-free (AKA η-reduction or pointless) and how can it be used in C#

Original Source Link

I stumbled upon this answer in codegolf.stackexchange.com where they talk about point-free programming. The following was an code example.

instead of

    foo.Select(x=>int.Parse(x))

you can use

    foo.Select(int.Parse)

Questions:

  1. How does Select()know how to pass the variable into int.Parse()?

  2. Is the point-free technique limited to Linq/Select or can other methods use it aswell?

  3. How can I implement this into my own C# functions? Would love some
    examples.

I would argue that your provided example is not in fact point-free as would be in functional language like Haskell. It is simply just a compiler shortcut. And that truly point-free style is not possible in C# due to lack of primitives for functional composition.

The heuristic of “there is no mention of function parameters” is not the exact definition of the term. As explained here, it is about how multiple functions are called together.

Lets say you have two functions f(A)->B and g(B)->C and you want to call them one after the other. In normal situation, you would call the first method, save it’s result and then pass the result to the second one :

B = f(A)
C = g(B)

or simplified:

C = g(f(A))

Both above are equivalent. point-free style would be different in that instead of calling the two functions, you first compose the functions into a third function h(A)->C and then call that:

h = f . g // here . is function composition operator that C# lacks
C = h(A)

No such thing is happening in your C# code. The two parts are identical in ‘style’ and first one just contains a one more level of indirection:

foo.Select(x=>int.Parse(x))

is equivalent to

int anonymous_compiler_generated_method(string x)
{
    return int.Parse(x);
}

foo.Select(anonymous_compiler_generated_method)

Which is not that different from

foo.Select(int.Parse)

If I were to use Haskell for comparison, I would call this example a trivial usage of higher-order functions.

How does Select know how to pass the variable into int.Parse

Select is approximately

IEnumerable<U> Select(this IEnumerable<T> source, Func<T, U> proj) {
    foreach (T obj in source) yield proj(obj);
}

It doesn’t deal in the variable, but a sequence of values. Other types that have Select have similar meanings

async Task<U> Select(this Task<T> source, Func<T, U> proj) {
    return proj(await source);
}

Is the point-free technique limited to Linq/Select or can other methods use it aswell?

You can use it whereever you wish to initialise a Func from a function, you don’t need a lambda to wrap it.

As noted in the comments, this isn’t really point-free in the same way as in Haskell, where you can define a function without naming it’s parameters.

Here it is a syntax for initialising a variable from a value.

How can I implement this into my own C# functions?

Have functions with Func or Action parameters

The short answers to your questions would be:

  1. The compiler does something called type inference, which it uses to figure out types you did not explicitly specify. This is also what happens when you use var instead of an actual variable name. The standard algorithm for this is called Hindley-Milner, and it has to do with a computation model called the typed lambda calculus.

  2. You can use it anywhere a Func is used, for example

int Plus1(int x) { return x + 1; }

Func<int, int> plus1 = x => Plus1(x);
Func<int, int> _plus1 = Plus1;
  1. Don’t. Its not a good idea to force a style from a different language on your language, as the code becomes difficult to read and difficult to maintain. FWIW you can integrate other primitives and emulate writing point-free code, but it’s more of a fun party trick than anything else:
using System.IO;
using System;
using System.Linq;

class Program
{
    static void Main()
    {
        var ls = Enumerable.Range(1, 10).Select<int, int>(new Compose<int, int, int>(y => y+1, y => y * 2));
        foreach (int l in ls) {
            Console.WriteLine(l);
        }
    }
}

class Compose<A, B, C> {
    Func<A, B> f;
    Func<B, C> g;

    public Compose(Func<A, B> f, Func<B, C> g) {
        this.f = f;
        this.g = g;
    }

    public static implicit operator Func<A, C>(Compose<A, B, C> c) {
        return x => c.g(c.f(x));
    }
}

The terms point-free and η-reduction come from functional programming, and while C# tries (and mostly succeeds) in presenting a functional API for programmers, there are some fundamental differences between it and functional languages.

I’d suggest you read up on the lambda calculus, which is the model of computation where these two terms originally stem from.

Tagged :

Leave a Reply

Your email address will not be published. Required fields are marked *