Filed under: F#,Informatik — Steffen Forkmann at 17:43 Uhr
Triggered by a short tweet by Tomas Petricek and a blog post by Don Syme I had a couple of conservations about null as a value, Null as a type (Nothing in Scala), the null object pattern and the Maybe monad (or Option type in F#). I decided to share my opinion on this topic in order to help others to make their own opinion.
Part I – Null as a value
Unfortunately most programming languages have some kind of null pointer. We are now even at a point where some people claim that Tony Hoare’s famous billion dollar mistake costs actually a billion dollar per year.
But before we start to think about solutions to the null pointer problem let us first see where null values might come in handy.
Uninitialized values
In a statement oriented language like C# it is sometimes not that easy to represent uninitialized state.
As you can see we forgot to initialize peter in the missing "else-branch" which might lead to a NullReferenceException. There are a couple of things we can do to prevent this from happening. First of all we should omit the explicit initialization with null:
In this case the C# compiler throws an error telling us "Use of unassigned local variable ‘peter’".
There is no way to forget the else branch since the types don’t match.
Unknown values
Consider a search function which tries to find a Person in a list:
Unknown values are probably the most common cause for the introduction of nulls and in contrast to uninitialized values they have much more global effects.
Solutions to unknown values
1. Using explicit exceptions
One easy solution to get rid of the nulls is to make the exception explicit:
One benefit here is that we have a concrete exception which is telling us what went wrong. Unfortunately nothing forces us to handle this exception in the calling method. This means the program can still crash. Another problem is that if we use try/catch as a control flow statement (i.e. use it very very often) then we might slow down our program.
And there is still a philosophical question: is it really an exception if we don’t have a person in our repository?
2. Using out parameters and returning bools
If we want to make the result of our search a little more explicit we can also introduce a boolean return value:
This is a pattern which is actually used everywhere in the .NET framework, so it must be good right?!
Just a simple question here: What did we gain exactly? If we forget to check for nulls why would we remember to check the bool?
There is another interesting observation here. Due to the introduction of out parameters we also introduce the uninitialized value problem in every calling method, which is bizarre.
3. Null object pattern
“In object-oriented computer programming, a Null Object is an object with defined neutral ("null") behavior. The Null Object design pattern describes the uses of such objects and their behavior (or lack thereof).”
So obviously this makes only sense in a context where objects have observable behavior. So let’s add some behavior to our Person class:
Now we can use this in the search method:
Let’s look at this solution for a moment.
Pro: Implementation of missing behavior is trivial
Pro: We don’t introduce null values –> we won’t get NullReferenceExceptions
Pro: The name of the class carries information about the reason for the null value
Cons: In order to to get rid of the Person(string name, int age) constructor in the null-object we had to introduce an interface
Cons: In order to use the interface we had to modify the Person class. This might be a real problem if we don’t have access to the source. We would need some kind of ad-hoc polymorphism like Haskell’s type classes, Scala’s Traits or Clojure’s Protocols (read more about the expression problem)
Cons: Everything we want to do with Persons has to be part of the IPerson interface or we have to fall back to explicit runtime type tests, because there is no way to get to the data:
Cons: Without ad-hoc polymorphism the Person class is getting more and more complex over time since we are adding all the behavior to it. Compare it with our starting point where it was a simple data representation. We totally lost that.
Cons: Errors/bugs might appear as normal program execution. [see Fowler, Martin (1999). Refactoring pp. 261]
Don’t use this approach it’s really not a good solution.
4. The Option type (a.k.a. Maybe monad)
The option type is a way to push the idea from 2. “returning bools” into the type system. There are lots of tutorials about the option type on the web, but for now it’s enough to know that it is something which is based on the following idea:
In order to allow pattern matching in C# we introduce a small helper:
With this idea we can get rid of all null values in the program, we don’t have to introduce an IPerson interface, the null case is type checked and the best thing is it’s really easy to create functions like IsOlderThan12:
Of course this is only the tip of the iceberg. If you read more about the option type you will see that its monadic behavior allows all kinds of awesome applications (e.g. LINQ, folds, map, …).
5. The Either type
One of the benefits of the null object pattern above was that it allows to encode the reason for the missing value. In some cases we need the same thing for the maybe monad. So let’s try to capture the reason in the None case:
Now we can use this in the TryFindPerson method:
As with the option type please don’t rewrite this stuff yourself. There is a Choice type in F# which helps to capture this idea and Mauricio has a couple of blog posts which explain how to use it from C# (of course FSharpx is helping here again).
6. The (singleton) list monad
The option type captures the idea that we can either have one value or nothing. We can do the same thing with List<T>:
Of course this uses only a small subset of the power of IEnumerable<T> but I wanted to show the similarity to the maybe monad. You can read more about the “poor man’s option type” on Mauricio’s blog.
Conclusion
We saw a couple of very different solutions to get rid of null values. Normally I’d only use the option type or the either type (if I need to carry the reason). In some very rare situations (e.g. C# only projects) I would also use singleton lists.
I wouldn’t use the other solutions. They are potentially dangerous, especially if your project gets bigger.
I’m happy to annouce a new nuget release of FSharpx.TypeProviders. The new version 1.5.3 allows to access JSON and XML in a strongly typed way. These new type providers are based on an XML type provider by Tomas Petricek. I added a write API and a JSON version.
Idea
Whereas XML allows different ways to define a schema, JSON has no schema. Nevertheless most documents have some kind of an implicitly defined schema. These new type providers try to infer the schema from a sample file and provide you types to access any file which is conform to this schema.
Let’s add this file to a new F# solution and reference the FSharpx.TypeProviders.dll via install-package FSharpx.TypeProviders. Enable the type provider in the following dialog:
Now that we have a sample data and the type provider ready we get full Intellisense:
In most cases you want to use the initial document only as a sample, therefore it’s also possible to load a different document at runtime which the type provider will treat with the same schema:
It’s even possible to modify properties:
Accessing XML
After adding a reference to System.Xml.dll and System.Xml.Linq.dll it’s also very easy to access XML. Since both type providers have the same features we will use yet another way to provide a schema sample. In this case we give the schema inlined:
Schema invalidation
One important aspect of type providers is the schema invalidation. For instance try to remove the line with the age property of WikiSample.txt and save the file. The type provider will detect this schema change and your code will no longer compile:
Filed under: C#,F# — Steffen Forkmann at 11:08 Uhr
I recently had the problem to register a global hotkey, but my “old” Win32-API calls didn’t work with WPF. I looked around the web and found the “Managed Windows API”, but I didn’t want to add another external dependency to my project, so I extracted the core functions for registering hotkeys and condensed the code to a new version.
As the original “Managed Windows API” is licensed by the GNU Lesser General Public License (LGPL) I want to provide my modifications here.
First we need a Window which can dispatch the window messages to our event handlers:
///<summary>
/// A Win32 native window that delegates window messages to
///handlers. So several
/// components can use the same native window to save
Filed under: English posts,F# — Steffen Forkmann at 16:02 Uhr
One of the nice properties of functional programming languages is the easy extensibility of custom functions. Let’s consider a simple F# function (from “FAKE – F# Make”) for a recursive directory copy:
open System
open System.IO
/// Copies a directory recursive
/// Thanks to Robert Pickering http://strangelights.com/blog/
/// param target: target directory : string
/// param source: source directory : string
let CopyDir target source =
Directory.GetFiles(source, "*.*", SearchOption.AllDirectories)
|> Seq.iter (fun file ->
let newFile = target + file.Remove(0, source.Length)
printf "%s => %s" file newFile
Directory.CreateDirectory(Path.GetDirectoryName(newFile)) |> ignore
File.Copy(file, newFile, true))
If we want to allow users to set custom file filters, we can add a third parameter:
/// Exclude SVN files (path with .svn)
/// excludeSVNFiles: string -> boollet excludeSVNFiles (path:string) = not <| path.Contains ".svn"
/// Includes all files
/// allFiles: string -> boollet allFiles (path:string) = true
Now it is possible to use CopyDirFiltered in the following ways:
/// Copies all files <=> same as CopyDir
CopyDirFiltered "C:\\target" "C:\\source" allFiles
/// Copies all files except SVN files
CopyDirFiltered "C:\\target" "C:\\source" excludeSVNFiles
/// Copies all files only if random number <> 2
let r = new Random()
CopyDirFiltered "C:\\target" "C:\\source" (fun path -> r.Next(5) <> 2)
Extensibility of functions in C#
Of course we can do the same thing in C# 3.0:
/// <summary>
/// Copies a directory recursive
/// and allows to filter the files
/// </summary>
/// <param name="target">The target.</param>
/// <param name="source">The source.</param>
/// <param name="fileFilter">The file filter.</param>
public static void CopyDirFiltered(string target, string source,
Func<string, bool> fileFilter)
{
string[] allFiles = Directory.GetFiles(
source, "*.*", SearchOption.AllDirectories);
foreach (string file in from f in allFiles
where fileFilter(f)
select f)
{
string newFile = target + file.Remove(0, source.Length);
Console.WriteLine("{0} => {1}", file, newFile);
Directory.CreateDirectory(Path.GetDirectoryName(newFile));
File.Copy(file, newFile, true);
}
}
Now it is easy to use the C# function with lambdas:
“A lambda expression is an anonymous function that can contain expressions and statements, and can be used to create delegates or expression tree types.”
Func<string, bool> filterSVN = x => !x.Contains(".svn");
Func<string, bool> allFiles = x => true;
/// Copies all files <=> same as CopyDir
CopyDirFiltered("C:\\target", "C:\\source", allFiles);
/// Copies all files except SVN files
CopyDirFiltered("C:\\target", "C:\\source", filterSVN);
/// Copies all files only if random number <> 2
var r = new Random();
CopyDirFiltered("C:\\target", "C:\\source", path => r.Next(5) != 2);
Keeping this simple technique in mind allows to create very flexible functions.
Claus Lundstrøm zeigt in einem schönen Blogpost wie man in NAV2009 den Code auf Seite der ServiceTier (also auch remote) debuggen kann – und zwar über Visual Studio 2008 direkt im generierten C#-Code. Mit dieser Variante ist man nicht mehr gezwungen das Debugging über den Classic-Client zu tun, sondern kann direkt aus dem Dynamics NAV RoleTailored-Client debuggen.
Dummerweise ist der generierte C#-Code, wie das bei generiertem Code eigentlich immer der Fall ist, nicht gerade “optisch schöner” C#-Style und hat auch nur noch wenig mit dem Original-C/AL-Code zu tun – ist aber immerhin lesbar.
Das ist ein wirklich interessanter Ansatz und erlaubt mit etwas Geschick auch UnitTesting für NAV 2009. Dafür werde ich demnächst mal versuchen ein kleines Beispiel zu bloggen.
Florian Mätschke hat soeben seine auf der BASTA! 2008 in Mainz angekündigte Umfrage auf seinem Blog veröffentlicht. Dabei wurden die BASTA!-Speaker zu der Technologie befragt, die sie im Moment am meisten fasziniert. “Gewinner” ist übrigens Silverlight 2 geworden, dicht gefolgt von funktionaler Programmierung (in F# bzw. LINQ) – wofür ich mich übrigens auch entschieden habe.
Insgesamt ist das Umfrageergebnis, wie für die BASTA! zu erwarten war, sehr .NET-lastig. Obwohl auf der Abendveranstaltung noch Technologien wie Waschmaschine und Auto als faszinierend erachtet wurden, haben sich die meisten Speaker schlussendlich für ihr Vortragsthema im weitesten Sinne entschieden.
Ich muss sagen, dass ich das Konzept der Umfrage sehr interessant finde. Das Problem ist nur, dass man z.B. auf einer Java-Konferenz natürlich vollkommen konträre Ergebnisse erzielt. Um die wirklichen “Technologie Highlights“ zu ermitteln müsste man die Umfrage selbstverständlich viel größer und anonymisiert anlegen.
Microsoft hat am 31.3.2008 auf PartnerSource die erste Version von Dynamics Mobile veröffentlicht. Dabei handelt es sich primär um ein auf .NET basierendes Architekturkonzept mit dem man Mobile Endgeräte (mit Windows Mobile 5.0 oder 6.0) an ERP-Systeme (Dynamics NAV oder AX) anbinden kann. Weiterhin wird mit “Mobile Sales” jedoch eine umfangreiche Beispielanwendung mitgeliefert.
ReSharper ist ein Plugin der Firma JetBrains für Visual Studio, das sich speziell die Produktivitätssteigerung beim Entwickeln als Ziel gesetzt hat. Besonderes Augenmerk wird dabei auf die Refactoring-Unterstüzung gelegt, also auf das nachträgliche Umgestalten von Quellcode. JetBrains wirbt damit das “intelligenteste AddIn für Visual Studio” entwickelt zu haben – doch was kann ReSharper wirklich?
Am 06.07.2007 findet ab 19:30 ein sehr interessantes Event in Leipzig statt. Roland Weigelt wird über die Entwicklung seines mehrfach ausgezeichneten Plugins GhostDoc berichten und einen Einblick in die Visual Studio Erweiterbarkeit geben. GhostDoc ist für mich neben ReSharper das wichtigste Plugin für Visual Studio überhaupt.
Der Vortragende wird folgende Features vorstellen: