Ayende @ Rahien

Refunds available at head office

Mixin & Dicing in C# 3

Note: It's probably pretty early to start talking about patterns for C# 3, but the thought popped to my mind and wouldn't go away.

Mixin: In computer science, a mixin is a group of functions which can be mixed into a class and become methods. They allow certain classes to take on certain functionality, in an object-oriented programming including in languages that do not support multiple inheritance, just as if the class were a cooking dish and a mixin was a specific ingredient.

Ruby on Rails recently got a act_as_taggable mixin, which basically allows you to just slap a single line of code on a class and get tagging support. I'm going to show the C# 3 interface for a similar implementation. I don't care about the implementation, so we'll just assume that this is a given. I want to be able to tag any database object with as few lines of code as possible and just have it work like magic, the ideal is Ruby's single line of code. I'm going to show the ideal, the interface & client code and then talk about how this is possible.

Let's see what we would like to write:

//Createing taggable mixin
public mixin Taggable<T>
{
 public void TagWith(params string tags) { .. }

 public static T TaggedWith(params string tags, FindWith with) { .. }
}
public enum FindWith 
{
 AnyOfTheTags,
 AllTheTags
}
//Adding tagging to a class


public class Photo : Taggable
{
...
}

//client code:
// create photo and adding tags
Photo photo = ... ;
photo = Photo.TaggedWith("flower","sun","yellow""mayhem");
//Loading with tags

Photo photo = Photo.TaggedWith("flower", FindWith.AnyOfTheTags);

This is what we would like to have, but right now and in the future we are not likely to get it. We can get something very close. Here is what we would need to write:

//creating taggable mixin


namespace Tagging
{
 public static class Taggable
 {
  public static void TagWith(this ITaggable<T> tagged, params string tags) { .. }

  public static IList<T> TaggedWith<T>(params string tags, FindWith with) where T : ITaggable<T> { .. }

 public interface Mixin<T> 
  where T : ActiveRecordBase, Mixin<T>   { }
 }
 
 public enum FindWith 
 {
  AnyOfTheTags,
  AllTheTags
 } 
}
//Adding tagging to a class
public class Photo : ActiveRecordBase, Taggable.Mixin<Photo>
{
...
}
//client code

using Tagging;
//create photo and adding tags
Photo photo = ... ;
photo.TagWith("sun","rain","colors");
//load photo with tags
IList<Photo> photoWithFlowers = Taggable.TaggedWith<Photo>("chaos",  FindWith.AnyOfTheTags);

So, what do we have here? We have a static class with extention methods that refer to the Taggable.Mixin<T> interface. The Taggable.Mixin<T> interface require that implementing class will inherit from ActiveRecordBase and implement ITaggable*, this is so the Taggable class will have a way to work with the database, (that is an implementation detail, it can certainly be done in other ways).

Then we have the Taggable class, which has an extention method to add tags to an object, and a static method (not an extention one), which takes a Taggable.Mixin type and return a list of the tagged instances of it. Check out the bolded lines, those is what you've to do in order to get the taggable support for an object. Add a declaration to Taggable.Mixin, and your object is set. Then in the client code just import the Tagging namespace, and you can use it as if it was part of the object.

I think that this is a really nice way to add functionality to objects in a non intrusive way. The client code can actually choose whatever it wants to be exposed to the tagging support or not, and all the class have to do is to declare its intention to accept the mixin.

As I said, it's a pretty pre-mature to start thinking about patterns for C# 3 (C# 2 is not yet released, after all), but I'm willing to bet quite a sum that this will be the way to create mixins is the .Net framework. This is just one of the cool things that you can do with the things that C# 3 will gives you. I expect a lot more goodies along the ways. The new features are useful for so much more beyond Linq.

One thing to considered, it's pretty early to say anything, but I can certainly see libraries such as the Boost providing tremendous value for developers in the C# 3 world.

* Implementing Taggable.Mixin - since Taggable.Mixin is an empty interface, the constraints here are just a way to say that any Taggable.Mixin must inherit from ActiveRecordBase. It's a nice way to declare things, since now we can say: ITaggable<Photo> is ActiveRecordBase, and it's always true.

Re: DLinq Mapping: External Or Not (Attributes or XML file)

Dinesh has a post where he talks about the relative advantages and disadvantages of ORM mapping schemes. There seems to be quite a bit of noise about DLinq only supporting attribute mapping in the PDC.

