2008-11-14 13 views
14

Can I specify interfaces when I declare a member?¿Hay algún lenguaje estático de tipo pato?

Después de pensar en esta cuestión por un tiempo, se me ocurrió que un lenguaje de pato estático podría funcionar. ¿Por qué las clases predefinidas no pueden vincularse a una interfaz en tiempo de compilación? Ejemplo:

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. 

¿Conoce algún idioma que admita esta función? ¿Sería útil en Java o C#? ¿Es fundamentalmente defectuoso de alguna manera? Entiendo que podría subclase MyClass e implementar la interfaz o usar el patrón de diseño del Adaptador para lograr lo mismo, pero esos enfoques simplemente parecen un código repetitivo innecesario.

Respuesta

9

lenguajes de tipo estático, por definición, comprobar tipos en tiempo de compilación , no tiempo de ejecución. Uno de los problemas obvios con el sistema descrito anteriormente es que el compilador comprobará los tipos cuando se compile el programa, no en tiempo de ejecución.

Ahora, se podría construir más inteligencia en el compilador por lo que podría derivar tipos, en lugar de tener el programador explícitamente declaran tipos; el compilador podría ver que MyClass implementa un método MyMethod(), y manejar este caso en consecuencia, sin la necesidad de declarar explícitamente las interfaces (como usted sugiere). Tal compilador podría utilizar la inferencia de tipo, como Hindley-Milner.

Por supuesto, algunos lenguajes estáticos como Haskell ya hacen algo similar a lo que sugiere; el compilador Haskell puede inferir tipos (la mayoría de las veces) sin la necesidad de declararlos explícitamente. Pero obviamente, Java/C# no tiene esta habilidad.

+0

Gracias. Acabo de empezar a aprender Haskell hace una semana y hasta ahora parece bastante genial. Sin embargo, es un poco una curva de aprendizaje; nunca antes había llegado a los idiomas funcionales. De todos modos, su sistema de inferencia de tipo es muy parecido a lo que me refiero. – Cybis

+3

C# parece tener inferencia Tipo. Por ejemplo, este artículo habla de eso. http://www.developer.com/net/csharp/article.php/3601646/Go-Inside-C-30s-Type-Inference-Process.htm –

9

No veo el punto. ¿Por qué no ser explícito de que la clase implementa la interfaz y ha terminado con ella? La implementación de la interfaz es lo que le dice a otros programadores que se supone que esta clase se comporte de la forma que define la interfaz. Simplemente tener el mismo nombre y firma en un método no transmite ninguna garantía de que la intención del diseñador fue realizar acciones similares con el método. Eso puede ser, pero ¿por qué dejarlo para la interpretación (y el mal uso)?

La razón por la que puede "escaparse" con éxito en idiomas dinámicos tiene más que ver con TDD que con el idioma en sí. En mi opinión, si el lenguaje ofrece la posibilidad de dar este tipo de orientación a otras personas que usan/ven el código, debe usarlo. En realidad, mejora la claridad y vale la pena los pocos caracteres adicionales. En el caso donde no tenga acceso para hacer esto, entonces un Adaptador tiene el mismo propósito de declarar explícitamente cómo se relaciona la interfaz con la otra clase.

+0

Estoy de acuerdo, eso es el punto de una interfaz, para describir lo que algo es. Úselo donde sea que pueda, no es un código repetitivo, es una buena práctica. – Mark

+2

¿Por qué este patrón tiene éxito en los lenguajes dinámicos, pero se consideraría una mala práctica en los lenguajes estáticos? ¿Qué tiene que ver TDD con eso que es único para el tipado dinámico? Como mencionó @mipadi, ¿por qué los lenguajes OO no admiten una especie de sistema de inferencia de tipo al estilo Haskell? – Cybis

+0

Desde mi comprensión limitada de la misma, los genéricos de Java son solo una forma de hacer lo que equivale al tipo de pato. –

1

Un diseño de prelanzamiento para Visual Basic 9 tenía soporte para el tipado de pato estático usando interfaces dinámicas pero cortaban la característica * para enviar a tiempo.

2

Boo definitivamente es un pato-mecanografiado lenguaje estático: http://boo.codehaus.org/Duck+Typing

un extracto:

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

+1

Un lenguaje estático con mecanografía de pato no es lo mismo que pato estático mecanografía. Tanto el VB existente como el próximo C# 4.0 tienen tipado estático y tipado dinámico/tardío/pato –

+0

No me refiero a una mezcla de tipado estático y dinámico (que parece ser a lo que te refieres, torial). Me refiero a la inferencia de tipos: el compilador puede ver de inmediato que MyClass y IMyInterface son compatibles sin que el programador explicite la relación. – Cybis

