Search code examples
c#haskellnewtype

What is C#'s equivalent to Haskell's newtype?


In Haskell, there's two ways of providing an alias for types: type and newtype. type provides a type synonym, which means the synonym is regarded by the type checker as exactly the same as the original type:

type UserId = Int
hasAccess :: UserId -> Bool
hasAccess id = {-- stuff --}

-- Elsewhere in the program
login :: Int -> Bool
login n = hasAccess n -- Typechecker won't complain

A newtype is similar, but is regarded by the type checker as a different type:

newtype UserId = UserId Int
hasAccess :: UserId -> Bool
hasAccess (UserId id) = {-- stuff --}

-- Elsewhere in the program
login :: Int -> Bool
login n = hasAccess n -- Typechecker will complain, n isn't a UserId !

In C#, you can define type synonyms with a top-level using declaration:

using UserId = Int;

However, a strongly-typed, compiler-checked type alias does not seem to be present in C# by default. I've looked into automatic code generation with T4 templates and CodeDOM to generate a class wrapper, but I don't really know how I could cleanly integrate those into my programming flow.

Ideally, I would like to be able to say on a top-level:

// Something like this?
using Int.UserId;

/* Elsewhere */
var id = new UserId(5);

public bool HasAccess( UserId id )
{
    /* Stuff */
}

This kicks the code generation into gear at compile-time. If that's not possible or provides a chicken-and-egg issue for IntelliSense, an automatic compilation option that runs every x minutes (or a button or whatever) would be nice.


Solution

  • No, C# does not have such a feature. The closest you can get to this are structs.

    public struct UserId
    {
        public int Id { get; private set; }
    
        public UserId(int id) : this() { Id = id; }
    }
    

    This way, the compiler indeed treats UserId and int as different types. Furthermore, you can add more methods to UserId that make sense given the fact that your int is effectively a user id. Note as well that this does not have any effect on the runtime, using a struct with a single int field does not cause any overhead against using int directly.

    Edit: Because you asked about T4, if you are working with Visual Studio, you can easily create a new T4 Text template that will expand to (C#)-code and will be compiled automatically. The template will be executed everytime you save it.