Here is my take on the subject:

Whenever possible, make mapping via attributes possible. The ability to quickly tag a class with attributes and have a workable mapping is very important in most design scenarios, where you want maximum flexibility in changing the mapping. Even though that a compiler can't catch logical mistakes in the mapping, there is a certain class of mistake thati it would catch, so it's good to have it.

Here are Dinesh' take on the  advantages of external mapping:

  1. Same classes can be mapped to multiple data models using different mapping files
  2. Avoids clutter in source code, especially in case of complex mapping
  3. Hides database-specific information from users of object model
  4. Some schema changes can be handled without recompilation (IMO, the class of changes that can be handled is often exaggerated)

I agree that 1 and 3 are good points for extenral mapping, but I feel that they can be hanlded via a secondary mapping which is external to the object while keeping most of the mapping in code. I wrote several applications using NHibernate, which uses XML mapping extensively. It was often a pain to trace the difference between the mapping and the model. The errors can be subtle and hard to track down.

I completely disagree with the advantanges of both 2 and 4. First, clutter in code? Those classes are the gateway to the database, the last thing I want to see is people starting to think of them like normal objects: customers.HugeOrdersCollection.Count - (Load all the orders to memory and return their number, prohibitely expensive operation). The idea in ORM is to make it easier to work with the database, not to divorce all knowledge of its existance. I know that this isn't what Dinesh suggested, but that is the thought behind removing "clutter" from the code.

About changing the schema without recompiling. Care to give me a real scenario where you would be changing a live database where you don't have to recompile? I can't think of any non-really-trivial changes that you can make that wouldn't force you to change code.

More on static interfaces

I've opened a ladybug feature request for it.

One other thing that it's going to be useful for is interface operators (but which can be also solved using extentions operators).

Tags:

Published at

Feature Request: Static Interfaces

I want C# 3 to have support for static interfaces! What is a static interface?

A static interface is like a normal interface which a class can implement, but it may contain static methods and constructors. The syntax is simple, just add the static keyword on the methods that you want to be static, add constructors as usual and you're set (I've bolded the new parts):

public interface IDocument
{
 void Save();
 void SaveAs(string name);

 IDocument(IDataStore dataStore);
 static IDocument Create();
 static IDocument Open(string name);
}

public class DocumentImpl : IDocument
{
 DocumentImpl(IDataStore dataStore) { ... }
 void Save() { ... }
 void SaveAs( string name ) { ... }
 static IDocument Create() { ... }
 static IDocument Open(string name) { ... }
}

A class that implements IDocumentCreator must provide static implementation for the Create() and Open() methods. In this case the problem is relatively simple, but in many cases such a thing can be a really big problem in trying to design a class hierarchy where static methods and constructors are part of the interface.

I'm sure that most developers can account for at least one case where they really wanted a way to specify either constructors or static methods in an interface, because that would simplify their life so much. The current solutions are factories [or factories' factories (and so on... )] which suffer from other set of problems or using reflection. Both of solutions are half measures, since it's a way to try to bypass the language limitation and gets the above code to work.

How would I use the above code? Idealy, like this:

public DispalyNewDocument(static IDocument docType)
{
 IDocument doc = docType.Create();
 IDocument doc2 = new docType(dataStore);
 //Dispaly docs.

}

DisplayNewDocument(DocumentImpl);

How would the compiler implement such a thing? Well, for a start, it would seperate the IDocument interface into two parts a static one and a regular one, the static one would result in an automatically generated class, which would implement the methods mark as static as instance methods with simple delegation to the static ones. Everywhere where I need the static portion of the interface, I just use "static InterfaceName" and pass the name of a class. The compiler would then pass an instance of the generated class. The compiler should also be smart enough to turn calls to new InterfaceInstance( args ) to method call to the interface (called Constructor, naturally) which would then return the instance of the class, simply by calling new on it.

The code declaration that I've above should generate the following:

public interface IDocument
{
 void Save();
 void SaveAs(string name);
}
public interface IDocument_StaticPortion
{
 IDocument Constructor(IDataStore dataStore);
 IDocument Create();
 IDocument Open(string name);
}
public class DocumentImpl : IDocument
{
 DocumentImpl(IDataStore dataStore) { ... }
 void Save() { ... }
 void SaveAs( string name ) { ... }
 static IDocument Create() { ... }
 static IDocument Open(string name) { ... }
}
public class DocumentImpl_StaticInterfaceImpl : IDocument_StaticPortion
{
 public IDocument Construcotr(IDataStore dataStore)
 {
   return new DocumentImpl(dataStore);
 }
 public IDocument Create()
 {
   return DocumentImpl.Create();
 }

