C Sharp


... [ Programmiersprachen und Sprachsysteme ] ... [ Monaden in anderen Programmiersprachen ] ... [ << Haskell ] ... [ Ruby >> ] ...


Mit C# 3.0 hat Microsoft funktionale Elemente zu C# hinzugefügt, zum Beispiel Lambda-Ausdrücke:

1
x => f(x)


Dieser Ausdruck berechnet aus dem Parameter x den Wert f(x). Neben Lambda-Ausdrücken gibt es noch andere Elemente, die C# im Zusammenhang mit Monaden interessant sind.


LINQ


LINQ ist eine Spracherweiterung, die für C# von Erik Meijer entwickelt wurde. Mit dieser auf den ersten Blick SQL-ähnliche Syntax können Anfragen auf bestimmten Datenstrukturen gestellt werden, für die entsprechende Bindings existieren. Unterstützt werden beispielsweise Datenbanken, Container, XML und Arrays.

1
2
3
4
5
int[] func(int[] numbers) {
	return 	from n in numbers
			where n > 5
			select n + 1;
}


In diesem Beispiel werden alle Werte n <= 5 aus der Resultatmenge gefiltert und anschließend wird jedes Element um Eins inkrementiert.

Hier ist ein Beispiel für den Zugriff auf XML Strukturen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
XElement books = XElement.Parse(
		@"<books>
			<book>
				<title>Pro LINQ: Language Integrated Query in C#2010</title>
				<author>Joe Rattz</author>
			</book>
			<book>
				<title>Pro .NET 4.0 Parallel Programming in C#</title>
				<author>Adam Freeman</author>
			</book>
		</books>");

var titles =
	from book in books.Elements("book")
	where (string)book.Element("author") == "Joe Rattz"
	select book.Element("title");


Die Book-Elemente werden aus dem XML extrahiert. Anschließend werden die gewünschten Book-Elemente selektiert.

Außer den bereits vorgestellten, besteht die LINQ-Syntax aus mehreren Schlüsselwörtern, wie beispielsweise grop und orderby, wobei diese vor allem für die Listenmonade von Interesse sind und nur begrenzt für andere Monaden eine Anwendung finden.


Interne Umsetzung von LINQ in C#


Das erste Beispiel für die LINQ-Syntax wird durch den C#-Compiler in entsprechende normale Funktionsaufrufe umgewandelt.

1
2
3
numbers
	.Where(n => n > 5)
	.Select(n => n + 1)


LINQ ist also nur eine Syntaxerweiterung für C#, wobei Where und Select rein funktional sind. LINQ-Anweisungen enden immer mit einer Select-Anweisung, die jedoch nicht umgesetzt wird, wenn diese nur aus einem Wert besteht.


LINQ und Monaden


Jede from .. in-Anweisung nach der ersten wird in einen SelectMany() Funktionsaufruf umgewandelt, wobei SelectMany genau die Aufgabe von bind aus Haskell erfüllt. Das heißt, dass mehrere from .. in Anweisungen in der LINQ-Syntax ähnlich zur do-Notation aus Haskell sind.

1
2
3
4
5
6
public Maybe<int> DoSomeDivision(int denominator)
{
	return from a in 12.Div(denominator)
		   from b in a.Div(2)
		   select b;
}



In diesem Beispiel wurden diese beiden Divisionen durch die Maybe-Monade zusammengebunden. Da die Typen der Funktionen Div und DoSomeDivision genau denen aus Haskell entsprechen, können wir eine Implementierung von bind erzeugen, die sich ähnlich der aus Haskell verhält.


SelectMany hat folgende Signatur:

1
public static M<b> SelectMany<a, b>(this M<a> source, Func<a, M<b>> selector)



Der erste Parameter vom typ M<a> und der zweite vom Typ Funktion von a auf M<b>, also Func<a, M<b>>. Der Rückgabetyp von SelectMany ist M<b>. Verglichen mit der Haskell-Notation

1
bind :: m a -> (a -> m b) -> m b


ist die Notation in C# deutlich länger und komplizierter, drückt aber die gleiche Typsignatur aus. Um eine Monade zu implementieren wird also nur eine typkompatibele Implementierung von SelectMany benötigt, wodurch auch Monaden implementierbar sind, die keine Collections sind.


Wie kann die Maybe-Monade mit SelectMany() implementiert werden?


Als Beispiel wird die aus Haskell bekannte Maybe-Monade implementiert, wofür ein Maybe-Typ benötigt wird, der ähnlich zu dem aus Haskell ist. Für diese Beispiel verwenden wir diesen Typ:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public interface Maybe<T>{}

public class Nothing<T> : Maybe<T>
{
}

public class Just<T> : Maybe<T>
{
	public T Value { get; private set; }
	public Just(T value)
	{
		Value = value;
	}
}


Maybe ist ein Interface mit einem Typparameter und zwei Ausprägungen: Nothing und Just. Nothing ist ein leerer Typ, der nur für die Unterscheidung zu Just benötigt wird und dementsprechend keine Variablen besitzt. Die Just-Ausprägung hat einen Konstruktor und Zugriffsmethoden für Value vom Typ T.

C# erlaubt es, neue Methoden für jeden beliebigen Typ zu definieren, indem ein Parameter mit "this" gekennzeichnet wird. Diese Schreibweise ist ähnlich zu der Möglichkeit in Haskell, jede beliebige Funktion in der Infixnotation zu schreiben.

1
2
3
4
public static Maybe<T> ToMaybe<T>(this T value)
{
	return new Just<T>(value);
}



ToMaybe ist eine Funktion, die jeden beliebigen Wert vom Typ T in einen Wert vom Typ Maybe<T> einpackt. ToMaybe entspricht daher der Unit-Funktion, wobei diese in C# für die Definition von Monaden nicht unbedingt benötigt wird.

Die SelectMany Funktion wird wie folgt definiert:

1
2
3
4
5
6
7
public static Maybe<B> SelectMany<A, B>(this Maybe<A> source, Func<A, Maybe<B>> selector)
{
	var justa = source as Just<A>;
	return justa == null ?
		new Nothing<B>() :
		selector(justa.Value);
}


Falls source vom Typ Just ist, wird justa gesetzt. Ansonsten besitzt justa den Wert null. Abhängig vom Wert von justa wird anschließend entweder ein neues Nothing-Objekt erzeugt oder die selector-Funktion aufgerufen.


Async in C#


Sowohl C# als auch F# unterstützen asynchrone Berechnungen, wobei sich die Implementierung zwischen C# und F# unterscheidet.

C# bietet ähnlich einer Koroutine eine Möglichkeit, die aktuelle Funktion zu suspendieren, bis das Ergebnis der Berechnung eingetroffen ist.

In F# gibt es so genannte Computational Expressions und Async-Workflows.

1
2
3
4
5
6
7
8
let downloadAndExtractLinks url =
	async {
		let webClient = new System.Net.WebClient()
		let html = webClient
					.DownloadString(url : string)
		let! links = extractLinksAsync html
		return url, links.Count
	}


Async-Workflows sind Computational Expressions und eine Computational Expression ist eine in do-Notation geschriebene Liste von Anweisungen. Async-Workflows sind daher Monaden in F#. Innerhalb von Computational Expressions werden let!-Anweisungen umgesetzt:

Construct De-sugared Form
let pat = expr in cexpr let pat = expr in cexpr
let! pat = expr in cexpr b.Bind(expr, (fun pat -> cexpr))
return expr b.Return(expr)



... [ Programmiersprachen und Sprachsysteme ] ... [ Monaden in anderen Programmiersprachen ] ... [ << Haskell ] ... [ Ruby >> ] ...
generated by schmidt-doku-generator (GitHub)