21 August 2008

Function overloading in JavaScript

Trying to write a function so that it will handle different numbers and types of argument always seems to take a lot more code that it should. Today I ended up with some nightmarish if...else... because I wanted to support:

  • Object, Object
  • Object, String
  • Object, Object, String

I remembered a post by John Resig from a while back on this, however his method doesn't handle different types. A quick Google found this snippet which does the job but seems a bit clunky, converting constructors to strings and the like. Satisfied that it could probably be improved upon here is my go at it.

Implementation

Here an Impl class holds the different method implementations against their signatures, defined as an array of constructors. The add method maps a new signature to an implementation, the exec method identifies the correct implementation for the current arguments and executes it and the compile method returns a function that can be used to overwrite the "daddy" function.

function Impl(){
 var _impl = {};
 return {
  add: function(aSignature, fImpl){
   _impl[aSignature] = fImpl;
  },
  exec: function(args, scope){
   var aArgs = Array.prototype.slice.call(args);
   var aCtors = [];
   for(var i in aArgs) aCtors.push(aArgs[i].constructor);
   return (_impl[aCtors] || function(){}).apply(scope, aArgs);
  },
  compile: function(){
   var impl = this;
   return function(){
    return impl.exec(arguments, this);
   };
  }
 };
}

Usage

Here's an example setup of a function stringify that will take two strings, two numbers or an array and do different things depending on what arguments are passed.

function stringify(){
 var someVar = 'Items: ';
 var impl = new Impl();
 
 impl.add([String, String], function(sLeft, sRight){
  return sLeft + ': ' + sRight;
 });
 impl.add([Number, Number], function(iLeft, iRight){
  return 'Sum: ' + (iLeft + iRight).toString();
 });
 impl.add([Array], function(aIn){
  return someVar + aIn.join(', ');
 });
 
 stringify = impl.compile();
 return stringify.apply(this, arguments);
}

Each implementation is added with an array of constructors e.g. [Number, Number] representing the arguments signature to match and the implementation function. Notice that we don't need to do any type checking of the variables in the implementation function, as we have to have matched the signature to get that far, and we can give then relevant names.

Notice the last two lines are:

stringify = impl.compile();
return stringify.apply(this, arguments);

So on the first execution of the function it is overwritten with a "compiled" version and then that version is called on the next line. Subsequent calls to the function will go straight to the "compiled" version thus speeding things up. This could also be done without the compilation and overwriting bit by just calling exec directly thus:

return impl.exec(arguments, this);

Calling our function

stringify('foo', 'bar'); // => 'foo: bar'
stringify(26, 5); // => 'Sum: 31'
stringify( [ 1, 2, 3 ] ); // => 'Items: 1, 2, 3'

Our different numbers and types of argument are giving us the output we'd expect.

Excluding particular derived types in NHibernate queries

Today I came across an NHibernate problem where I needed to select every instance of a particular base type and all its derived types from a database, apart from one particular derived type. Here is a trivial example:

public class Mammal {}
public class Dog : Mammal {}
public class Cat : Mammal {}
public class DomesticCat : Cat {}

In this case the problem was equivalent to selecting every mammal that isn't a domestic cat.

We're using the table per class hierarchy inheritance model in NHibernate which uses values in a discriminator column to determine which type is held in a particular row of the table.

Selecting the whole hierarchy is done like this:

In HQL:
IQuery q = sess.CreateQuery("from Mammal");
IList mammals = q.List();
In Criteria:
ICriteria crit = sess.CreateCriteria(typeof(Mammal));
List mammals = crit.List();

I then needed to be able to effectively add a WHERE discriminator <> 'DomesticCat' to the end of the query. I had a quick search for this special discriminator property and for a Criteria Expression for excluding a particular type but couldn't find either.

The Solution

I finally found the solution on the WHERE clause page of the HQL chapter in the NHibernate reference. There is a special property called class which you can test against a type name in HQL or an actual type in Criteria queries e.g.

In HQL:
IQuery q = sess.CreateQuery("from Mammal m where m.class != 'DomesticCat'");
IList mammals = q.List();
In Criteria:
ICriteria crit = sess.CreateCriteria(typeof(Mammal));
crit.Add( Expression.Not( Expression.Eq("class", typeof(DomesticCat)) ) );
List mammals = crit.List();

04 August 2008

Generic collections and inheritance

Update - 07/2010

Covariance and contravariance support in .NET 4.0 takes care of this problem without the need for casting. Here's the relevant MSDN page: Covariance and Contravariance in Generics

I stumbled upon a small annoyance today when trying to use a generic collection of type B where a generic collection of type A is expected where B inherits from A. With arrays this works fine and the elements are implicitly cast to the base type.

Here's a snippet compiler script which illustrates the problem - list-converter.txt.

In the script I'm using BaseType and InheritingType for A and B. The script initially shows the implicit cast taking place for an array of B objects on line 19. The ArrayTest method expects an array of A but is quite happy being called with an array of B.

public static void ArrayTest(BaseType[] bar) { ... }

InheritingType[] myArray = new InheritingType[]{ it1, it2 };
ArrayTest(myArray);

If we now look at the ListTest method and try running this section of code...

public static void ListTest(List<BaseType> bar) { ... }

List<InheritingType> myList = new List<InheritingType>();
myList.Add(it1);
myList.Add(it2);
  
ListTest(myList);

...we get an error...

Argument '1': cannot convert from 
'System.Collections.Generic.List<InheritingType>' to 
'System.Collections.Generic.List<BaseType>'

We get the same result if we try an explicit cast

ListTest((List<BaseType>)myList);

Obviously allowing an implicit conversion for generics in general doesn't make a lot of sense but for lists I think it does and it's a pain to have to convert from one generic collection type to another.

Solution

Thankfully, with the help of some generics (of all things) and the ConvertAll method of List there's quite an elegant solution to this problem, we can create ourselves a nice generic list converter, here's an example:

public class ListConverter<TFrom, TTo> where TFrom : TTo
{
 public static IEnumerable<TTo> Convert(IEnumerable<TFrom> from)
 {
  return Convert(new List<TFrom>(from));
 }
 
 public static List<TTo> Convert(List<TFrom> from)
 {
  return from.ConvertAll<TTo>(new Converter<TFrom, TTo>(Convert));
 }

 public static TTo Convert(TFrom from)
 {
  return (TTo)from;
 }
}

We use two type arguments, the type we're converting from, which will be B from the example above, and the type we're converting to, A. Notice also the where TFrom : TTo which enforces that B must inherit from A.

As we need the ConvertAll method of List we have a method that takes an IEnumerable and creates a new List. We also have the method that does the main ConvertAll on the List and the delegate which is passed to ConvertAll.

This allows us to create a type converter as we code without needing to mess about e.g.

ListTest(ListConverter<InheritingType, BaseType>.Convert(myList));