Are there any static duck-typed languages?
Asked Answered
S

15

21

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.

Shipworm answered 14/11, 2008 at 2:47 Comment(0)
M
10

Statically-typed languages, by definition, check types at compile time, not run time. One of the obvious problems with the system described above is that the compiler is going to check types when the program is compiled, not at run time.

Now, you could build more intelligence into the compiler so it could derive types, rather than having the programmer explicitly declare types; the compiler might be able to see that MyClass implements a MyMethod() method, and handle this case accordingly, without the need to explicitly declare interfaces (as you suggest). Such a compiler could utilize type inference, such as Hindley-Milner.

Of course, some statically typed languages like Haskell already do something similar to what you suggest; the Haskell compiler is able to infer types (most of the time) without the need to explicitly declare them. But obviously, Java/C# don't have this ability.

Milch answered 14/11, 2008 at 3:37 Comment(2)
Thanks. I've just started learning Haskell about a week ago and so far it seems pretty cool. It's a bit of a learning curve though - never really got into functional languages before. Anyway, its type inference system is a lot like what I'm referring to.Shipworm
C# seems to have Type inference. For example, this article talks about it. developer.com/net/csharp/article.php/3601646/…Raina
S
23

A brand new answer to this question, Go has exactly this feature. I think it's really cool & clever (though I'll be interested to see how it plays out in real life) and kudos on thinking of it.

As documented in the official documentation (as part of the Tour of Go, with example code):

Interfaces are implemented implicitly

A type implements an interface by implementing its methods. There is no explicit declaration of intent, no "implements" keyword.

Implicit interfaces decouple the definition of an interface from its implementation, which could then appear in any package without prearrangement.

Scupper answered 11/11, 2009 at 20:16 Comment(4)
Could you link to the actual part of the Go documentation which explains how this works? And, even better, include an example here?Elianaelianora
As you wish (but just linked to the example)Scupper
so what happened if there are 2 different interfaces having the same method signature?Supraorbital
@Supraorbital I’d guess MyClass would be accepted as the argument to a function regardless of which of the two interfaces the function uses in its parameter list, then.Jejunum
G
15

How about using templates in C++?

class IMyInterface  // Inheritance from this is optional
{
public:
  virtual void MyMethod() = 0;
}

class MyClass  // Does not explicitly implement IMyInterface
{
public:
  void MyMethod()  // But contains a compatible method definition
  {
    std::cout << "Hello, world!" "\n";
  }
}

template<typename MyInterface>
void CallMyMethod(MyInterface& m)
{
  m.MyMethod();  // instantiation succeeds iff MyInterface has MyMethod
}

MyClass obj;
CallMyMethod(obj);     // Automatically generate code with MyClass as 
                       // MyInterface

I haven't actually compiled this code, but I believe it's workable and a pretty trivial C++-ization of the original proposed (but nonworking) code.

Gillmore answered 23/11, 2008 at 21:21 Comment(7)
Someone did mention C++ templates, but I think that answer got deleted. Anyway, templates are basically just advanced macros. A new "CallMyMethod" is created for each different type you pass to it, so it's not really type inference.Shipworm
Furthermore, I don't know of a good C++ IDE with intellisense that doesn't get confused on templates (Visual Studio's doesn't always list all choices, and sometimes it stops entirely).Shipworm
To be fair, the original poster didn't mention type inference per se at all. I just tweaked his code slightly and showed that it can work in C++ pretty much as-is.Gillmore
I can't understand how people still think that C++ templates are just advanced macros, it's a turing complete programming mechanism, not a text replacement tool.Gabrielegabriell
@Gabrielegabriell real (syntactic) macros are turing-complete, too. And even more. You can do pretty much everything at compile time in Clojure, for example.Fazio
Among other things, C++ templates aren't even "advanced" macros. They're actually very basic macros at best. No language (or metalanguage) where you have to roll your own if can seriously be considered an advanced programming environment.Windmill
@Leushenko as of C++11, you don't have to: en.cppreference.com/w/cpp/types/conditional .Lynnalynne
Z
10

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.

Zimbabwe answered 14/11, 2008 at 3:9 Comment(6)
I agree, thats the point of an interface, to describe what something is. Use it whereever you can, its not boilerplate code, its good practice.Ophthalmic
Why is this pattern successful in dynamic languages, but would be considered bad practice in static languages? What does TDD have to do with it that's unique to dynamic typing? As @Milch mentioned, why can't OO languages support a sort of Haskell-style type-inference system?Shipworm
From my limited understanding of it, Java generics are just a round about way of doing what amounts to duck type.Ratan
@James: sorry, but no. Java generics are what amounts to automatic casts to/from the Object type. You can't write code like the one shown above.Fannie
I am unaware of any language or framework which would recognize the concept of "Collection of things which implement IFoo and IBar, unless everything one wanted to hold in the collection also implemented some interface IFooBar` which inherits IFoo and IBar. If no common type exists, the only thing code can do is define the collection as holding one type, and cast as needed to the other. From a static perspective, duck-typing would allow one to define the type of collection as {IFoo,IBar}, and have its items be regarded as satisfying both constraints.Squinty
what if the code has already been compiled and you don't have the source because it's not yours? how will you make it inherit an interface? you can't expect others to make the right decisions all the timeOtherworldly
M
10

Statically-typed languages, by definition, check types at compile time, not run time. One of the obvious problems with the system described above is that the compiler is going to check types when the program is compiled, not at run time.

