PART 0 RI AL Beginnings CO PY RI GH TE D MA TE CHAPTER 1: Primer
1 Primer WHAT’S IN THIS CHAPTER? ‰ Understanding strategies ‰ Reviewing Lambda calculus ‰ Infering types ‰ Understanding mutability ‰ Creating your own bindings Object-oriented programming has been with us for close to two decades, if not longer; its expressions of those concepts via the languages C# and Visual Basic, and the platform on which they run, the CLR, have been more recent, only since 2002.
x CHAPTER 1 PRIMER and these types are frequently collected together in a large bundle known as a framework or class library. And despite the relatively slow start that C++ class libraries saw during C++’s heyday, the combination of fast, cheap network access and volunteers willing to share their efforts has led to a proliferation of these infrastructural bundles that dwarfs anything the industry has seen before. Unfortunately, despite the huge success of C++, Java, and .
Setup x 5 programming techniques available to the developer using off-the-shelf tools and technology, but as the problems surmount, so will the desire to change those tools into something more, something that demonstrates the need for a new approach to programming beyond the traditional object-oriented one, and a new language to explore and expose those concepts more succinctly and directly. SETUP Imagine a system, a simple one (to start) for tracking students and instructors and the classes they teach.
x PRIMER CHAPTER 1 { get { return m_major; } set { m_major = value; } } } class Instructor : Person { private string m_dept; public Instructor(string fn, string ln, int a, string dept) : base(fn, ln, a) { Department = dept; } public string Department { get { return m_dept; } set { m_dept = value; } } } class Class { private string m_name; private List m_students = new List(); private Instructor m_instructor; public Class(string n) { Name = n; } public string Name { get { return m_nam
It’s That Time of Year Again… static Program() { Instructors.Add(new Instructor(“Al”, “Scherer”, 38, “Computer Science”)); Instructors.Add(new Instructor(“Albert”, “Einstein”, 50, “Physics”)); Instructors.Add(new Instructor(“Sigmund”, “Freud”, 50, “Psychology”)); Instructors.Add(new Instructor(“Aaron”, “Erickson”, 35, “Underwater Basketweaving”)); Students.Add(new Student(“Matthew”, “Neward”, 10, “Grade school”)); Students.Add(new Student(“Michael”, “Neward”, 16, “Video game design”)); Students.
x CHAPTER 1 PRIMER if (s.FirstName == first && s.LastName == last) { // ... Do something } } This feels sloppy somehow — after some deeper thought, several things emerge as “wrong.” First, an obvious bottleneck emerges if this list becomes large; for a school of a half-dozen Instructors and a few dozen Students, this simple one-at-a-time comparison works well enough, but if the system grows to incorporate campuses all across the world and millions of Students, this is going to break down quickly.
It’s That Time of Year Again… x 9 public Student Find(string first, string last) { foreach (Student i in data) { if (i.FirstName == first && i.LastName == last) return i; } return null; } } At fi rst glance, this seems like a good idea, but a longer look reveals that these two classes differ in exactly one thing — the kind of data they “wrap.” In the fi rst case, it’s a list of Instructors, and in the second, a list of Students. The sharp C# 2.
x CHAPTER 1 PRIMER return null; } } which works, for now. Unfortunately, although this solves the immediate problem, what happens when the Database needs to search for a Student by major, or an Instructor by field? Because those are properties not specified on the Person type, once again the Database class will fail.
Strategy x 11 The type constraint is still necessary, because the Database needs to return “null” in the event that the search fails. But now at least the Database is once again generic.
x CHAPTER 1 PRIMER THE DELEGATE STRATEGY The whole interface-based Strategy approach can be eliminated in favor of a well-defined delegate type and an instance of an anonymous method: delegate bool SearchProc(T candidate); class Database where T : class { private List data = new List(); public Database() { } public void Add(T i) { data.
The Delegate Strategy x 13 private List data = new List(); // ... public T[] FindAll(SearchProc algorithm) { List results = new List(); foreach (T i in data) { if (algorithm(i)) results.Add(i); } return results.ToArray(); } } C# 2.
x CHAPTER 1 PRIMER public Database() { } public void Add(T i) { data.Add(i); } public IEnumerable Filter(Predicate pred) { List results = new List(); foreach (T it in data) if (pred(it)) results.Add(it); return results; } public IEnumerable Map(Func transform) { List results = new List(); foreach (T it in data) results.
The Delegate Strategy x 15 { foreach (T it in data) yield return (transform(it)); } In terms of their purpose, Filter is the easiest to grasp — it applies the Predicate to each element in the Database and includes that element if the Predicate returns true. Map is less obvious — it applies a Func (an operation) to each element in the Database, transforming it into something else, usually (though not always) of a different type.
x CHAPTER 1 PRIMER { return acc + 1; })); But even more intriguingly, a collection of Students can be “reduced” to an XML representation by applying the same approach and transforming a Student into a string representation: string studentXML = (Students.Reduce(“”, delegate(Student st, string acc) { return acc + “” + st.FirstName + “”; })) + “”; If some of this sounds familiar, it is because much of this was later expanded to be a major part of the C# 3.
Lambda Calculus (Briefly) x 17 As can be surmised from the preceding code, the LINQ Aggregate extension method is the moral equivalent of the Reduce written earlier. And as was demonstrated, this means LINQ can be used to “reduce” a collection of Students into an XML representation. C# 3.0 also offered a slightly more terse way of specifying those delegates to be passed in to the Database, something called a lambda expression: Student s = null; while (s == null) { Console.
x CHAPTER 1 PRIMER { return op(l, r); } static void MathExamples() { int x = 2; int y = 3; // using explicit anonymous methods int added = Operate(x, y, delegate(int l, int r){ return l+r; }); int multd = Operate(x, y, delegate(int l, int r){ return l*r; }); // using lambda expressions int addedagain = Operate(x, y, (l, r) => l + r); int multdagain = Operate(x, y, (l, r) => l * r ); } When used this way, it doesn’t make a lot of sense, because it would seem obvious to just write x + y, but now that
Lambda Calculus (Briefly) x 19 get { return m_majorApproval; } } } class Class { // ... public bool Assign(Student s) { if (s.CanTake(this)) { Students.Add(s); return true; } return false; } } Now, the validation code can be kept in a third place, thus localizing it to neither the Student nor the Class, but someplace accessible to either, such as a ProgramSignup collection someplace that the Student constructor can access (or the Class.
x CHAPTER 1 PRIMER In the lambda calculus, this operation can be thought of as asking a function to take an int and another function, where that second function takes an int and returns an int back. So, in C# 2.
Lambda Calculus (Briefly) static Op> Curry(Op fn) { return delegate(T1 arg1) { return delegate(T2 arg2) { return fn(arg1, arg2); }; }; } static void MoreMathExamples() { int result = Add(2, 3); Operation add2 = delegate(int l, int r) { return l + r; }; int result2 = add2(2, 3); DelegateOp add3 = delegate(int l) { return delegate(int r) { return l + r; }; }; int result3 = add3(2)(3); Func> add4 = delegate(int l) { return delegate(int r) { return l + r; }
x CHAPTER 1 PRIMER written in small chunks, even as small as simple operations and then composed together, such as what might be needed for input validation code for a web application.
Type Inference x 23 C# 3.0 provides some of this, via the automatic property declaration — it assumes the task of creating a field and the get/set logic to return and assign to that field, respectively, but unfortunately, C# 3.0 will choke on the var declaration as a method parameter or as a field. And 3.
x CHAPTER 1 PRIMER Of course, types need not be the only thing inferred by the compiler; because public is the most common access modifier for methods, constructors and properties, and private is most common for fields, let those be the inferred default: // This is not legal C# 3.
Type Inference x 25 Unfortunately, the syntax is getting tricky to parse, particularly if the constructor body is now implicitly “inside” the class. It’s going to have difficulty knowing what denotes a member of the class and what denotes a local variable inside the constructor body. Even if the compiler could, the programmer may not, so an explicit declaration of what is a member and what isn’t would be helpful: // This is not legal C# 3.
x CHAPTER 1 PRIMER On top of all this, the language can start to make the explicit “generic” declarations of the earlier C# operations less necessary, because now the compiler will have the ability to infer the actual types of the parameters, and with it, the ability to infer them as generic type parameters: var swap = delegate (l, r) { var temp = r; r = l; l = temp; }; Here, the compiler can infer that l and r are of the same type, but that actual type is entirely irrelevant, because any type (clas
Expressions, Not Statements x 27 Of course, now that the situation has reversed itself, if the programmer does want the ability to modify the internals of an object, the programmer must explicitly mark the parts of the class that need to be mutable: // This is not legal C# 3.0 class Person(firstName, lastName, age : int) { member FirstName = firstName; member LastName = lastName; mutable member Age = age; member FullName = FirstName + “ “ + LastName; override ToString() { return String.
x CHAPTER 1 PRIMER If every (or most every) language construct is an expression, it means the language takes a more input-yields-output style to it, which reinforces the general nature of testable programs, rather than just as a series of statements.