Slide 1

Slide 1 text

C# Light A proposal for a new C# syntax Inspired by a post by Phil Trelford UPGRADE YOUR MONITOR Read these slides and you might get a free triple height monitor* *or visual equivalent

Slide 2

Slide 2 text

Why change something that works?

Slide 3

Slide 3 text

Why change something that works? Because... Why not compete with scripting languages? Lightweight, but keep the power of static typing.

Slide 4

Slide 4 text

Why change something that works? Because... Why not compete with scripting languages? Lightweight, but keep the power of static typing. Because... Times are changing. Defaults should change too. Make immutability the new default.

Slide 5

Slide 5 text

Why change something that works? Because... Why not compete with scripting languages? Lightweight, but keep the power of static typing. Because... Times are changing. Defaults should change too. Make immutability the new default. Because... Many common patterns are too hard. Make good practices easy (but allow deviations).

Slide 6

Slide 6 text

! HEALTH WARNING ! These slides are for consumption by open-minded programmers ONLY. When taken with pre-existing closed-mindedness, can cause shock, high blood pressure, anxiety, nausea, confusion and panic.

Slide 7

Slide 7 text

You must be at least this open minded to read these slides How open minded are you? Slime mould Bony fishes People who don't use LINQ People who don't use generics Opposable thumbs People who want C# v6 People who use lambdas

Slide 8

Slide 8 text

Vision for C# Light Clean and lightweight code. Immutability by default. Common scenarios are easy. We make mutability a special case, rather than the other way around.

Slide 9

Slide 9 text

