Some notes on C# 3.0 (was RE: [DOTNET-CX] How to overload == for System.Double)

"Bart De Smet [MVP]" <[email protected]> Sun, 4 Feb 2007 00:54:46 +0100
Newsgroups gmane.comp.windows.devel.dotnet.cx
Message-ID <002d01c747ee$af13afc0$0d3b0f40$@net>
Hi folks,

Although this discussion is a bit off track since the C# 3.0 discussion
contributions, I'd like to address a few questions that have popped up.
(Note: If you have had prior exposure to C# 3.0 and LINQ you might consider
it a waste of time reading this post in much detail.)

> Now, in the huge push to implement LINQ in C# 3 it was deemed
> that C# wasn't flexible enough to implement what was needed.

Well, that's sort of right. All additions to the language are certainly
LINQ-supporting but on the other hand these have their uses outside the LINQ
story as well. Think of local type inference when dealing with "complex"
types (especially when composed from generics); think of anonymous types
when you don't bother of declaring a whole class in the first stages of
development (the tools will support to refactor an anonymous type to a
"named" type but means of a few clicks and auto code generation); think of
extension methods that other languages do have in other forms as well and
have proven useful out there; think of lambda functions which democratize
what has been seen as dark science for non-functional programmers; etc. On
the other hand, look at it in terms of evolution; a C# 2.0 feature called
iterators (which allow continuation style of execution allowing for lazy
evaluation) is literally at the basis of the LINQ-to-objects API so saying
that C# wasn't flexible enough seems a pretty big overstatement. Rather, you
can do (almost) everything that LINQ supports using C# 2.0 syntax:

void Bar()
{
    IEnumerable<string> expr = from s in names
                               where s.Length == 5
                               orderby s
                               select s.ToUpper();
}

becomes

void Bar()
{
   IEnumerable<string> t1 = Sequence.Where<string>(names, WherePredicate);
//C# 3.0: Sequence.Where<string>(names, s => s.Length == 5);
   IEnumerable<string> t2 = Sequence.OrderBy<string, string>(t1,
OrderByHelper); //C# 3.0: Sequence.Where<string>(t1, s => s);
   IEnumerable<string> expr = Sequence.Select<string, string>(t2,
SelectHelper); //C# 3.0: Sequence.Where<string>(t2, s => s.ToUpper());
}

bool WhereHelper(string s)
{
   return s.Length == 5;
}

string OrderByHelper(string s)
{
   return s;
}

string SelectHelper(string s)
{
   return s.ToUpper();
}

So, certainly the C# 3.0 features are for a big part "syntactical sugar"
which has for some people a bad connotation but one has to agree with the
fact it simplifies things. Okay, people will have to learn new syntax but it
pays of a bunch. LINQ isn't just about querying one sort of data source,
it's about unifying all sorts of data sources, from relational to
hierarchical data. Once you learn one (language integrated = strongly typed)
syntax, you can query virtually any type of data source, possibly joining
different data sources together seamlessly.

> They're--what I consider--deviating from that initial vision
> of simplicity and breaking their initial rules.

Well, simplicity becomes apparent even in the (at first sight frightening)
LINQ world when you compare it to all of the rabbits you have to pull out of
your magic .NET (or Java or C++ or ...) programmer's hat when looking at
today's data querying APIs. As Anders has stated many times already, where
do you still find any trace of the original simplicity (although implying
limitations as well) of dBase, Reflex, FoxPro? Somewhere in the dark ages of
programming, data and programming were so close together that we've said
some way or another it was too simple to be true, hence all of the (ever
becoming more complicated) data access APIs out there. Consider the
following:

using (SqlConnection conn = new SqlConnection(dsn))
{
   SqlCommand cmd = new SqlCommand(conn, "SELECT Name FROM Customers WHERE
Age > @Age");
   cmd.Parameters.Add("@Age", SqlDbType.Int);
   cmd.Parameters["@Age"].Value = age;
   conn.Open();
   SqlDataReader reader = cmd.ExecuteReader();
   while (reader.Read())
   { ... }
}

A lot of sacrifices have been made, such as compile-time checking of the SQL
statement (how could we do this, without ending up like C++'s printf
parameter validation by some compilers?) and strong-typedness. Rather, this
kind of APIs forces you to tell the system HOW you want something to be
done, rather than telling the system WHAT you want it to do. SQL has been in
the WHAT camp virtually since day one (not accounting for cursors), but the
wilderness of today's (so-called modern) data access APIs throw all of that
away to a big extent. Imagine the pain you're going through when you have to
join data from relational data (SQL) with data from hierarchical data
(XQuery, XPath) and put it in object (O/R + "O/X" mapping). LINQ fills that
gap and bridges all worlds together, further opening up for future
enhancements on various fields, including parallelizing (aka "PLINQ")
execution (which will be the next big thing in the world of multi-core
procs) by taking advantage of the expression-based ("WHAT") coding.

To conclude this point, simplicity has many faces and moving forward from a
statement-based execution (which, effectively, the CLR is all about and C#
initially provided a somewhat direct mapping between CLR features and higher
language constructs - contrast to, say, VB.NET due to the latter's
late-bounded character and helper library support) to a partial
expression-based execution with lazy evaluation, dynamic runtime expression
compilation and translation, adopting influences from the functional
programming world, etc can be seen as driving away from this level of
"simplicity", allowing for simplicity on another level. As usual, it's a
tradeoff and personally I think it will pay off looking at the productivity
gains (just to name one) it brings.

> That's further reinforced in the C# spec with telling comments like
> "...extension methods be used sparingly and only in
> situations where instance methods are not feasible or possible."

As usual, things can be misused but let me remind you that the current spec
isn't the final C# 3.0 spec yet. I agree that sentences like this can raise
a lot of questions and further practical do's and don't's have to become
apparent. Some time ago some bloggers (including me, see post of December 10
on my blog - link to be supplied further on) raised the question of possible
versioning issues with C# 3.0's extension methods so care has to be taken
when dealing with these constructs. Some even see a form of mixins in here
(which effectively is a MI thing so no straightforward CLR-based
implementation is feasible).

> I get the impression that the syntax is all
> proprietary MS extensions, and only useful for SQL or whatever MS decides
they want to
> enhance.  Bleah!

Nothing is proprietary in this except for the syntax, which is fixed
(wrapping a language designer's head around the exercise of allowing custom
language keyword and syntax additions would be a real challenge ;-)). As I
mentioned earlier, C# 3.0 is for a large part (very useful!) syntactical
sugar for developer convenience. Saying "where" means calling a Where
method, whether it's an instance method or an extension method. The same
holds true for all other keywords that map directly one some method. Third
parties are free to implement libraries that provide support for LINQ
against "whatever", like LINQ-to-Oracle, LINQ-to-DB2, LINQ-to-MySQL,
LINQ-to-AD, LINQ-to-My_favorite_WS, LINQ-to-WMI, LINQ-to-Outlook,
LINQ-to-WMP, LINQ-to-SharePoint, LINQ-to-iTunes, etc. It's exactly this
provider-based model that will contribute to the power of LINQ. Remember
that LINQ isn't just a compile-time thing, it's also about building
expression trees that can be translated into domain-specific query languages
at run-time (which is what for instance LINQ-to-SQL does). I'm planning to
post a sample on my blog pretty soon on LINQ-to-AD (LDAP) to illustrate
this. In the meantime read my musings on C# 3.0 over here:

* C# 3.0 posts:
http://community.bartdesmet.net/blogs/bart/archive/tags/C_2300_+3.0/default.
aspx
* LINQ-SQO CodePlex project: http://www.codeplex.com/LINQSQO

If you have further questions or points of discussion around C# 3.0, feel
free to drop me a mail or put it on the discussion groups (for which a new
thread might be more appropriate).

-Bart

-----Original Message-----
From: Discussion relating to the specifics of the C# and Managed C++
languages [mailto:[email protected]] On Behalf Of Alan Baljeu
Sent: vrijdag 2 februari 2007 19:26
To: [email protected]
Subject: Re: [DOTNET-CX] How to overload == for System.Double

> -----Original Message-----
> From: Discussion relating to the specifics of the C# and
> Managed C++ languages [mailto:[email protected]]
> On Behalf Of Peter Ritchie
> Sent: Friday, February 02, 2007 12:02 PM
> To: [email protected]
> Subject: Re: [DOTNET-CX] How to overload == for System.Double
>
> C# has deep roots in C++ (C# is a form of C++++).  There was
> a conscious effort in the design of C# in the beginning to
> break free of the non-OO legacy attributes of C++ (as it has
> deep roots in C).  A very powerful feature of C++ is
> nonmember functions that facilitate much of the abilities
> that extension functions give you.  There was an explicit
> decision to not include a feature like nonmember functions in
> C# because it didn't fit within the vision of the C# language
> (at the time) and wasn't considered safe, truly object oriented, etc.
>
> Now, in the huge push to implement LINQ in C# 3 it was deemed
> that C# wasn't flexible enough to implement what was needed.
> They're--what I consider--deviating from that initial vision
> of simplicity and breaking their initial rules.  That's
> further reinforced in the C# spec with telling comments like
> "...extension methods be used sparingly and only in
> situations where instance methods are not feasible or possible."
>
I just read the Linq overview.  I like some of what I see, but this just
baffles me:

    IEnumerable<string> expr = from s in names
                               where s.Length == 5
                               orderby s
                               select s.ToUpper();

Since when did this remotely resemble C#?  I get the impression that the
syntax is all
proprietary MS extensions, and only useful for SQL or whatever MS decides
they want to
enhance.  Bleah!

===================================
This list is hosted by DevelopMentor.  http://www.develop.com

View archives and manage your subscription(s) at http://discuss.develop.com

===================================
This list is hosted by DevelopMentorĀ®  http://www.develop.com

View archives and manage your subscription(s) at http://discuss.develop.com