 public IDocument Open(string name)
 {
   return DocumentImpl.Open(name);
 }
}

And the code that is using it should generated:

public DispalyNewDocument(IDocument_StaticPortion  docType)
{
 IDocument doc = docType.Create();
 IDocument doc2 = docType.Constructor(dataStore);
 //Dispaly docs.
}

DisplayNewDocument(new DocumentImpl_StaticInterfaceImpl());

Using fairly simple transformation and no changes to the CLR itself, we get an incredible boost to productivity via static interfaces. We can now specify all the details about our class, including the static ones. This has a lot of implication on design and totally eradicate a large class of creational patterns, since you can just express them fully in the language.

What do you think?

Tags:

Published at

Quote of the week

James often has the best quotes around:

When you lose a usability race with Unix developers, it's generally a bad sign

[Regarding current and future Office UI]

Tags:

Published at

Cool Linq Idea: UI Mapping

This is purely hypotetical, but imagine the following way to show the user's details:

User user = ... ;//Get user something

var view = new View( user, 
 new Text(u => u.Name,  s => user.Name = s),
 new Password( u => u.Password,  s => user.Password = s ),
 new List(from g in user.Groups select g,  g => user.Groups.Add(g), g => user.Groups.Remove(g)) );
view.Show();

Where View is a class which display view items in a TableLayout. And each item knows how to handle changes. This could be a very rapid way to create UI.

What do you think?

Why Super Fetch is exciting

When I'm using my primary computer, I'm usually just go away when I'm done, so anything that is save in the virtual memory fetch is there when I return. I'm assuming here, of course, but I don't think that it clears out the cache unless there is a memory pressure. I can certainly see an increase in the amount of memory that my session is taking whenever I run for long periods of times.

My current uptime is 23 days (reboots are purely windows update ones), and applications luanch are nearly instantenious. This is not the same on my laptop. I can't keep it always on, so every time I start working with it, I need to bring things to memory, which may take some time. Imagine waiting for VS.Net to load from a laptop (usually slow) HD. Yuck.

Cold boot - VS.Net start on my main computer:

3 seconds to start screen, 14 seconds to start page being usable.

Opening VS.Net after it has been opened:

Instantenous start screen, 4 seconds to usable start page.

The cold boot times are probably double that on a laptop, so imagine how good it is to get the good times on the first time.

Tags:

Published at

Watching the PDC keynote

As I said, I'd a lot of problem in trying to watch the streaming keynote, so I finally (after what, three days?) managed to download it all (and that is on a broad band, mind you).

So I'm watching it now, and I'm seeing the new things in Vista, and I'm sitting here with my mouth open wide.

No, I'm not talking about the UI, it's the Super Fetch stuff that has be stunned:

  • Fast applications start after reboots.
  • The ability to add more memory via USB keys.

I'm really digging this stuff, it's the coolest thing in the world, I think.

Tags:

Published at

More on innovation

Just thinking, I think that Microsoft was the first* that made deep integration between the language constructs mix with the framework it live on. I'm talking about such things as using and IDisposable, where you had a language construct that you can control and add to.

This theme continue with the Linq infra structure, it's not ADO.Net build into the language, it's simply macro transformation that is open for the developer to extend with his own code. I'm familiar with the concept from Boo, and it's a very powerful one.

* I'm not a language researcher, but I would love to know about similar schemes.

Tags:

Published at

DLinq info

I just checked the DLink hands on lab, and it's just what I would expect it to be. Considerring that I'm expected DataContext to be as widely spread as an ADO.Net driver today.

This is certainly taking data access to a new level. The ease of creating / deleting / updating records is just staggering. Microsoft really did it this time. It's much better than any approach that any other (obviously it is best to have language support for your ORM of choice).

Tags:

Published at

How to make C# 3 even better...

After getting so excited about the things that are going to be in C# 3, let's see the things that I would like to see coming:

Extentions method for properties and operators - Operators are super important because this would be the only way to add operators to interfaces. Extentions properties are a must have if we have extentions methods. The spec mentions that they are considered, I certainly think that they should go in.

