6

Implicit typing in languages like C# permits compile-time type checks without necessarily referring "directly" to a target class:

var toy = SantasWorkshop.Build();
toy.amuse();

In this scenario, we don't need to know what type of toy it is. And yet, we'll still know at compile-time whether our toy fails to amuse().

However, this is limited to the local scope. If we want to put that toy into a Stocking and retain the compile-time checks, we need to know something about it at compile time -- other than it's ability to amuse(). E.g., this is illegal:

class Stocking {
  List<var> toys;
  public void add(var toy) {
    toys.add(toy);
  }
}

If we're willing to sacrifice on the compile-time check, languages like C# allow us to use dynamic typing:

class QuestionableLookingStocking {
  List<dynamic> toysIHope;
  public void add(dynamic toyIHope) {
    toysIHope.add(toyIHope);
  }
}

But, this will allow us to add objects to a QuestionableLookingStocking that may not actually amuse(). And we won't know about it until someone adds Coal at runtime.

class Coal {
  public ChristmasMood ruinChristmas() {
    return new SadChristmas();
  }
}

Would it be logically possible and feasible for implicit typing to be extended to members? I.e., is there any reason the compiler couldn't complain if someone tries to Stocking.add() an object that doesn't support the operations required by all references to Stocking.toys[n]?

Are there patterns that could simulate implicit member types?

  • 2
    I don't know if they go all the way, but languages in the ML family (ML, OCaml, F#) do much, much heavier implicit typing than C#. The inputs to functions are also typically implicitly typed. I don't think it extends as far as "class members" (or records, as it were), – KChaloux Dec 20 '16 at 19:06
  • 1
    Look however at Ocaml type system ; it has a particular typing for objects. – Basile Starynkevitch Dec 20 '16 at 19:22
  • 1
    Strongly related: stackoverflow.com/q/612689 – Robert Harvey Dec 20 '16 at 20:42
  • This would be inherently impossible as you might not have the code available. What happens when the information you need is in another .DLL for which you don't have the source? – Loren Pechtel Dec 20 '16 at 23:21
  • @LorenPechtel If you can flesh that out a little, "It's impossible" is a totally legitimate answer. – svidgen Dec 20 '16 at 23:23
3

You can simulate all of this in C# using generics.

This code is legal:

class Stocking<T> {
  List<T> toys;
  public void add(T toy) {
    toys.add(toy);
  }
}

And I'm pretty sure it fulfills all of your established objectives: you get compile-time type safety over all of your members, and every toy is an object that supports the operations required by all references to Stocking.toys.

Naturally, you're not going to get any additional capabilities from each toy beyond what is already provided by type T, which is probably what you're really after. So how about trying something like this:

public class Toy
{
    public string Name { get; set; }
}

public class Truck: Toy
{
    public void Dump()
    {
        Console.WriteLine("Dumping");
    }
}

public class Stocking<T>
{
    List<T> toys = new List<T>();
    public void Add(T toy)
    {
        toys.Add(toy);
    }
    public T Get(int index)
    {
        return toys[index];
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        var s = new Stocking<Toy>();
        var t = new Truck() { Name = "Tonka" };

        s.Add(t);
        var toy = s.Get(0);

        // Generic Toy behavior
        Console.WriteLine(toy.Name);

        // Truck-specific behavior
        if (toy is Truck)
        {
            (toy as Truck).Dump();
        }
        Console.ReadLine();
    }
}

This code outputs

Tonka
Dumping

Which is exactly what you would expect.

  • 1
    But this is not what the OP asked for, as it's simply moving the explicit type definition from one place to another. – Mason Wheeler Dec 20 '16 at 20:28
  • The var is still there; it's just implied. I've added a couple more lines of code in the Main method to clarify. – Robert Harvey Dec 20 '16 at 20:30
  • 1
    Let me clarify: var let's me operate as though I'm using an interface that doesn't actually exist anywhere. Generics require that A) an interface or base class actually exists, and B) everything that could go into the Stocking implements it explicitly. I'm skeptical (but hopeful) that a trick or pattern exists, or that it's a potential language feature, that my "anonymous pseudo-interface" could actually be constituted at compile-time, and enforced in a manner that doesn't require explicit inheritance. – svidgen Dec 20 '16 at 20:33
  • In C#, you can't enforce at compile time what isn't declared at compile time. Haskell might do this; it's type system is much more powerful than the C# type system; I suggest you look there. In any case, I consider the ability to check the supertype at runtime a reasonable compromise; since you really have no idea what SantasWorkshop.Build() might return, the only way to know that at compile time is to declare a concrete return type (i.e. Toy). – Robert Harvey Dec 20 '16 at 20:37
  • 4
    Also, to clarify: type inference doesn't use an implied interface that doesn't actually exist; it uses an actual, declared "interface." What happens is that the compiler works out the type of the expression declared on the right-hand side of the equals sign, and that's the type of the object that is assigned to the var variable. var doesn't even exist in the compiled IL. – Robert Harvey Dec 20 '16 at 20:48
0

To expand upon Robert Harvey's answer:

You don't need to stop at Toy.

public class Possession
{
    public String Name {get; set;}

    public abstract void Use();
}

public class Toy : Possession
{
    public void Amuse()
    {
        Use();
    }
}

public class Truck : Toy
{
}

public class Dumptruck : Truck
{
    public override void Use()
    {
        Console.WriteLine("Dumping");
    }
}

public class Child
{
    public String Name {get; set;}
    public List<Possession> Owns;
    public List<Toy> Stocking;

    public void AddToStocking(Possession Item)
    {
        if (Item is Toy)
            Stocking.Add(Item);
        else
            throw new GrinchException($"{Item.Name} is not a toy, why are you trying to put it in {Name}'s stocking?");
    }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.