Wednesday, October 29, 2014

The real power of generics

I just watched the "Effectively Using Generics in Object Pascal" Code Rage 9 session by Ray Konopka, where he showed a great introduction to generics which is mostly about putting things into lists, dictionaries or other data structures. So basically container types what generics are great for.

When someone asked how you can find out the type of T inside your generic type Ray responded that if you need to do that you may be doing something wrong and why that might be necessary because generic types should be type agnostic.

It might be correct if you only think of collection types when you think about generics. And this is what many people do, hence the discussion I have seen in several places why other pascal languages would need generics because they have powerful array support or other excuses.

But this is limited thinking very much. Generics are way more than a tool to build collection types.
You can do things like the multicast events in Spring4D where of course the generic type needs to find out the exact type info of the T in Event<T> to execute the correct code depending on how you are dealing with a event type or an anonymous method type.
Or think about using generics when using the Spring4D dependency injection container. How could we use TContainer.RegisterType<T> or TContainer.Resolve<T> without finding out the exact type. In fact in that API you mostly don't even pass a variable of that type but just use the generic methods as replacement for something like TContainer.RegisterType(TypeInfo(...)).

Want more examples? Sure!
Think of something like Mock<T> where T is then the type you want to create a mock for. You of course need to find out the type info to pass that into the code that does all the rtti magic beneath (like TVirtualInterface). Think of fluent APIs that return T and provide some typesafe way to use that API (which is what the container registration API and mocks do). Hey, even TComparer<T> from System.Generics.Defaults internally looks up what type the T is to call the correct comparison routine depending on the TTypeKind of the type.

I hope that showed some of the examples where generics give you great power beyond that overused "list of something" example. I am sure you can think of more possibilities!

Oh, by the way the answer to the question asked is: you can use TypeInfo(T) or some of the new intrinsic functions to get information of your generic type parameter.

Thursday, October 9, 2014

Generics and variance

When working with generic collections there is often the question: "Why can't I just assign my TList<TPerson> to a variable of TList<TEntity>? TPerson inherits from TEntity!"

There is something called covariance and contravariance - but the bad news are: Delphi does not support variance. But we are trying to understand what this is all about anyway, shall we?

Let's take a look at our TList<TPerson> again and find out why it is invariant as the Wikipedia article says. Invariant means it's not compatible with either TList<TEntity> nor TList<TCustomer> (TCustomer inherits from TPerson). Let's assume we would hardcast it to TList<TEntity> and continue working with it. It seems correct since every TPerson is a TEntity so a TList<TEntity> is a TList<TPerson> as well, no? No! And that is because data goes out and in. Just iterating over the list and taking the items and printing their name would work. But someone could also try to add a TOrder (it inherits from TEntity) into that list. This will work because at that point we have a TList<TEntity> but at a later point this might horribly crash since some other code still uses the list as TList<TPerson> that suddenly contains a TOrder which clearly does not belong there.

Now let's take a look at a TEnumerable<TPerson> that you might hardcast to a TEnumerable<TEntity>. This will work because you can only retrieve things from TEnumerable<T> but not add any. That is also why C# choses the out keyword for marking a generic type as covariant. And that is also why IReadOnlyList<T> is covariant. You can only retrieve items from it but not add any - even if you could remove items it would be covariant but not if you could add some.

Example: Imagine a box of apples. You can treat it as a box of fruits as long as you just take out some or count them. But you are not allowed to add oranges to it.

Now that we understood what covariance is we might understand easier what contravariance is. As its name implies its the opposite of covariance. And indeed it works the other way around. The Wikipedia articles says the subtyping is reversed. Think of an argument of TProc<TCustomer> that is passed somewhere. So for every TCustomer in a list it does something with that instance and what exactly is done is specified in the anonymous method that is getting passed. The code in that method gets a TCustomer and can access anything a TCustomer has. So now if we had contravariance this would apply here and we could actually pass a TProc<TEntity> because we only want access to the properties of a TEntity which is the base type of TCustomer. Now you see that the C# keyword in makes sense for a contravariant generic type.