Compiler time reflection information - By that I mean a way to explicitly get reflection information where it's known. For instance, I may want to pass a property info to a method, right now I've to use strings to do it. The things that I would like to see are: methodInfo(), propertyInfo(), eventInfo(), fieldInfo(), parameterInfo(). All of them as language constructs exactly like typeof(), I can see that this would be a problem with overloaded methods, though.

The anonymous type is depedant on the properties orderring - Does this makes sense?

(new { Str = "a", Int = 1}).GetType() != (new { Int = 1, Str = "a"}).GetType();

This is just silly. What is the reason you would want  two types with the same number / type of properties to be different? This is inconsistent with the rest of the language.

Type inferencing beyond the local scope - I want to be to do the following:

public var Method() { return "something"; } 

This have a lot of implications, but it's a great productivity saver and it allows a lot of nice tricks. Some of the problems I can think of with this is what happens when you've anonymous type as a return type:

public var Method() { return new { Str = "Something" }; } 
  • What is the return type of the method?
  • How would I know from outside the method?
  • How would I know what the type is outside of the assembly?

I can see two possible way to do that:

  • Allow only static types in non-internal API, this is the best choice, I think.
  • Only allow var return types for internal methods, this is not a favoriate of mine.

In both cases, I think that it's important to allow anonymous types as a return type from a method inside a single assembly. It's a very good way to move a lot of information from one method to the other in a type safe way.

Another problem is accidently changing the return type of a method with var return type. I think that this would be cought very quickly, as it would cause immediate breakage in other places, so it's not such a big problem.

One thing to consider is situation like this:

public var Method(int i) 

      if (i%2==0) 
            return new object(); 
      else 
            return "something"

What would be the return type of the method?

  • error if they are not exactly the same type.
  • revert to the most basic type.

This leads me to another question, why are all the type inferencing insisting on keeping the same type?

var array = new []{1,"something"null}; 

The array should be object[] , and the above should not cause an error. The idea is to go to the common base class (or object, of course), and it's been successfully implemented in other languages.

Tags:

Published at

Searchable project name, first for Microsoft

I just noticed, Microsoft finally learned a very important lesson. Make it searchable.

...after giving the CLR the nearly unsearchable name of .Net, which is the second most common name on the internet, bested only by COM, which is also a product of Microsoft.

...after unleashing C#, which is just as unsearchable.

Microsoft finally learned. Linq is actually searchable, with good results*.

* 24 Hours after the announcement, the official page is #5 at goggle (but doesn't appear on the first 20 results on MSN, ironically.

 

Tags:

Published at

Streaming sucks

I want to watch the PDC keynote, available here, but I've a very simple choice:

  • Try to watch a low quality video that will stutter or,
  • Hm... tTry to watch a low quality video that will stutter.

I'm not in the US, and while I do have broad band, I'm having serious problems getting streaming to work, even for the low quality videos. I don't want to want to watch a low quality one. I want to watch the highest quality avialable, I want to see the new stuff myself, not just hear about them from blogs. But there is no download link!

Sorry, streaming doesn't cut it, not nearly enough. It's good while the event is live, certainly, but it's been over 24 hours already, where is the download link?

Luckily, I found the ASF Recorder, which will take an asf file and save the output to disk, so I can get leave it overnight and then watch it at leisure later.

As an aside, anyone knows whatever the rest of the presentations will be online? I can't seem to find anything on the net.

Tags:

Published at

Innovation

I'm still digesting all the new things that we're going to get in C# 3. I've already expressed my joy about the things that this would allow me to do.

I think that those changes should shut anyone who claims that Microsoft cannot innovate. Yes, some of the new features have been there in other langauges before, but I've never seen a main stream language that make such a move. The interesting thing is that while Linq will probably get all the attention, it's the foundations that makes it so interesting, since you can use it outside of Linq.

I'm simply amazed that the next next version has such good features even before the next version has been released. It makes me wonder what goodies we can expect toward beta 1.

P.S,

I'm currently seeing the Office 12 movie from channel 9, and it's just an amazing UI.
I now understand why we've Office 2003 UI in the .Net framework, because Office 12 UI is so different.
But the amount of mail that she is getting in the movie is simply staggering.

Tags:

Published at

Microsoft, Release C# 3 ASAP

