6

This is part of a series of questions which focuses on a project called the Abstraction Project, which aims to abstract the concepts used in language design in the form of a framework.

Another page associated to it related to ease of use can be viewed here. The meta-topic associated to an inquiry about the framework and the proper place to post can be found here.

I'm writing a framework which describes the concepts within static high-level languages (like C#, VB.NET, et cetera). As a part of that, it involves synthesis of the structures that describe those concepts.

One question I had was about what's called Structural Typing, which I aim to implement through type-parameters (constructors with parameters would be a definite addition.) It's a concept that neither C# nor VB.NET implement.

The question I have today is: is structural typing in a hierarchical model necessary?

One small example that I can think of is models developed that rely on patterns, but don't necessarily have an interface to specify this pattern. This is useful in instances where you want to use the code for multiple base types without a common ancestor, but with similar structure.

If you wanted to display a list of items to the Console via their name, you'd need to write one method for the lowest common ancestor that defines that name property. Person, Control, and Type would all use different methods, as an example.

If implemented, the resulted code would look something like:

public static void DisplayNames<T>(IEnumerable<T> namedItems)
    where T has
    {
        ///<summary>Returns the item's name</summary>
        string Name { get; }
    }
{
    foreach (var namedItem in namedItems)
        Console.WriteLine(namedItem.Name);
}

The '[' and ']' are used for disambiguation reasons (it'd be difficult to try to discern the body of a type-parameter's structure from an interface's structure otherwise.)

Suggestions welcome.

  • 1
    Your brackets should really probably be braces. – Robert Harvey Jul 26 '11 at 23:38
  • Perhaps a good disambiguation would be adding the word 'has' to help clarify the context. This way if 'T' is required to be a class and have a series of members, the parser state would be easier to identify. An example being: where T : class, has { string Name { get; } } – Allen Clark Copeland Jr Jul 26 '11 at 23:44
  • I very much dislike the idea of duck-typing based on member names, but would like to see a framework support duck-typing based on combinations of interface and class constraints, e.g. be able to declare something as a `{Animal,IFoo,IBar}`. It's possible to define an interface `IFooBar` which combines `IFoo` and `IBar`, but such an interface will not be satisfied by something that implements `IFoo` and `IBar`, but doesn't expressly implement `IFooBar`. Also, there are a few things like cloneability, mutability, immutability, and abandonability which should be orthogonal to a class hierarchy. – supercat Oct 10 '12 at 03:41

3 Answers3

2

It looks like you are trying to combine compile-time safety with duck-typing.

C++ templates already do this automatically - you can attempt to call anything on a template's parametrized type and it will fail to compile if the template type does not support that call. Things are kept generic by another template feature: if you do not use a specific feature of a specific template instantiation (implying a specific parametrized type), then it doesn't matter that a definition exists which does not compile. If you don't use it, it's not an error. Example:

class MyClass 
  {
  public:
     MyClass() {}
     std::string GetName() const { return "MyClass"; }
  };

template<typename T>
class TMyTemplate
  {
  public:
    TMyTemplate()
      {
      std::cout << myClassInstance_.GetName();
      }

    void ConceptMemberFunction()
      {
      std::cout << myClassInstance_.ConceptFunction()
      }

  private:
    T myClassInstance_;
  };


  int main(void)
    {
    TMyTemplate<MyClass>  myTemplate; // Compiles just fine.
    // myTemplate.ConceptMemberFunction(); // Compile-time error
    }

This feature of templates can be used for compile-time "concept checking" (see boost::concept_check) - create and instantiate a template that "uses" the specific features that the concept requires - compilation will fail if the class does not support all the required features.

Are these the concepts you are referring to in your question?

Joris Timmermans
  • 8,998
  • 2
  • 36
  • 60
  • Similar; however, I think the major difference lies in how templates are handled versus how the system I'm planning on implementing would be handled. http://lhq.rpgsource.net/text/StructuralTypingExample.html Gives a good example of how it might be handled. The idea is there would need to be a comipler-based type-parameter equivalency, when two type-parameters are equivalent, the marshaller class that handles dispatch would implement the target type's bridge, and register it under the type closure implied in that generic combination. – Allen Clark Copeland Jr Jul 28 '11 at 08:48
  • The equivalency check would ensure that one bridge doesn't call another, which calls another, which calls another, and so on. If such a thing occurred, it would yield a poorer run-time than if you had used dynamic typing. Dynamic typing is great for static<->dynamic language interop, but poor compared to static language performance characteristics. – Allen Clark Copeland Jr Jul 28 '11 at 09:00
  • The major difference is: Templates, from what I understand replicate the full structure of the class/function. Since generics themselves do not do that, and share a common code base, the expansion point is in the dispatch of the generic closure used upon that generic method/type. So it's the 'message' handler that's replicated, not the inner code of the class. – Allen Clark Copeland Jr Jul 28 '11 at 09:10
1

Given that most of the above answers essentially provided more context as to the nature of what structural typing is, not so much focused on whether it was necessary, as the initial question stated. I will mark this answer as implicit and based upon the target audience of the language.

For certain programming patterns a structural typing model on top of a static typing model is warranted; however, at the same time the specific implementation or current implementations don't add relevant context to the question at hand as they are examples of it in use. They explicitly state it is possible, but don't relate how it being possible relates to the need aspect initially requested in the original question.

Thus we'll answer the question by the following: it's needed where appropriate in a development environment that provides explicit type-based implementation constraints as well as an environment where two separately developed libraries follow the same pattern but do not share a common ancestor or implement the same interfaces, this is where structural typing would come into play.

Use cases are easier to come up with given a language that promotes both kinds of typing as noted by @back2dos, but that in and of itself does not answer the need aspect, it answers the question of whether it can be done.

0

Well, it can be combined. Specifically haXe shows this.

This has some advantages:

  • It lowers coupling. The target of an abstraction doesn't need to know it is being abstracted. (Granted, you could use adapters, but in most languages, that's a lot of work).
  • It allows abstracting over class objects. In haXe you could do something as:
    typedef Singleton<T> = { function getInstance():T }, which allows abstracting over all classes (or instances or anonymous objects) that match this type.