Now, you could build more intelligence into the compiler so it could derive types, rather than having the programmer explicitly declare types; the compiler might be able to see that MyClass implements a MyMethod() method, and handle this case accordingly, without the need to explicitly declare interfaces (as you suggest). Such a compiler could utilize type inference, such as Hindley-Milner.

Of course, some statically typed languages like Haskell already do something similar to what you suggest; the Haskell compiler is able to infer types (most of the time) without the need to explicitly declare them. But obviously, Java/C# don't have this ability.

Milch answered 14/11, 2008 at 3:37 Comment(2)
Thanks. I've just started learning Haskell about a week ago and so far it seems pretty cool. It's a bit of a learning curve though - never really got into functional languages before. Anyway, its type inference system is a lot like what I'm referring to.Shipworm
C# seems to have Type inference. For example, this article talks about it. developer.com/net/csharp/article.php/3601646/…Raina
R
10

F# supports static duck typing, though with a catch: you have to use member constraints. Details are available in this blog entry.

Example from the cited blog:

let inline speak (a: ^a) =
    let x = (^a : (member speak: unit -> string) (a))
    printfn "It said: %s" x
    let y = (^a : (member talk: unit -> string) (a))
    printfn "Then it said %s" y

type duck() =
    member x.speak() = "quack"
    member x.talk() = "quackity quack"
type dog() =
    member x.speak() = "woof"
    member x.talk() = "arrrr"

let x = new duck()
let y = new dog()
speak x
speak y
Reavis answered 23/11, 2008 at 20:47 Comment(0)
R
6

TypeScript!

Well, ok... So it's a javascript superset and maybe does not constitute a "language", but this kind of static duck-typing is vital in TypeScript.

enter image description here

Rumrunner answered 27/11, 2014 at 17:2 Comment(2)
I think Typescript uses structural typing (which to be fair is very similar to ducktyping, only stricter)Ulna
TS actually goes even further than that - not only are classes implicitly assignable to matching interfaces, but a class is inherently an interface type itself, which means you can assign other types to them. Building at scale with this feature is wonderfully simple - substituting dependencies in tests and so on. Breezy. 😀Leclaire
S
4

Most of the languages in the ML family support structural types with inference and constrained type schemes, which is the geeky language-designer terminology that seems most likely what you mean by the phrase "static duck-typing" in the original question.

The more popular languages in this family that spring to mind include: Haskell, Objective Caml, F# and Scala. The one that most closely matches your example, of course, would be Objective Caml. Here's a translation of your example:

open Printf

class type iMyInterface = object
  method myMethod: unit
end

class myClass = object
  method myMethod = printf "Hello, world!"
end

let callMyMethod: #iMyInterface -> unit = fun m -> m#myMethod

let myClass = new myClass

callMyMethod myClass

Note: some of the names you used have to be changed to comply with OCaml's notion of identifier case semantics, but otherwise, this is a pretty straightforward translation.

Also, worth noting, neither the type annotation in the callMyMethod function nor the definition of the iMyInterface class type is strictly necessary. Objective Caml can infer everything in your example without any type declarations at all.

Shaffer answered 24/10, 2009 at 3:28 Comment(0)
T
3

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
    ^
Thralldom answered 31/5, 2016 at 22:53 Comment(0)
S
2

A pre-release design for Visual Basic 9 had support for static duck typing using dynamic interfaces but they cut the feature* in order to ship on time.

Sedulous answered 14/11, 2008 at 3:36 Comment(0)
K
2

Structural types in Scala does something like this.

See Statically Checked “Duck Typing” in Scala

Koehler answered 28/12, 2008 at 23:5 Comment(0)
U
2

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).

Urchin answered 9/5, 2014 at 6:24 Comment(0)
S
1

Boo definitely is a static duck-typed language: http://boo.codehaus.org/Duck+Typing

An excerpt:

Boo is a statically typed language, like Java or C#. This means your boo applications will run about as fast as those coded in other statically typed languages for .NET or Mono. But using a statically typed language sometimes constrains you to an inflexible and verbose coding style, with the sometimes necessary type declarations (like "x as int", but this is not often necessary due to boo's Type Inference) and sometimes necessary type casts (see Casting Types). Boo's support for Type Inference and eventually generics help here, but...

Sometimes it is appropriate to give up the safety net provided by static typing. Maybe you just want to explore an API without worrying too much about method signatures or maybe you're creating code that talks to external components such as COM objects. Either way the choice should be yours not mine.

Along with the normal types like object, int, string...boo has a special type called "duck". The term is inspired by the ruby programming language's duck typing feature ("If it walks like a duck and quacks like a duck, it must be a duck").

Stallfeed answered 14/11, 2008 at 3:38 Comment(3)
A static language with duck typing isn't the same as static duck typing. Both existing VB and the upcoming C# 4.0 have static typing and dynamic/late-bound/duck typingSedulous
I don't mean a mix of static and dynamic typing (which seems to be what you're referring to, torial). I'm referring to type-inference - the compiler being able to see immediately that MyClass and IMyInterface are compatible without the programmer making the relationship explicit.Shipworm
My bad. Boo will not do that for you.Stallfeed
N
1

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).

Nadda answered 15/4, 2014 at 7:48 Comment(0)
Z
0

Sounds like Mixins or Traits:
http://en.wikipedia.org/wiki/Mixin
http://www.iam.unibe.ch/~scg/Archive/Papers/Scha03aTraits.pdf

Zackzackariah answered 28/12, 2008 at 22:55 Comment(0)
W
0

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.

Whisk answered 22/12, 2009 at 20:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.