+0

Mi mal. Boo no hará eso por ti. – torial

8

F # apoya la tipificación de pato estática, aunque con un inconveniente: hay que utilizar restricciones miembros. Los detalles están disponibles en este blog entry.

Ejemplo del blog citado:

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 
12

Cómo sobre el uso de plantillas en 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 

realidad no he compilado el código, pero yo creo que es viable y una bastante trivial C++ - zación del código original propuesto (pero no laborable).

+1

Alguien mencionó plantillas C++, pero creo que esa respuesta se eliminó. De todos modos, las plantillas son básicamente macros avanzadas. Se crea un nuevo "Método CallMyModhod" para cada tipo diferente que le pase, por lo que no es realmente una inferencia de tipo. – Cybis

+0

Además, no conozco un buen IDE de C++ con intellisense que no se confunda en las plantillas (Visual Studio no siempre enumera todas las opciones, y algunas veces se detiene por completo). – Cybis

+1

Para ser justos, el cartel original no mencionó la inferencia tipo * per se * en absoluto. Acabo de ajustar ligeramente su código y demostré que puede funcionar en C++ casi como está. –

4

La mayoría de los idiomas en el apoyo a la familia ML tipos estructurales con la inferencia y esquemas de tipo restringidos, que es la terminología del lenguaje diseñador de geek que parece más probable es que lo que quiere decir con la frase "estático pato-tipado "en la pregunta original.

Los idiomas más populares en esta familia que vienen a la mente incluyen: Haskell, Objective Caml, F # y Scala. El que más se acerque a tu ejemplo, por supuesto, sería Objective Caml. He aquí una traducción de su ejemplo:

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 

Nota: algunos de los nombres que ha utilizado tienen que ser cambiados para cumplir con la noción de la semántica de casos identificador de OCaml, pero por lo demás, se trata de una traducción bastante sencillo.

También, vale la pena señalar, ni la anotación de tipo en la función callMyMethod ni la definición del tipo de clase iMyInterface es estrictamente necesaria. Objetivo Caml puede inferir todo en su ejemplo sin ninguna declaración de tipo.

15

Una nueva respuesta a esta pregunta, Go has exactly this feature. Creo que es genial & inteligente (aunque me interesará ver cómo se desarrolla en la vida real) y felicitaciones al pensar en ello.

como se documenta 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.

+0

¿Podría vincular a la parte real de la documentación de Go que explica cómo funciona esto? Y, aún mejor, incluya un ejemplo aquí? –

+1

Como desee (pero solo vinculado al ejemplo) – Grumdrig

0

En la última versión de mi lenguaje de programación que soporta Heron algo similar a través de una coacción-subtipificación estructural operador llama as. Así que en lugar de:

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

lo tanto se escribe:

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

Al igual que en el ejemplo, en este caso MyClass no tiene que poner en práctica de manera explícita IMyInterface, pero si lo hizo el reparto podría suceder de forma implícita y el operador as podría omitirse.

Escribí un poco más sobre la técnica que llamo subtipificación estructural explícita en this article.

1

Las nuevas versiones de C++ se mueven en la dirección de tipado estático de patos. Puede que algún día (? Hoy) escribir algo como esto:

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

y éste deje de recopilar si no hay llamada de función correspondiente para x+y.

cuanto a su crítica:

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

pero es la inferencia de tipos (que se puede decir foo(bar) donde foo es una función de plantilla), y tiene el mismo efecto, excepto que es más eficiente en tiempo y necesita más espacio en el código compilado.

De lo contrario, tendría que buscar el método durante el tiempo de ejecución. Tendría que encontrar un nombre, luego verificar que el nombre tenga un método con los parámetros correctos.

O tendría que almacenar toda esa información sobre las interfaces que coinciden, y examinar cada clase que coincida con una interfaz, y luego agregar automáticamente esa interfaz.

En cualquier caso, eso le permite romper implícita y accidentalmente la jerarquía de clases, lo que es malo para una nueva característica porque va en contra de los hábitos de los programadores de C#/Java. Con las plantillas C++, ya sabes que estás en un campo minado (y también están agregando características ("conceptos") para permitir restricciones en los parámetros de la plantilla).

2

TypeScript!

Bien, bien ... Así que es un superconjunto javascript y puede que no constituye una "lengua", pero este tipo de pato tipos estáticos es vital a máquina de escribir.

enter image description here

1

Crystal es un lenguaje estáticamente pato-mecanografiado. Example:

def add(x, y) 
    x + y 
end 

add(true, false) 

la llamada a add causas de este error de compilación:

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

add(true, false) 
^~~ 

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

    x + y 
    ^
Cuestiones relacionadas