It has some disadvantages:

  • It is quite hard to implement in a fast way (compared to interfaces). When compiled to static languages, haXe uses reflection to operate on such types, which is quite costly.
  • It is a relatively weak contract. I.e.: Is it legitimate to interpret everything, that has a float x and y property as a point? Proponents of duck-typing probably answer with a firm yes. I am not so sure.

One thing you should see is that function types are in fact a special kind of structural types, which allow abstraction over function values.

Interfaces do have advantages over structural types. An interface does not only describe a structure. It describes a role. When a class chooses to implement an interface, it means, it is suitable to play such a role. There is a sort of reliability to that, that structural types cannot give you. Just because something can quack, it isn't necessarily suitable to be used as a duck.
For function types, this becomes quite evident. If you take SomeType->Bool as a function type, this doesn't describe the role of the function. The function could be performing an operation (such as removing an item from a collection) and saying, whether it failed or not, but it could also be a test to be performed on an object, that can either pass or fail.

You could say structural subtyping is a good tool to maintain type safety in abstractions that elude all other type semantics provided by a language. It is a powerful tool, that should be used with care.

back2dos
  • 29,980
  • 3
  • 73
  • 114
  • The focus I'm planning on would essentially implement the structural typing through interfaces in the background. Reflection would be a last resort effort in the case where the types used in the generic closure have no dispatch marshal (i.e. bridge.) The entire reason I'd be implementing it in this way is the static validity of it (if the compiler enforces structural requirements on the types used.) – Allen Clark Copeland Jr Jul 30 '11 at 16:32
  • The view that it views because it `has a float x and a float y it is a point` is a bit off because it's not saying that, it's saying that it has an x and a y, it says that it has the structure set forth, not that it plays a role. You could say something with an x and a y _has a point_, but that's different and closer to my goal. That's why I'm not calling this duck typing, from my understanding duck typing and structural typing are different, one's dynamic, the other is verifiable and static. – Allen Clark Copeland Jr Jul 30 '11 at 16:37
  • @Alexander Morou: Duck typing is at runtime (and verifiable depending on the language), structural typing at compile time. Other than that, there is no real difference. Of course the type as such is not saying "it is a point". The type itself just defines a structure. However the whole point of it is, that some code operates on objects of that type. This is only meaningful, if you make implicit assumptions about what the structure means. In your example, you assume, that the `Name` names the `namedItem`, where in fact in the case of a `Person`, you might rather want the `FullName`. – back2dos Jul 30 '11 at 19:28
  • Depends, honestly. The general idea of the structural typing implied by this would be you'd specify a set of assumptions about the underlying model, so long as the types supplied to the generic method/type satisfy the structural constraints, it is considered valid. This given implementation assumes compile-time validation, and falls back to a duck-typing view when the compiler that was used does not support structural type constraints. I've updated the [structural typing example](http://lhq.rpgsource.net/text/StructuralTypingExample.html). It uses the fallback for run-time checking. – Allen Clark Copeland Jr Jul 30 '11 at 20:23