I don't want to wait until mid 2007 to start using the cool stuff they've right now. The things I could do with LINQ are just astounding. Please don't make all of us droll too long.
Tags:

Published at

C# 3 Usage

Check out this post, where Cyrus is showing a cool way to combine the abilities of C# 3 to create some powerful code. This is possible to do in 1.1 or 2.0, just painful.

Syntatic Sugar Isn't!

Tags:

Published at

C# 3, so cool!

Just look at C# 3 future, it's so pretty I want it now. Check out the videos that they have there. There is so much good stuff there, and it's only the start.  First of all, read the C# 3.0 spec [doc file], or just read the rest of the post to see the highlights.

Type inferencing and implicitly typed arrays- I got addicted to that when using Boo, it's something so simple, but it saves so much. In most cases, it means no more casting hell. Andres let it slip in a presentation about a year ago, in essence, it means:

var sum = 0;
var intArray = new [] { 1,2,3,4};
foreach ( var i in intArray )
{
    sum += i;
}

This is a very cool idea, especially since you can use it for the foreach construct, which should make for far better code. Did you notice how I declared the array? "new []", without a type, the compiler just fix it up automatically.

Extensions methods - This is a feature that lets you add new instance methods to a class, without touching the class code and even if the class is already compiled. The idea opens up so many possibilities that it's just staggering. Fowler talked about it in Refactoring. Other languages, such as SmallTalk and Ruby already has it (SmallTalk has it for 30 years, I think), and it has prove to be a useful feature. You could add methods to System.Object, which would then be "inherited" by any object. Consider adding ToXmlString() for all objects, for instance. Or persistence to database. And no, it's not just syntactic sugar, those things are important. This mean that you're no longer at the mercy of the class designer. It's a truly powerful tool. The nicest thing about it, I think, is that it'll work with generics, so you get a host of goodness for very little effort. Here is the example from the spec:

public static class Sequence{
            public static IEnumerable<S> Select<T,S>(
                        this IEnumerable<T> source, Func<T,S> selector)
            {
                        foreach (T element in source) yield return selector(element);
            }

            public static IEnumerable Select<T,S>(
                        this IEnumerable source, Func<T,S> selector)
            {
                        foreach (T element in source) yield return selector(element);
            }
}

What does the above say? Well, it means that you can now use Select() on any object that implements IEnumerable. You could even use them on your 1.0 and 1.1 collections! Can you say goodness? This is cool on so many levels that it's not even funny.

Here is another nice thing that you can do:

public static class ExtermalObjectMethods
{
    public static bool IsNull(this object obj)
    {
         return obj == null;
    }
}
object nullObj = null;
if (nullObj.IsNull() )
   Console.WriteLine("Object is null");

Just how cool is that?

Extension properties, events and operators are currently considered, but not yet supported. I certainly hope that Microsoft will implement those, as they would allow a much nicer syntax for a lot of things (an IsNull property, for start). Adding operators to interfaces is another big problem that this can solve.

Lambda Expressions - Like anonymous methods, only with an even easier syntax. This is something that I don't think most .Net developers will appreciate now, but they certainly would two years from now, when the functional goodness will be fully entranced. Here is how you would extract all the names from a list of customers:

var customers = GetListOfCustomers();
string[] names = customers.Select( c => c.Name );

Can you see the beauty of it? The "c => c.Name" is the lambda expression, which is just a way to tell the compiler to do the dirty work, instead of having to do it ourselves using anonymous delegates. If it was all that they were good for, they wouldn't amount to much, but they have quite a few other usages, as you'll see shortly.

Object and collection initializers - This is something that we had for quite some time in attributes, which is now coming to both object initialization and collections initialization. The idea is actually very simple, consider the class Person:

var ayende = new Person{Name = "Ayende", Surname = "Rahien"};
var persons = new List<Person> { 
    new Person{Name = "Ayende", Surname = "Rahien"}, 
    new Person{Name = "Foo", Surname = "Bar"}
}

Just consider the shear amount of code that you would've to write in order to do the same today (or even in 2.0).

Anonymous types - No, it's not Java's anonymous classes, but it's a nice enough feature. It means that you can declare a type by just using it.

