Foreword
Someone today and chatted to the C # LINQ, find LINQ LINQ I know it seems like most people know not the same, how a different law? In fact, LINQ can also be used to engage in functional programming.
Of course, not to say a few writing lambda
and the like with the use of Java as stream
such even if the named LINQ, LINQ is actually a another something.
LINQ
In C #, LINQ I believe we have seen the following wording:
IEnumerable<int> EvenNumberFilter(IEnumerable<int> list)
{
return from c in list where c & 1 == 0 select c;
}
With LINQ syntax above code implements a screening list even.
LINQ is just a set of tools used to facilitate the operation of it, if we want our own if the type of support LINQ syntax, then we need to achieve our type IEnumerable<T>
, then you can be so used. . .
Oh, is that right? I understand all that. . . . . .
? ? ? Oh, my God, of course not!
In fact, LINQ and IEnumerable<T>
has nothing to do! LINQ is just a set of extensions only method, which is mainly composed of the following methods:
Method name | Method Description |
---|---|
Where | Data filtering |
Select/SelectMany | Data Projector |
Join/GroupJoin | Data connection |
OrderBy/ThenBy/OrderByDescending/ThenByDescending | Sorting data |
GroupBy | Data packets |
...... |
The above method corresponds LINQ Keywords: where
, select
, join
, orderby
, group
...
When the compiler compiles C # code that will convert LINQ syntax syntax extension method calls, for example:
from c in list where c > 5 select c;
It will be compiled into:
list.Where(c => c > 5).Select(c => c);
For another example:
from x1 in list1 join x2 in list2 on x1.k equals x2.k into g select g.u;
It will be compiled into:
list1.GroupJoin(list2, x1 => x1.k, x2 => x2.k, (x1, g) => g.u);
For another example:
from x in list orderby x.k1, x.k2, x.k3;
It will be compiled into:
list.OrderBy(x => x.k1).ThenBy(x => x.k2).ThenBy(x => x.k3);
also:
from c in list1
from d in list2
select c + d;
It will be compiled into:
list1.SelectMany(c => list2, (c, d) => c + d);
Stop, stop!
In addition, the compiler at compile time will always be first and then call the LINQ syntax translation compiled as a method, then, as long as the name of the corresponding method, does not mean you can use the LINQ syntax (escape
So you see this SelectMany
is not it. . .
SelectMany
is Monad
Oh my God, you look at this poor SelectMany
, this not Monad
required bind
function?
Gradually things get interesting again.
We have inherited the spirit of the one, write again Maybe<T>
.
Maybe<T>
First, we write an abstract class Maybe<T>
.
First, we add it to a Select
method for selecting the Maybe<T>
data, if it is T
, then a return Just<T>
, if it is Nothing<T>
, then a return Nothing<T>
. The equivalent of our returns
function:
public abstract class Maybe<T>
{
public abstract Maybe<U> Select<U>(Func<T, Maybe<U>> f);
}
Then we realize our Just
and Nothing
:
public class Just<T> : Maybe<T>
{
private readonly T value;
public Just(T value) { this.value = value; }
public override Maybe<U> Select<U>(Func<T, Maybe<U>> f) => f(value);
public override string ToString() => $"Just {value}";
}
public class Nothing<T> : Maybe<T>
{
public override Maybe<U> Select<U>(Func<T, Maybe<U>> _) => new Nothing<U>();
public override string ToString() => "Nothing";
}
Then, we have to Maybe
realize bind
- that is, to Maybe
add a known SelectMany
method.
public abstract class Maybe<T>
{
public abstract Maybe<U> Select<U>(Func<T, Maybe<U>> f);
public Maybe<V> SelectMany<U, V>(Func<T, Maybe<U>> k, Func<T, U, V> s)
=> Select(x => k(x).Select(y => new Just<V>(s(x, y))));
}
So far, Maybe<T>
achieved over! What, on this? ? So how to use it? Exciting time coming!
First, we create a few Maybe<int>
:
var x = new Just<int>(3);
var y = new Just<int>(7);
var z = new Nothing<int>();
We were then calculated using the LINQ x + y
, x + z
:
var u = from x0 in x from y0 in y select x0 + y0;
var v = from x0 in x from z0 in z select x0 + z0;
Console.WriteLine(u);
Console.WriteLine(v);
Output:
Just 10
Nothing
perfect! The above LINQ compiled as:
var u = x.SelectMany(_ => y, (x0, y0) => x0 + y0);
var v = x.SelectMany(_ => z, (x0, z0) => x0 + z0);
At this time, functions k
as int -> Maybe<int>
, and function s
as (int, int) -> int
, an addition function.
Function k
parameters we do not care, it is used as a selector
, we only need to generate a make Maybe<int>
, and then using the function s
of the two int
values do adder, and the results into a packed Just<int>
inside the can.
This process, if there are either produced Nothing
, the result is always the follow-up operation Nothing
, because Nothing.Select(...)
still Nothing
.
A little extension
We give this Maybe<T>
plus a Where
:
public abstract class Maybe<T>
{
public abstract Maybe<U> Select<U>(Func<T, Maybe<U>> f);
public Maybe<V> SelectMany<U, V>(Func<T, Maybe<U>> k, Func<T, U, V> s)
=> Select(x => k(x).Select(y => new Just<V>(s(x, y))));
public Maybe<U> Where(Func<Maybe<T>, bool> f) => f(this) ? this : new Nothing<T>();
}
Then we can play:
var just = from c in x where true select c;
var nothing = from c in x where false select c;
Console.WriteLine(just);
Console.WriteLine(nothing);
When the condition returns Just
, otherwise it returns Nothing
. Above will output:
Just 3
Nothing
There is a smell inside (escape
postscript
Subsequent articles in this series will be written in the mortgage, if C # a little disappointing, the Discriminated Unions, Higher Kinded Generics and Type Classes characteristics plus, and we continue.