public class Person { public Person(string name, DateTime birthday) { _name = name; _birthday = birthday; } private readonly string _name; private readonly DateTime _birthday; /// /// Full name /// public string Name { get { return _name; } } /// /// Birthday /// public DateTime Birthday { get { return _birthday; } } } Here is a typical immutable class in C# which we'll use as an example throughout.

Slide 10

Slide 10 text

Here is a typical immutable class in C# which we'll use as an example throughout. 27 lines of code. Can we do better? public class Person { public Person(string name, DateTime birthday) { _name = name; _birthday = birthday; } private readonly string _name; private readonly DateTime _birthday; /// /// Full name /// public string Name { get { return _name; } } /// /// Birthday /// public DateTime Birthday { get { return _birthday; } } }

Slide 11

Slide 11 text

Here is a typical immutable class in C# which we'll use as an example throughout. 27 lines of code. Can we do better? Why not remove the lines that don't contain useful information? public class Person { public Person(string name, DateTime birthday) { _name = name; _birthday = birthday; } private readonly string _name; private readonly DateTime _birthday; /// /// Full name /// public string Name { get { return _name; } } /// /// Birthday /// public DateTime Birthday { get { return _birthday; } } }

Slide 12

Slide 12 text

In a typical C# project less than 50% of the lines contain useful information! Surely we can do better?

Slide 13

Slide 13 text

Proposal 1: Simplify doc strings

Slide 14

Slide 14 text

We can start by simplifying the doc strings. Let's make "summary" the default. public class Person { public Person(string name, DateTime birthday) { _name = name; _birthday = birthday; } private readonly string _name; private readonly DateTime _birthday; /// /// Full name /// public string Name { get { return _name; } } /// /// Birthday /// public DateTime Birthday { get { return _birthday; } } }

Slide 15

Slide 15 text

We can start by simplifying the doc strings. Let's make "summary" the default. public class Person { public Person(string name, DateTime birthday) { _name = name; _birthday = birthday; } private readonly string _name; private readonly DateTime _birthday; /// /// Full name /// public string Name { get { return _name; } } /// /// Birthday /// public DateTime Birthday { get { return _birthday; } } }

Slide 16

Slide 16 text

public class Person { public Person(string name, DateTime birthday) { _name = name; _birthday = birthday; } private readonly string _name; private readonly DateTime _birthday; /// Full name public string Name { get { return _name; } } /// Birthday public DateTime Birthday { get { return _birthday; } } } We can start by simplifying the doc strings. Let's make "summary" the default. 4 lines saved!

Slide 17

Slide 17 text

Proposal 2: Automatically create backing fields from constructor parameters Yes, I know C# 6 has something similar. Keep reading...

Slide 18

Slide 18 text

public class Person { public Person(string name, DateTime birthday) { _name = name; _birthday = birthday; } private readonly string _name; private readonly DateTime _birthday; /// Full name public string Name { get { return _name; } } /// Birthday public DateTime Birthday { get { return _birthday; } } } Because immutability is the default, we can simplify further.

Slide 19

Slide 19 text

public class Person { public Person(string name, DateTime birthday) { _name = name; _birthday = birthday; } private readonly string _name; private readonly DateTime _birthday; /// Full name public string Name { get { return _name; } } /// Birthday public DateTime Birthday { get { return _birthday; } } } Why not automatically define and initialize read- only backing fields from the constructor parameters?

Slide 20

Slide 20 text

public class Person { public Person(string name, DateTime birthday) { } /// Full name public string Name { get { return name; } } /// Birthday public DateTime Birthday { get { return birthday; } } } Of course, you can still define mutable private fields in the usual way. Unlike C# 6, you don't need to in the immutable case. 4 lines saved! Why not automatically define and initialize read-only backing fields from the constructor parameters?

Slide 21

Slide 21 text

Proposal 3: Merge the primary constructor with the class definition C# 6 has this too.

Slide 22

Slide 22 text

public class Person { public Person(string name, DateTime birthday) { } /// Full name public string Name { get { return name; } } /// Birthday public DateTime Birthday { get { return birthday; } } } How often do you have more than one constructor?

Slide 23

Slide 23 text

public class Person { public Person(string name, DateTime birthday) { } /// Full name public string Name { get { return name; } } /// Birthday public DateTime Birthday { get { return birthday; } } } How often do you have more than one constructor? Why not merge the constructor with the class definition?

Slide 24

Slide 24 text

public class Person(string name, DateTime birthday) { /// Full name public string Name { get { return name; } } /// Birthday public DateTime Birthday { get { return birthday; } } } How often do you have more than one constructor? 4 more lines saved! Why not merge the constructor with the class definition? You can still define secondary constructors separately if you need to.

Slide 25

Slide 25 text

Proposal 4: (this one is controversial)

Slide 26

Slide 26 text

Proposal 4: Use indentation instead of curly braces

Slide 27

Slide 27 text

public class Person(string name, DateTime birthday) { /// Full name public string Name { get { return name; } } /// Birthday public DateTime Birthday { get { return birthday; } } } Do we really need the braces? The indentation already gives us all the clues we need.

Slide 28

Slide 28 text

public class Person(string name, DateTime birthday) = /// Full name public string Name = get { return name; } /// Birthday public DateTime Birthday = get { return birthday; } Do we really need the braces? The indentation already gives us all the clues we need. 6 more lines saved! You can still have explicit begin/end markers for blocks if you need to.

Slide 29

Slide 29 text

public class Person(string name, DateTime birthday) = /// Full name public string Name = get { return name; } /// Birthday public DateTime Birthday = get { return birthday; } Add "equals" as an indicator to start a new block.

Slide 30

Slide 30 text

People who complain about using a language with syntactic whitespace People who have spent time using a language with syntactic whitespace "Oddly enough, Python's use of whitespace stopped feeling unnatural after about twenty minutes. I just indented code, pretty much as I would have done in a C program anyway, and it worked." - Eric Raymond Helpful Venn Diagram Overlap

Slide 31

Slide 31 text

Observation With these 4 proposals: 27 lines of code has shrunk to 9 lines!

Slide 32

Slide 32 text

C# Because "C# Light" means you see 3x more code on your screen! Why use "C# light"? Reason 1

Slide 33

Slide 33 text

public class Person { public Person(string name, DateTime birthday) { _name = name; _birthday = birthday; } private readonly string _name; private readonly DateTime _birthday; /// /// Full name /// public string Name { get { return _name; } } /// /// Birthday /// public DateTime Birthday { get { return _birthday; } } } public class Person(string name, DateTime birthday) = /// Full name public string Name = get { return name; } /// Birthday public DateTime Birthday = get { return birthday; } Why use "C# light"? C# C# Light Reason 2 Because "C# Light" means you write 1/3 as much code.

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

Proposal 5: Eliminate syntax noise There is a lot of syntax "noise" that is not needed, IMO.

Slide 36

Slide 36 text

Proposal 5a: Eliminate "get" keyword for immutable properties

Slide 37

Slide 37 text

public class Person(string name, DateTime birthday) = /// Full name public string Name = get { return name; } /// Birthday public DateTime Birthday = get { return birthday; } The class is immutable. Every property is "get" only.

Slide 38

Slide 38 text

public class Person(string name, DateTime birthday) = /// Full name public string Name = get { return name; } /// Birthday public DateTime Birthday = get { return birthday; } The class is immutable. Every property is "get" only. So why bother with the "get" keyword?

Slide 39

Slide 39 text

public class Person(string name, DateTime birthday) = /// Full name public string Name = return name; /// Birthday public DateTime Birthday = return birthday; The class is immutable. Every property is "get" only. So why bother with the "get" keyword? A bit cleaner, IMO.

Slide 40

Slide 40 text

Proposal 5b: Eliminate "return"

Slide 41

Slide 41 text

public class Person(string name, DateTime birthday) = /// Full name public string Name = return name; /// Birthday public DateTime Birthday = return birthday; Why not take a leaf out of other languages and make the return implicit for the last line in a block? This is the "expression-based" approach.

Slide 42

Slide 42 text

public class Person(string name, DateTime birthday) = /// Full name public string Name = name; /// Birthday public DateTime Birthday = birthday; Why not take a leaf out of other languages and make the return implicit for the last line in a block?

Slide 43

Slide 43 text

Proposal 5c: Make immutable properties public by default

Slide 44

Slide 44 text

public class Person(string name, DateTime birthday) = /// Full name public string Name = name; /// Birthday public DateTime Birthday = birthday; The properties are immutable.

Slide 45

Slide 45 text

public class Person(string name, DateTime birthday) = /// Full name public string Name = name; /// Birthday public DateTime Birthday = birthday; The properties are immutable. Why not make them public by default. There's no way a client of the object can corrupt it!

Slide 46

Slide 46 text

class Person(string name, DateTime birthday) = /// Full name string Name = name; /// Birthday DateTime Birthday = birthday; The properties are immutable. Why not make them public by default. There's no way a client of the object can corrupt it! You can still use the "private" keyword if you need to. Only the default has changed.

Slide 47

Slide 47 text

Proposal 5d: Make semicolons optional

Slide 48

Slide 48 text

class Person(string name, DateTime birthday) = /// Full name string Name = name; /// Birthday DateTime Birthday = birthday; How often do you have more than one semicolon on a line? Excepting for loops, of course.

Slide 49

Slide 49 text

class Person(string name, DateTime birthday) = /// Full name string Name = name /// Birthday DateTime Birthday = birthday How often do you have more than one semicolon on a line? So why bother?

Slide 50

Slide 50 text

Observation: Removing syntax noise opens up more opportunities to save space.

Slide 51

Slide 51 text

class Person(string name, DateTime birthday) = /// Full name string Name = name /// Birthday DateTime Birthday = birthday Why not move short code fragments on to the same line?

Slide 52

Slide 52 text

class Person(string name, DateTime birthday) = /// Full name string Name = name /// Birthday DateTime Birthday = birthday Why not move short code fragments on to the same line? C# 6 plans to have "expression- bodied members" too.

Slide 53

Slide 53 text

Proposal 6: Use type inference for properties

Slide 54

Slide 54 text

class Person(string name, DateTime birthday) = /// Full name string Name = name /// Birthday DateTime Birthday = birthday Why do we have to repeat the type? Can't the compiler figure it out for us?

Slide 55

Slide 55 text

class Person(string name, DateTime birthday) = /// Full name Name = name /// Birthday Birthday = birthday Why do we have to repeat the type? Can't the compiler figure it out for us? Cleaner, but now we can't tell that it's a property!

Slide 56

Slide 56 text

class Person(string name, DateTime birthday) = /// Full name member Name = name /// Birthday member Birthday = birthday We need some way to indicate properties. Let's use the word "member".

Slide 57

Slide 57 text

class Person(string name, DateTime birthday) = /// Full name member Name = name /// Birthday member Birthday = birthday /// Age member Age() = DateTime.Today.Subtract(birthday).Days / 365 We can define methods the same way.

Slide 58

Slide 58 text

class Person(string name, DateTime birthday) { /// Full name public string Name { get; } = name; /// Birthday public DateTime Birthday { get; } = birthday; /// Age public int Age() => DateTime.Today.Subtract(birthday).Days / 365; } Here is the C# 6 equivalent with auto-properties and expression-bodied members This is nice and compact too. Still need explicit types and "public" keyword, though.

Slide 59

Slide 59 text

Review

Slide 60

Slide 60 text

Let's review the proposals: 1) Make doc strings more compact 2) Move backing fields into constructor (because immutable) 3) Move constructor into class definition 4) Remove curly braces 5) Remove syntax noise - Remove "get" keyword from immutable properties - Return last value in a block automatically - Make immutable properties public by default - Make semicolons optional 6) Allow types of properties to be inferred from constructor

Slide 61

Slide 61 text

class Person(string name, DateTime birthday) = /// Full name member Name = name /// Birthday member Birthday = birthday public class Person { public Person(string name, DateTime birthday) { _name = name; _birthday = birthday; } private readonly string _name; private readonly DateTime _birthday; /// /// Full name /// public string Name { get { return _name; } } /// /// Birthday /// public DateTime Birthday { get { return _birthday; } } } Normal C# Before and After C# Light 27 lines before. 7 lines after.

Slide 62

Slide 62 text

No content

Slide 63

Slide 63 text

What about the goal "make common scenarios easy"?

Slide 64

Slide 64 text

The next set of proposals aims to do exactly this.

Slide 65

Slide 65 text

Proposal 7: Automatically generate code for equality

Slide 66

Slide 66 text

class Person(string name, DateTime birthday) : IEquatable = /// Full name member Name = name /// Birthday member Birthday = birthday override Equals(Person obj) = if (obj == null) return false Person p = obj as Person if ((Person)p == null) { return false } // Return true if the fields match: (name == p.Name) && (birthday == p.Birthday) override Equals(Person p) = if ((object)p == null) return false // Return true if the fields match: (name == p.Name) && (birthday == p.Birthday) override GetHashCode() = name.GetHashCode ^ birthday.GetHashCode How often do you have to write all this equality code?

Slide 67

Slide 67 text

[StructuralEquality] class Person(string name, DateTime birthday) = /// Full name member Name = name /// Birthday member Birthday = birthday Why not let the compiler write it for you! You just need to use a special attribute. How often do you have to write all this equality code?

Slide 68

Slide 68 text

Proposal 8: Compact syntax for DTOs

Slide 69

Slide 69 text

[StructuralEquality] class Person(string name, DateTime birthday) = /// Full name member Name = name /// Birthday member Birthday = birthday "Dumb" objects with no methods (aka DTOs) are very common. Since they have no methods, can we make the syntax even simpler?

Slide 70

Slide 70 text

[StructuralEquality] class Person(string name, DateTime birthday) = /// Full name member Name = name /// Birthday member Birthday = birthday "Dumb" objects with no methods (aka DTOs) are very common. Since they have no methods, can we make the syntax even simpler? class Person = {string name, DateTime birthday} var person = {name="Alice", birthday=Today} Yes, we can. Just define a named class in the same way as an anonymous type.

Slide 71

Slide 71 text

Proposal 9: Create non-nullable reference types

Slide 72

Slide 72 text

class Person = {string name, DateTime birthday} // ok var person = {name="Alice", birthday=Today} // error person = null This one is a no-brainer. If a class is defined with: • the [StructuralEquality] attribute • the anonymous type syntax then it is automatically non-nullable No more null testing needed!

Slide 73

Slide 73 text

Proposal 10: Allow anonymous types to implement interfaces

Slide 74

Slide 74 text

public class TempDisposable: IDisposable { public void Dispose() { Console.Write("Disposed"); } } var tempDisposable = new TempDisposable(); Sometimes you don't want to have to create a whole class just to implement an interface temporarily.

Slide 75

Slide 75 text

var tempDisposable = {new IDisposable with member this.Dispose() = Console.Write("Disposed") } Sometimes you don't want to have to create a whole class just to implement an interface temporarily. Why not use the "anonymous type" syntax to create an object without having to create a class explicitly?

Slide 76

Slide 76 text

Proposal 11: Allow subclasses to be merged into a single "case" class

Slide 77

Slide 77 text

Requirement: We accept three forms of payment: Cash. Check or Card. For Cash we don't need any extra information For Checks we need a check number For Cards we need a card type and card number This one needs a bit of background. Say that you have a requirement for taking payments, as shown below: How would you implement this?

Slide 78

Slide 78 text

interface IPaymentMethod {..} class Cash() : IPaymentMethod {..} class Check(int checkNo): IPaymentMethod {..} class Card(string cardType, string cardNo) : IPaymentMethod {..} You would probably implement it as an interface and a set of subclasses, like this:

Slide 79

Slide 79 text

interface IPaymentMethod {..} class Cash() : IPaymentMethod {..} class Check(int checkNo): IPaymentMethod {..} class Card(string cardType, string cardNo) : IPaymentMethod {..} But that is a lot of code for such a common scenario. Also... (a) The implementation and data is probably scattered around four separate files. (b) The requirements are hard to reconstruct from the classes. (c) The subclasses are not "closed". Any class that implemented the interface would work. This might not be what you want!

Slide 80

Slide 80 text

class PaymentMethod = | Cash | Check(int checkNo) | Card(string cardType, string cardNo) Answer: Create a "case" or "choice" class that encapsulates all three payment methods in one class, in one place. There are three different constructors, each with different data.

Slide 81

Slide 81 text

class PaymentMethod = | Cash | Check(int checkNo) | Card(string cardType, string cardNo) Answer: Create a "case" or "choice" class that encapsulates all three payment methods in one class, in one place. There are three different constructors, each with different data. PaymentMethod cash = Cash(); PaymentMethod check = Check(123); PaymentMethod card = Card("Visa", "4012888888881881"); The compiler keeps track of which constructor was used to create the instance.

Slide 82

Slide 82 text

Answer: Create a "case" or "choice" class that encapsulates all three payment methods in one class, in one place. void PrintPayment(payment) = switch (payment) { case Cash : // print cash case Check(checkNo) : // print check info case Card(cardType,cardNo) // print card info } A case statement is then used to match the subclass that was created, and at the same time extract the relevant data. class PaymentMethod = | Cash | Check(int checkNo) | Card(string cardType, string cardNo) PaymentMethod cash = Cash() PaymentMethod check = Check(123); PaymentMethod card = Card("Visa", "4012888888881881"); There are three different constructors, each with different data. The compiler keeps track of which constructor was used to create the instance.

Slide 83

Slide 83 text

Proposal 12: Require all properties to be initialized in the constructor

Slide 84

Slide 84 text

Avoid initialization errors. Require all properties to be initialized in the constructor. No more properties left as null by mistake! public class Person(string name, DateTime birthday) = /// Full name public string Name = get { return name; } /// Birthday public DateTime Birthday = get { return birthday; }

Slide 85

Slide 85 text

For example: Compiler error. "birthday" is missing. public class Person(string name) = /// Full name public string Name = get { return name; } /// Birthday public DateTime Birthday = get { return birthday; } Avoid initialization errors. Require all properties to be initialized in the constructor. No more properties left as null by mistake!

Slide 86

Slide 86 text

If they don't have to be initialized, you must make them optional. public class Person(string name, DateTime? birthday) = /// Full name public string Name = get { return name; } /// Birthday public DateTime? Birthday = get { return birthday; } Avoid initialization errors. Require all properties to be initialized in the constructor. No more properties left as null by mistake!

Slide 87

Slide 87 text

Excited by C# Light? Want it now?

Slide 88

Slide 88 text

But C# Light is available for download right now* C# v6 is not yet available *syntax not identical to that shown in this proposal.

Slide 89

Slide 89 text

Will C# Light work with legacy C# code? C# Light will be integrated with Visual Studio. Syntax highlighting, debugging support, and more. What tooling is available for C# Light? Of course. As with any .NET language, assemblies written in C# Light can be mixed with normal C# assemblies, and you can make calls between them.

Slide 90

Slide 90 text

How can I find out more about C# Light? Full details of C# light and how to download it, go to: bit.ly/csharp-light