var ayende = new {Name = "ayende", WebSite = http://www.ayende.com/};
Console.WriteLine(ayende.WebSite);

This would create a new type with Name and WebSite properties. While this may save some typing in certain cases, I'm not certain that this is a useful feature. You cannot use it outside of a method's boundary, since it has no type that you can access.

This is nice, but I don't like the way it's implemented now. Boo has something similar, but since Boo has type inferencing throughout the language there it actually makes sense, since it allows to return a strongly typed object from a  method, instead of an object array. Here is the Boo example:

def MethodThatReturnSeveralArgs():
   return tuple { Int: 1, Name: "Ayende", Null: null, TimeStamp: DateTime.Now}

print MethodThatReturnSeveralArgs().Name

Since declaring variables using the "var" syntax is limited to local variables, I think this is unnecessarily limited.

Query Expressions - I expect this to be the big thing for C# 3, just as generics are the big thing for C# 2. The idea is to allow an SQL like syntax for selecting objects from a database/memory/xml directly into the language. The idea is quite involved, and I'm sure to generate a lot of discussion. The idea is to be able to do the following:

var payingCustomers = from c in db.Customers where c.Orders.Count > 0 
     select new { Email = c.Email, Name = c.Name };
foreach (var c in payingCustomers)
{
   SendDiscountOffer(c.Email, c.Name);
}

There are all sorts of ways where you can plug into this statement and do all sorts of cool things with it. db.Customers may be an in memory class, or it can redirect the query to database, to remote machine, etc. This is also apparently the use case for anonymous types, as return types from lambda expressions. It's nice, but it's not enough. It should be expanded to regular methods as well, in my opinion. I suggest checking the spec anyway, I'm just touching the edge here, and I'm sure that there are a lot of cool implementations that I'm missing here.

Expression Trees - are a way to turn lambda expressions into data. The idea is to be able to do this:

var valuedCustomers = session.Select(x => x.IsValued==true);

What is this? Well, what happened here is that we saved the lambda expression "x => x.IsValued==true" and passed it to a persistence layer, which would parse the expression, and load from the database all the rows for which the expression is true.

The important thing here is that the Select method is not using a opaque string, but code that can be type checked and verified by the compiler. Presumably refactoring tools for C# 3 will also support this, so it will make life even easier.

For the author of the Select() method, life would be much easier as well. He won't have to parse strings, but rather deal with an AST tree. From experience, I know that dealing with ASTs is not the easiest thing in the world, but it's certainly much better than dealing with strings.

Summary:

All in all, I really like the direction that C# 3 is heading for. Functional programming is not appropriate all the time, but it can save quite a bit of typing and a lot of awkward syntax. I'm very excited about the new abilities, and I think that they will present a quantum leap much bigger than 1.1 -> 2.0. Considering that those are just the language enhancements, I can hardly grasp the amount of goodness we will get in the class library and the associated tools.

I expect quite a lot of noise about query expressions (yes, they are cool). And a lot of activity in the area of lambda expressions as data, since it's a cool way to express much more than just ORM syntax.

The future is bright indeed.

P.S: It's 2:04 AM and I'm reading language specs, yes, I'm a geek.

Tags:

Published at

Incredible

Read this post about how Microsoft's employees helped Katrina's victims.

The list is just incredible.

Tags:

Published at

Coding Block

Ever had one of those? I did almost no coding all week, and I'm not I can explain why. I was in quite a few job interviews, and I'm considerring some offers, but I certainly didn't spend so much time there that I would have no time to code. I just don't have the incentive to do so.

This happens every now and then. I find it remarkably similiar to a writer's block. I used to write quite a bit of fiction, and I had those blocks then as well. It's the same feeling, the ideas are there, as well as the capability. It's the drive to do something with them that is missing.

Reducing the untestable

Andres has a nice post about unit testing a-sync code, which led me to think about another problem that I've, which is tests which run for a long time. Those are usually integration tests, where I'm simulating a request and checking the response from the server. This means that a test run is slow, in the order of ~30-40 seconds for 60 tests.

I'm thinking about creating an NUnit extention (I'm pretty sure that MbUnit already has this functionality) that run all the tests in the fixture in parallel. The advantages is that you're more likely to finish sooner, and your code is also tested for multi threaded scenarios, which most test do not do.

Faces Project Management - No XML Required

Faces is an interesting concept. I don't care for the software itself, but for the implementation. It's using python to config the project's options. This is a very flexible and natural way to do it.

No angle bracket in sight :-)

Tags:

Published at

The anti XML brignad

Interesting read. I'm not the only one bemoaning the hold XML has gotten over our life.

Tags:

Published at