Are there any static duck-typed languages?

2019-01-22 13:50发布

Can I specify interfaces when I declare a member?

After thinking about this question for a while, it occurred to me that a static-duck-typed language might actually work. Why can't predefined classes be bound to an interface at compile time? Example:

public interface IMyInterface
{
  public void MyMethod();
}

public class MyClass  //Does not explicitly implement IMyInterface
{
  public void MyMethod()  //But contains a compatible method definition
  {
    Console.WriteLine("Hello, world!");
  }
}

...

public void CallMyMethod(IMyInterface m)
{
  m.MyMethod();
}

...

MyClass obj = new MyClass();
CallMyMethod(obj);     // Automatically recognize that MyClass "fits" 
                       // MyInterface, and force a type-cast.

Do you know of any languages that support such a feature? Would it be helpful in Java or C#? Is it fundamentally flawed in some way? I understand you could subclass MyClass and implement the interface or use the Adapter design pattern to accomplish the same thing, but those approaches just seem like unnecessary boilerplate code.

15条回答
等我变得足够好
2楼-- · 2019-01-22 14:23

D (http://dlang.org) is a statically compiled language and provides duck-typing via wrap() and unwrap() (http://dlang.org/phobos-prerelease/std_typecons.html#.unwrap).

查看更多
欢心
3楼-- · 2019-01-22 14:24

Crystal is a statically duck-typed language. Example:

def add(x, y)
  x + y
end

add(true, false)

The call to add causes this compilation error:

Error in foo.cr:6: instantiating 'add(Bool, Bool)'

add(true, false)
^~~

in foo.cr:2: undefined method '+' for Bool

  x + y
    ^
查看更多
霸刀☆藐视天下
4楼-- · 2019-01-22 14:25

I don't see the point. Why not be explicit that the class implements the interface and have done with it? Implementing the interface is what tells other programmers that this class is supposed to behave in the way that interface defines. Simply having the same name and signature on a method conveys no guarantees that the intent of the designer was to perform similar actions with the method. That may be, but why leave it up for interpretation (and misuse)?

The reason you can "get away" with this successfully in dynamic languages has more to do with TDD than with the language itself. In my opinion, if the language offers the facility to give these sorts of guidance to others who use/view the code, you should use it. It actually improves clarity and is worth the few extra characters. In the case where you don't have access to do this, then an Adapter serves the same purpose of explicitly declaring how the interface relates to the other class.

查看更多
倾城 Initia
5楼-- · 2019-01-22 14:25

In the latest version of my programming language Heron it supports something similar through a structural-subtyping coercion operator called as. So instead of:

MyClass obj = new MyClass();
CallMyMethod(obj);

You would write:

MyClass obj = new MyClass();
CallMyMethod(obj as IMyInterface);

Just like in your example, in this case MyClass does not have to explicitly implement IMyInterface, but if it did the cast could happen implicitly and the as operator could be omitted.

I wrote a bit more about the technique which I call explicit structural sub-typing in this article.

查看更多
6楼-- · 2019-01-22 14:27

Structural types in Scala does something like this.

See Statically Checked “Duck Typing” in Scala

查看更多
叛逆
7楼-- · 2019-01-22 14:28

New versions of C++ move in the direction of static duck typing. You can some day (today?) write something like this:

auto plus(auto x, auto y){
    return x+y;
}

and it would fail to compile if there's no matching function call for x+y.

As for your criticism:

A new "CallMyMethod" is created for each different type you pass to it, so it's not really type inference.

But it IS type inference (you can say foo(bar) where foo is a templated function), and has the same effect, except it's more time-efficient and takes more space in the compiled code.

Otherwise, you would have to look up the method during runtime. You'd have to find a name, then check that the name has a method with the right parameters.

Or you would have to store all that information about matching interfaces, and look into every class that matches an interface, then automatically add that interface.

In either case, that allows you to implicitly and accidentally break the class hierarchy, which is bad for a new feature because it goes against the habits of what programmers of C#/Java are used to. With C++ templates, you already know you're in a minefield (and they're also adding features ("concepts") to allow restrictions on template parameters).

查看更多
登录 后发表回答