Succinc<T> v2.0.0 release notes

! Warning: this post hasn't been updated in over three years and so may contain out of date information.

A few days ago, version 2.0.0 of Succinc<T> was released via nuget. As Succinc<T> follows the conventions of semantic versioning, the switch from 1.x to 2.0 is a clue that the release includes breaking changes. In addition though, a number of new features have been added. This release notes describes both the breaking changes and new features.

Quick note on the breaking changes

Throughout this release notes, I talk in terms of features having been removed, renamed or moved to new namespaces. In all cases, the feature has, in reality, been replicated. The old version has then been marked with the Obsolete attribute. I talk in terms of them having been removed etc as I’m sure nearly every user of Succinc<T> will set the “treat warnings as errors” flag in their projects. As such, your code won’t compile as soon as you install v2.0.0 of Succinc<T>.

If you don’t treat warnings as errors, then firstly, shame on you! You should. More seriously though, please be aware that you’ll get a number of warnings with v2.0.0 and, as the warnings advise, it’s my intention to remove all these obsolete features with v2.1.0.

For complete details on the breaking changes, please read this issue on GitHub

Pattern matching

The primary driver for this new version was a desire to rewrite the pattern matching functionality, as there were problems with the original implementation. My aim was to improve things behind the scenes, without breaking the API. The issues with the original code were:
1. The functionality was spread over 60+ classes. Many of these were marked public, with internal constructors, as they weren’t intended to be publicly accessible. They needed to be public though to make the method-chaining approach used by Succinc<T>’s pattern matching feature work.
2. Even though I wrote the code, I found it difficult to navigate around those 60+ classes to understand how that code worked. There was little chance of anyone else working it out therefore. This had to change.
3. The original version made heavy use of closures to capture values within the pattern. Closures are great, but they carry a heavy performance cost and really should only be used when necessary. Very few uses were really required, so it made sense to remove them where possible.

The new version now makes heavy use of explicit interfaces to restructure the code into around 40 interfaces and just 15 classes. As some classes now implement half a dozen or more interfaces, there was a trade off between performance and readability versus the Single Responsibility Principle, with me favouring the former over the latter. I think I’ve achieved a pragmatic balance though.

There is one breaking change risk to this rewrite: all of the classes are now internal and have changed name. So if you were using these classes in some “clever” way, your code will break horribly. In reality, I’m guessing (hoping) that there’s very little chance of this having happened. The API remains unchanged, and I didn’t have to change a single one of the unit tests for pattern matching, so any code that uses this feature properly should be unaffected.

Option<T>, Maybe<T> and None

The origins of Succinc<T> lie in the desire to have a type that can have a value, or a means of indicating no value (without using null). In trying to solve this, I started learning F# and came across the Option<T> type. So Succinc<T> has such a type.

Since then, it’s become apparent that the Haskell alternative – Maybe<T> – is popular in functional C# circles. I believe in going with the flow as much as possible, so if Maybe is the more common term in C#, then Succinc<T> ought to use that too. At the same time, this seemed like a cosmetic change that offered no practical benefits. Why create work for people in rewriting existing code just for a cosmetic change?

So I opted for a third approach: Succinc<T> now has both Option<T> and Maybe<T>. The latter is a struct that wraps an Option<T>. I’ve implemented implicit cast operators for both too. So the two can be used interchangeably … almost. The one thing I couldn’t get to work was equality between the two. If you want to compare an Option<T> and a Maybe<T> to see if they are equal, you must explicitly cast one to the other.

I’d welcome feedback on this change. Should I have just switched to Maybe<T>? Have I made things confusing by having both? Should I have just left things as they were? Also, if you know how I con fix the equality checks, please do let me know (or even better, send me a pull request!)

One other change related to Option<T> is to the None type. None is a value singleton; there is only ever one instance of None. This instance used to be accessible via None.Value. However, C# 6 provides static “usings”, which allow Value to be specified without the type name. Value isn’t at all descriptive, so has been replaced with none. This is a breaking change for anyone who was using None.Value. You’ll have to use none instead.

“Implicit” lambdas and converting between Func and Action

Have you ever had to write a line of code along the lines of:

and wished that C# supported implicitly typed lambdas? Well now with Succinc<T>, it does. The above line can be rewritten as:

Succinc<T> v2.0.0 provides a set of methods for typing lambdas (and method references): Lambda, Transform, Func and Action. For more details, please see Succinc typed lambdas guide.

On the subject of Func and Action, Succinc<T> now supports the means of converting between the two. To explain, C#’s history lie in imperative programming, where something is either a procedure (performs some side affect and doesn’t return anything), or a function (produces some result, which is returned from the function). Procedures in C# are marked as void, to denote they return nothing.

In functional languages though, everything is a function. Those functions that return “nothing”, return a special value (often called unit). Succinc<T> allows you to treat all methods and lambdas as functions, by wrapping Action delegates in a Func that returns unit. Further, as unit isn’t a useful return type, a new method void Ignore<T>(T) is provided, which will take any expression and discard the resultant value.

A contrived example of the use of these features is:

For more details, please see the Action/Function conversions guide.

Credit where credit is due time: these features where inspired by (and in some cases, directly copied from) the E247.Fun library from 247Entertainment. This functionality is replicated in Succinc<T> with their blessing and the licence and copyright notices reflect the origins of that functionality.

It’s all change for Succinc<T>’s parsers and XXXOrNone methods

At the risk of appearing a slave to the F# language, v2.0.0 sees another change to bring Succinc<T>’s method names in line with conventions used by that language: namely prefixing methods that return Option<T> with Try.

As a result, all the parser methods in Succinc<T> have changed from ParseInt, ParseBool etc to TryParseInt, TryParseBool etc.

Further, the IEnumerable<T> extension methods have changed from FirstOrNone, ElementAtOrNone etc to TryFirst, TryElementAt etc.

A full list of the name changes can be found in the v2.0.0 breaking changes documentation.

The partial application methods have moved (and some removed)

Due to adding new functional features to Succinc<T> (Unit, typed lambda support and Action/Func interoperability functions), it made sense to move the partial application methods from their SuccincT.PartialApplications namespace to the new SuccincT.Functional namespace that these other features occupy.

Because Succinc<T> now provides the means for typing lambdas and method references, there is no real need for the delegates and TailApply methods that handled methods with optional parameters. Instead, they can be typed via the Action method and the standard TailApply used instead. These features have therefore been removed.

Please see the v2.0.0 breaking changes documentation for more details.

And that pretty much wraps it up. Hopefully, the improved performance of pattern matching, and the new functional features, are big enough benefits to warrant spending a small amount of time modifying your code to work with v2.0.0.