# Saturday, November 08, 2008

Correctly implementing structural equality involves a lot of code. My macro implements this for you.

Watch the Screencast

Before

using System;
using System.Console;
using Nemerle.Utility;
 
module Program
{
    [Record]
    public class Location
    {
        public Latitude : double;
        public Longitude : double;
 
        public override Equals(other : object) : bool
        {
            match (other)
            {
                | null => false
                | l is Location => l.Latitude == Latitude && l.Longitude == Longitude
                | _ => false;
            }
        }
 
        public override GetHashCode() : int
        {
            Latitude.GetHashCode() ^ Longitude.GetHashCode()
        }
 
        public static @==(l1 : Location, l2 : Location) : bool
        {
            def l1IsNull = object.ReferenceEquals(l1, null);
            def l2IsNull = object.ReferenceEquals(l2, null);
            match (l1IsNull, l2IsNull)
            {
                | (true, true) => true
                | (false, false) => l1.Equals(l2)
                | _ => false
            }
        }
 
        public static @!=(l1 : Location, l2 : Location) : bool
        {
            !(l1 == l2)
        }
    }
 
    module Program
    {
        Main() : void
        {
            def l1 = Location(30, 70);
            def l2 = Location(30, 70);
            def l3 = Location(40, 90);
 
            WriteLine("l1.Equals(l2) -> {0}", l1.Equals(l2));
            WriteLine("l1 == l2 -> {0}", l1 == l2);
            WriteLine("l1 != l2 -> {0}", l1 != l2);
            WriteLine("l1.GetHashCode() == l2.GetHashCode() -> {0}", l1.GetHashCode() == l2.GetHashCode());
            WriteLine("l1 != l3 -> {0}", l1 != l3);
 
            _ = ReadLine();
        }
    }
}

After

using System;
using System.Console;
using Nemerle.Utility;
using SampleMacros;
 
module Program
{
    [Record]
    [ImplementEquality(Latitude, Longitude)]
    public class Location
    {
        public Latitude : double;
        public Longitude : double;
    }
 
    module Program
    {
        Main() : void
        {
            def l1 = Location(30, 70);
            def l2 = Location(30, 70);
            def l3 = Location(40, 90);
 
            WriteLine("l1.Equals(l2) -> {0}", l1.Equals(l2));
            WriteLine("l1 == l2 -> {0}", l1 == l2);
            WriteLine("l1 != l2 -> {0}", l1 != l2);
            WriteLine("l1.GetHashCode() == l2.GetHashCode() -> {0}", l1.GetHashCode() == l2.GetHashCode());
            WriteLine("l1 != l3 -> {0}", l1 != l3);
 
            _ = ReadLine();
        }
    }
}

ImplementEquality.n

using Nemerle;
using Nemerle.Compiler;
using Nemerle.Compiler.Parsetree;
using Nemerle.Macros;
 
namespace SampleMacros
{
    [MacroUsage(MacroPhase.BeforeInheritance, MacroTargets.Class)] 
    public macro ImplementEquality(typeBuilder : TypeBuilder, params members : list[PExpr]) 
    { 
        ImplementEqualityModule.AddEqualsMethod(typeBuilder, members); 
        ImplementEqualityModule.AddEqualityOperator(typeBuilder); 
        ImplementEqualityModule.AddInequalityOperator(typeBuilder); 
    } 
 
 
    module ImplementEqualityModule 
    { 
        public AddEqualsMethod(typeBuilder : TypeBuilder, members : list[PExpr]) : void 
        { 
            def other = Macros.NewSymbol(); 
            def comparisons = members.Map(m => { 
                def s = Splicable.Name(Name(m.ToString())); 
                <[ (this.$s).Equals($(other : name).$s) ]> 
            }); 
 
 
            def b = comparisons.Tail.FoldLeft(comparisons.Head, (c, acc) => <[ $acc && $c ]>); 
            typeBuilder.Define(<[ decl: 
                public override Equals(obj : object) : bool 
                { 
                    match (obj) { 
                        | null => false 
                        | $(other : name) is $(typeBuilder.ParsedTypeName) => $b 
                        | _ => false 
                    } 
                } 
            ]>); 
 
 
            def hashCodes = members.Map(m => { def s = Splicable.Name(Name(m.ToString())); <[ this.$s.GetHashCode() ]> }); 
            def hashCode = hashCodes.Tail.FoldLeft(hashCodes.Head, (c, acc) => <[ $acc ^ $c ]>); 
            typeBuilder.Define(<[ decl: 
                public override GetHashCode() : int 
                { 
                    $hashCode 
                } 
            ]>); 
        } 
 
 
        public AddEqualityOperator(typeBuilder : TypeBuilder) : void 
        { 
            typeBuilder.Define(CreateEqualityOperator(typeBuilder.ParsedTypeName, false)); 
        } 
 
 
        public AddInequalityOperator(typeBuilder : TypeBuilder) : void 
        { 
            typeBuilder.Define(CreateEqualityOperator(typeBuilder.ParsedTypeName, true)); 
        } 
 
 
        CreateEqualityOperator(typeRef : PExpr, invert : bool) : ClassMember.Function 
        { 
            def op = Splicable.Name(Name(if (invert) "!=" else "==")); 
 
 
            <[ decl: 
                public static $op(p1 : $(typeRef), p2 : $(typeRef)) : bool 
                { 
                    def p1Null = object.ReferenceEquals(null, p1); 
                    def p2Null = object.ReferenceEquals(null, p2); 
                    match (p1Null, p2Null) { 
                        | (true, true) => true 
                        | (false, false) => $(if (invert) <[false == p1.Equals(p2)]> else <[p1.Equals(p2)]> ) 
                        | _ => false 
                    } 
                } 
            ]> 
        } 
    } 
 
 
 
}
Saturday, November 08, 2008 5:18:15 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [3]  | 