If we had a labeler at hand it would be covariant because we can label not only apples but all kinds of fruits.

So how to deal with that kind of dilemma in Delphi?
Well there are several approaches to this. One is carefully(!) using hard-casts making sure that nobody does evil things (like adding oranges into the apple box). When using Spring4D collections you can make use of the ElementType property that every interfaced collection type has to make sure you are not adding wrong things to your list. And you can use the IObjectList interface when dealing with lists that contain class types. This is especially useful when connecting lists to GUI elements because they just have to know about IObjectList. It's almost the same as IList<TObject> but with a different guid and QueryInterface only succeeds for a list that contains objects. But most importantly take the correct type for what you are doing. If you just want to iterate things without manipulating then take IEnumerable<T> or IReadOnlyList<T> instead of IList<T> (see encapsulation).

But there is even more about variance and one is very interesting that not even C# does support - but Java and C++ do: return type covariance.

Imagine a TVehicleFactory with a virtual method CreateNew method that returns a TVehicle. Now you inherit a TCarFactory from it which overrides the CreateNew method. It still has TVehicle as result but it really makes more sense to let it return TCar. If that would work we had return type covariance.

I hope this article shed a bit more light on generics and variance and why certain things don't work the way one might expect.

Friday, October 3, 2014

New language feature in XE7

Nope, not the dynamic array enhancement that everyone talked about already.

In fact I am going to show you something that I haven't seen anyone talking about yet.
It wasn't even mentioned on the official XE7 shows and the reason probably is because it is not been finished completely as in used in generics in the RTL that would benefit from that.

While it is not the desired compiler/linker support for reducing generics code bloat it certainly is something that can help reducing it when used properly.

There are new compiler intrinsic routines called

function IsManagedType(T: TypeIdentifier): Boolean;
function HasWeakRef(T: TypeIdentifier): Boolean;
function GetTypeKind(T: TypeIdentifier): TTypeKind;

These functions when used are evaluated at compiletime and can be used in if and case statements.
Since the expressions of these statements are constant the compiler can eliminate the unused code paths.

Oh, and by the way TTypeKind has been moved to System.pas so no need to include TypInfo for that anymore, yay!

Sounds complicated? Let's see some code:

type
  TFoo<T> = class
    procedure Bar;
  end;

procedure TFoo<T>.Bar;
begin
  case GetTypeKind(T) of
    tkInteger: Writeln('int');
  else
    Writeln('not int');
  end;
end;

begin
  TFoo<Integer>.Bar;
  Readln;
end.

Ok you say, before XE7 we had to write this:

class procedure TFoo<T>.Bar;
begin
  case PTypeInfo(TypeInfo(T)).Kind of
    tkInteger: Writeln('int');
  else
    Writeln('not int');
  end;
end;

So what's the difference? This basically works similar to a compiler directive that we don't have.

When you compile the code using GetTypeKind in XE7 you can see that there is only a blue dot in the line with the Writeln('int') but not in the other one. That indicates only this line can be executed. It would be different when using the other version because that expression is not evaluated to a constant expression at compile time. Hence the compiler cannot remove any part of the case statement.

What does that mean? Well you can write generic types as thin wrappers around code that is written explicitly for the different types without blowing up your binary in the end.

I guess in XE8 we will see that been used in Generics.Collections to reduce the size of the resulting binary. However it cannot remove all duplicated binary code since regardless how thin the generic class is it gets compiled for every instantiation (any different type put in for the T) even if the code is exactly the same. So we will see what other ideas they come up with to solve that problem.

I am also investigating this further to find out if that might be any useful in Spring4D.

Oh, btw compiler guys - while you are adding compiler magic, can you please add the NameOf function aswell? Especially when dealing with databinding and things that are currently referenced as magic strings in the source code this could prevent breaking code by typos or refactorings. Thanks!