Introducing LINQ (3) Programming Paradigms
[LINQ via C#] - [Introducing LINQ]
Programming paradigm is the fundamental style of programming.
Imperative vs. declarative
LINQ
This is the fore mentioned example of collection processing:
public partial class Imperative { public static List<Person> FilterAndOrderByAge(IEnumerable<Person> source) { List<Person> results = new List<Person>(); foreach (Person person in source) { if (person.Age >= 18) { results.Add(person); } } Comparison<Person> personComparison = delegate(Person a, Person b) { int ageComparison = 0 - a.Age.CompareTo(b.Age); return ageComparison != 0 ? ageComparison : string.Compare(a.Name, b.Name, StringComparison.Ordinal); }; results.Sort(personComparison); return results; } }
and the same processing with LINQ:
public partial class LinqToObjects { public static IEnumerable<Person> FilterAndOrderByAge(IEnumerable<Person> source) { return from person in source where person.Age >= 18 orderby person.Age descending, person.Name select person; } }
Their styles/paradigms are very different:
- The first method has a imperative style/paradigm: it focuses on how to implement the query by providing the explicit steps of the algorithm.
- The second method has declarative style/paradigm: it declares what is the query, like "orderby person.Age descending". It is abstract. It does not provide the steps of the sorting algorithm.
Imperative and declarative programming paradigms are different philosophies:
- Imperative paradigm is about thinking from bottom up. It explicitly provides each action to be taken, and a sequence of action can be a “bigger” action, and so on. Computation is to execute these actions.
- Object oriented programming of C# is a typical imperative paradigm.
- Declarative paradigm is about thinking from top down. It is higher level, more abstract, has clear correspondence to mathematical logic, where can be considered as theories of a formal logic, and computations can be considered as deductions in that logic space. As a higher level and more abstract paradigm, it usually minimize or eliminate side effects.
- Functional programming is a typical declarative paradigm, and it is the major topic of LINQ.
SQL
As fore mentioned, LINQ is SQL-like. The following SQL query is very declarative:
SELECT [ProductName], [UnitPrice] FROM [Products] ORDER BY [UnitPrice] DESC
XAML
Another declarative example is XAML. Compare C#:
Button button = new Button(); button.Content = "Submit"; button.HorizontalAlignment = HorizontalAlignment.Left; button.VerticalAlignment = VerticalAlignment.Top; button.Margin = new Thickness(10, 10, 0, 0); button.Width = 75; button.Click += this.Submit;
with the following XAML:
<Button Content="Submit" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10,10,0,0" Width="75" Click="Submit" />
Above C# is imperative and above XAML is declarative.
HTML
Another controversial topic is HTML. In CLR via C# 2nd edition, Jeffrey Richter said (This paragraph is removed in the 3rd edition),
An example of declarative programming is when a person creates a text file and explicitly enters HTML tags into the file by using an editor such as Notepad.exe. In this scenario, the HTML tags act as instructions that are eventually processed by the Internet browser so that it can lay out the page in a window. The HTML tags are declaring how the program (Web page) should be displayed and operate, and it's the programmer who decides what tags to use and where. Many hard-core programmers don't consider HTML programming to be real programming, but I do.
Similar to C# vs. XAML, if comparing with JavaScript:
var img = document.CreateElement("img"); img.src = "https://farm3.staticflickr.com/2875/9215169916_f8fa57c3da_b.jpg"; img.style.width = "300px"; img.style.height = "200px"; img.title = "Microsoft Way";
with HTML:
<img src="https://farm3.staticflickr.com/2875/9215169916_f8fa57c3da_b.jpg" style="width: 300px; height: 200px;" title="Microsoft Way" />
then HTML is the declarative.
Programming paradigms and languages
Imperative, declarative, object-oriented, functional, ... There are many paradigms for programming. The common paradigms can be categorized as:
- Declarative programming
- Event-driven programming
- Generic programming
- Imperative programming
- Metaprogramming
- Parallel programming
- Structured programming
- ...
One programming language can adopt several paradigms. Take C as an example:
- C is typically used as procedural;
- It can also be used in object-oriented programming.
Another example is JavaScript:
- JavaScript is imperative by default, it is
- Procedural
- Prototype-based
- It is also elegantly functional
And finally, C# is:
- Declarative (attribute, regular expression, data annotation, code contracts, …)
- Reactive (Rx)
- Functional (lambda expression, higher-order function, LINQ, …)
- Event-driven (event)
- Generic
- Imperative (by default)
- Class-based Object-oriented (class)
- Procedural (static class, static method, using static)
- Metaprogramming (code DOM, expression tree, IL emit, compiler as a service)
- Reflective (reflection)
- Parallel (TPL, Parallel LINQ)
- Structured
- Aspect-oriented (Unity)
Thanks to Microsoft and Anders Hejlsberg, C#/.NET is powerful and productive, work in many different scenarios.
Declarative C#
C# 3.0+ introduced a lot of syntax to make it more declarative. For example, the object initializer collection initializer:
List<Person> team = new List<Person>(); Person anna = new Person(); anna.Age = 25; anna.Name = "Anna"; team.Add(anna); Person bob = new Person(); bob.Age = 30; bob.Name = "Bob"; team.Add(bob); Person charlie = new Person(); charlie.Age = 35; charlie.Name = "Charlie"; team.Add(charlie); Person dixin = new Person(); dixin.Age = 30; dixin.Name = "Dixin"; team.Add(charlie);
Comparing to:
List<Person> team = new List<Person> { new Person() { Age = 25, Name = "Anna" }, new Person() { Age = 30, Name = "Bob" }, new Person() { Age = 35, Name = "Charlie" }, new Person() { Age = 30, Name = "Dixin" }, };
the first code snippet is more imperative, and the second is more declarative. Actually, there are many other declarative aspects in C# programming.
Attribute
Actually, declarative programming in C# is not something brand new. C# has attributes from the beginning:
[HandleError] public class HomeController : Controller { [HttpGet] public ActionResult Index() { return this.View(); } }
Regular expression
Regular expressions can be considered declarative:
namespace System.ComponentModel.DataAnnotations { using System.Text.RegularExpressions; [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] public sealed class EmailAddressAttribute : DataTypeAttribute { private static readonly Regex emailRegex = new Regex( "^((([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(\\\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)+(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.?$", RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture | RegexOptions.Compiled); public EmailAddressAttribute() : base(DataType.EmailAddress) { this.ErrorMessage = DataAnnotationsResources.EmailAddressAttribute_Invalid; } public override bool IsValid(object value) { if (value == null) { return true; } string text = value as string; return text != null && emailRegex.Match(text).Length > 0; } } }
Data annotation
Data Annotation is intuitively declarative:
public class Person { [Required(ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = nameof(Resources.NameRequired))] [StringLength(1, ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = nameof(Resources.InvalidName))] public string Name { get; set; } [Required(ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = nameof(Resources.AgeRequired))] [Range(0, 123, ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = nameof(Resources.InvalidAge))] // https://en.wikipedia.org/wiki/Oldest_people public int Age { get; set; } [EmailAddress(ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = nameof(Resources.InvalidEmail))] public string Email { get; set; } }
Code contracts
C# 3.0 and 4.0 introduced code contracts, which is also declarative:
public class Person { private readonly string name; private readonly int age; public Person(string name, int age) { Contract.Requires<ArgumentNullException>(!string.IsNullOrWhiteSpace(name)); Contract.Requires<ArgumentOutOfRangeException>(age >= 0); this.name = name; this.age = age; } public string Name { [Pure] get { Contract.Ensures(!string.IsNullOrWhiteSpace(Contract.Result<string>())); return this.name; } } public int Age { [Pure] get { Contract.Ensures(Contract.Result<int>() >= 0); return this.age; } } }
LINQ and Functional C#
Above LinqToObjects.FilterAndOrderByAge method implementation is equivalent to (actually is compiled to):
public partial class LinqToObjects { public static IEnumerable<Person> FilterAndOrderByAge(IEnumerable<Person> source) { return source .Where(person => person.Age >= 18) .OrderByDescending(person => person.Age) .ThenBy(person => person.Name); } }
This LINQ to Objects program is functional, and purely functional:
- Type inference
- Extension method
- Lambda expression as anonymous function
- Higher order function
- Query expression/Query method
Since C# 3.0, more and more language features has been added to C#, which make C# more and more functional. Besides above features, there are more:
- Auto property, auto property initializer, getter only auto property
- Object initializer, collection initializer, index initializer, extension Add in collection initializers
- Anonymous type
- Partial class, partial interface, partial method
- Lambda expression as expression tree, expression-bodied members
- Async lambda expression
- Covariance and contravariance
Next, these language features will be explained in detail.