InheritConstructors is a handy macro when you need to simply pass all arguments to a base constructor.

Watch the Screencast 

Before

using System;

 

namespace MacrosDemo

{

    public class MyBaseClass

    {

        public this(name : string, foo : int, bar : bool)

        {

            _name = name;

            _foo = foo;

            _bar = bar;

        }

 

        _name : string;

        _foo : int;

        _bar : bool;

 

        public virtual Test() : string

        {

            "Hello from MyBaseClass"

        }

    }

 

    public class MySubClass : MyBaseClass

    {

        public this(name : string, foo : int, bar : bool)

        {

            base(name, foo, bar)

        }

 

        public override Test() : string

        {

            "Hello from MySubClass"

        }

    }

 

    module Program

    {

        Main() : void

        {

        }

    }

}

After

using System;

using Nemerle;

 

namespace MacrosDemo

{

    public class MyBaseClass

    {

        public this(name : string, foo : int, bar : bool)

        {

            _name = name;

            _foo = foo;

            _bar = bar;

        }

 

        _name : string;

        _foo : int;

        _bar : bool;

 

        public virtual Test() : string

        {

            "Hello from MyBaseClass"

        }

    }

 

    [InheritConstructors]

    public class MySubClass : MyBaseClass

    {

        public override Test() : string

        {

            "Hello from MySubClass"

        }

    }

 

    module Program

    {

        Main() : void

        {

        }

    }

}

Saturday, November 08, 2008 5:13:02 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 

The [Record] macro is applied to classes and makes it very easy to create simple data-transfer objects.

Watch the Screencast

Before

using System;

 

namespace MacrosDemo

{

    public class CustomerDto

    {

        public Id : int;

        public FirstName : string;

        public LastName : string;

        public IsPrefered : bool;

 

        public this(id : int, firstName : string, lastName : string, isPrefered : bool)

        {

            Id = id;

            FirstName = firstName;

            LastName = lastName;

            IsPrefered = isPrefered;

        }

    }

 

    module Program

    {

        Main() : void

        {

            def dto = CustomerDto(1, "John", "Smith", true);

        }

    }

}

 

After

using System;

using Nemerle.Utility;

 

namespace MacrosDemo

{

    [Record]

    public class CustomerDto

    {

        public Id : int;

        public FirstName : string;

        public LastName : string;

        public IsPrefered : bool;

    }

 

    module Program

    {

        Main() : void

        {

            def dto = CustomerDto(1, "John", "Smith", true);

        }

    }

}

 

Saturday, November 08, 2008 5:04:29 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
# Sunday, July 13, 2008

Sending data from a resource to a view in a strongly-typed, but syntactically lightweight manner is not possible with C# or VB.NET.

So check out this screencast of the new "render" macro in NRest!

In a nutshell, my resource looks like:

[Resource("")]
public class RootResource
{
    public Get() : void
    {
        def test = 42;
        render (Foo = "Hello", Bar = "World", test);
    }
}

My VB.NET view looks like:

Public Class RootPage
    Inherits Page(Of RootPageContent)

    Public Overrides Function GetHtml() As XElement
        Return _
        <html>
            <body><%= Content.Foo %>, <%= Content.Bar %></body>
        </html>
    End Function
End Class

The RootPageContent DTO class is generated at compile time by the render macro. Everything is strongly-typed.

.net | dsl | macro | nemerle | nrest | REST | web
Sunday, July 13, 2008 3:27:17 PM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  |