I've spent a fair bit of time during the past few weeks learning some of the ins and outs of both C# and Haskell. My experience with C# has mostly been ho-hum; There haven't been too many interesting language features that perk me up. Often, I'm left wondering why they've added certain features which often seem redundant and add unnecessary syntax to the language. However, I haven't spent much time doing concurrent programming in the C#, so hopefully I'll have some interesting things to report in that regard next week when I plan on doing a pretty thorough review.
OTOH, Haskell has been a pleasant surprise. I've had some minimal exposure to Haskell due to reading about functional programming (Haskell is often used as a teaching language for FP concepts), so I knew that Lisp had quite an influence on the design of the language. What I didn't realize is the degree to which Haskell borrowed concepts from logic programming (probably Prolog). I also didn't expect some of these concepts to be relevant to a type of programming problem that I commented on recently in the forums. Let's start with a simple example.
Let's suppose that you work for a logistics company and you've been assigned the task of implementing a system which tracks the position of both land and air vehicles. The obvious solution is to use a 3D vector for airplanes and a 2D vector for land vehicles. For the moment we'll assume that we're only working on representing the location of the vehicles and not worry about other details. The issue that I've seen arise time and time again (eg satellite positions, distances between words/groups of words, actually pretty much anything dealing with a metric space and/or distances) is that you're going to want to abstract the 2D and 3D information into some kind of high-level data structure (perhaps a Vector2D and Vector3D classes which are used in the curLoc and destLoc and other fields of Vehicle and/or Route classes). The problem is that you often need to 'destructure' the custom data type in order to use the values in a mathematical formula or function (especially when dealing with a library). Frequently this results in somewhat fugly looking code and I often end up creating two versions of each function (eg an x1,y1,x2,y2 parameter version and a vec1, vec2 parameter version that does the destructuring) which helps reduce errors and improves maintainability at the cost of redundancy (and some minimal overhead). This was pretty evident in the Haversine formula
at Rosetta Code. Nearly all of the imperative languages elected to skip placing the location of the airports into something like a 2-tuple and merely called a haversine function using hard-coded values.
What does Haskell offer the programmer to address this issue? Haskell makes use of a concept called pattern matching which you might recall from Prolog. In Haskell, this is generally called a pattern. To demonstrate the use of patterns, we'll start by extracting the altitude of an aircraft from a 3-tuple which contains the x,y,z location information (Obviously, the true altitude of an aircraft would require additional steps, but I want to keep the focus on pattern matching and not mathematical formulas). Below, I've defined a function called getAltitude which accepts a 3-tuple as input [ie the (_,_,z) part]; However, I'm not interested in the first two values in the tuple so I use the underscore to ignore them (anyone remember this from Prolog?) and match or assign the third position in the tuple to z which I then return.
Prelude> let getAltitude (_,_,z) = z
Prelude> getAltitude (101.7, -30.3, 25000)
Below I use pattern matching to add, subtract and calculate the dot product of 2d vectors using pattern matching. Notice how elegant this implementation is compared to having to implement two versions of the function (or ever worse keep all the data in variables or arrays).
Prelude> let add2d (x1,y1) (x2,y2) = (x1+x2,y1+y2)
Prelude> let sub2d (x1,y1) (x2,y2) = (x1-x2,y1-y2)
Prelude> let dotProd2d (x1,y1) (x2,y2) = (x1*x2) + (y1*y2)
Prelude> let loc0 = (0,0) ; loc1 = (4,3) ; loc2 = (4,10)
Prelude> let v0 = (0,0) ; v1 = (4,3) ; v2 = (4,10)
Prelude> v0 `add2d` v1
Prelude> add2d v0 v1
This language feature is used frequently in Haskell. Below is another example which recursively calculates the sum of the elements of a list (note: a list in Haskell is placed inside of square brackets; Also, the ' at the end of the function is often used to denote a similar function in Haskell). Notice that the first pattern matches the empty list, and therefore, end the recursion when it returns 0.
Prelude> let sum'  = 0 ; sum' (first:rest) = first + sum' rest
Prelude> sum' [1, 2, 3]
Prelude> sum' [2,4..10]
I'll post some more interesting Haskell examples when I get a chance. Haskell is a pretty interesting language and I would highly encourage people to spend some time with it.