que monkey duck class interface language-design duck-typing static-typing

class - monkey - duck typing ruby



¿Hay algún lenguaje estático de tipo pato? (15)

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

Un experto:

Boo es un lenguaje estáticamente tipado, como Java o C #. Esto significa que sus aplicaciones boo se ejecutarán tan rápido como las codificadas en otros lenguajes estáticos para .NET o Mono. Pero el uso de un lenguaje estático a veces lo limita a un estilo de codificación inflexible y detallado, con las declaraciones de tipo a veces necesarias (como "x como int", pero esto no suele ser necesario debido a la inferencia de tipos de boo) y los tipos a veces necesarios (ver Casting Types). El soporte de Boo para Type Inference y eventualmente genéricos ayudan aquí, pero ...

A veces es apropiado renunciar a la red de seguridad proporcionada por el tipado estático. Tal vez solo quiera explorar una API sin preocuparse demasiado por las firmas de métodos o tal vez esté creando código que se comunique con componentes externos como objetos COM. De cualquier forma, la elección debe ser tuya y no mía.

Junto con los tipos normales como objeto, int, cadena ... boo tiene un tipo especial llamado "pato". El término está inspirado en la función de mecanografía del pato del lenguaje de programación ruby ​​("Si camina como un pato y grazna como un pato, debe ser un pato").

¿Puedo especificar interfaces cuando declaro un miembro?

Después de pensar en esta cuestión por un tiempo, se me ocurrió que un lenguaje de tipo 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.

¿Conoces algún idioma que soporte tal característica? ¿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.


Los lenguajes de tipo estático, por definición, comprueban los tipos en tiempo de compilación , no el 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, puede generar más inteligencia en el compilador para que pueda derivar tipos, en lugar de que el programador declare explícitamente los tipos; el compilador podría ver que MyClass implementa un método MyMethod() y manejar este caso en consecuencia, sin la necesidad de declarar interfaces explícitamente (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.


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


Un diseño preliminar 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.


F # admite el tipado de pato estático, aunque con un truco: debe usar restricciones de miembro. Los detalles están disponibles en esta entrada de blog .

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



La mayoría de los idiomas de la familia ML respaldan los tipos estructurales con inferencia y esquemas de tipos restringidos , que es la terminología geek del diseñador de lenguaje que parece más probable es lo que quiere decir con la frase "tipado de pato estático" 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. Aquí hay una traducción de tu 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 utilizó deben modificarse para cumplir con la noción de caso de identificador de OCaml, pero de lo contrario, esta es una traducción bastante sencilla.

Además, vale la pena señalar que 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.


En la última versión de mi lenguaje de programación, Heron admite algo similar a través de un operador de coerción de subtotificación estructural llamado as . Entonces, en lugar de:

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

Usted escribiría:

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

Al igual que en su ejemplo, en este caso MyClass no tiene que implementar explícitamente IMyInterface , pero si lo hiciera, el molde podría suceder implícitamente y el operador as podría omitirse.

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



Las nuevas versiones de C ++ se mueven en la dirección de tipificación de pato estática. Puede algún día (¿hoy?) Escribir algo como esto:

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

y no se podría compilar si no hay una llamada de función coincidente para x+y .

En cuanto a tu crítica:

Se crea un nuevo "Método CallMyModhod" para cada tipo diferente que le pase, por lo que no es realmente una inferencia de tipo.

Pero ES INferencia tipo (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 ocupa más espacio en el código compilado.

De lo contrario, tendrías 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 todas las clases que coinciden 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).



¡Mecanografiado!

Bueno, está bien ... Así que es un superconjunto de JavaScript y tal vez no constituye un "lenguaje", pero este tipo de tipado de pato estático es vital en TypeScript.


Una nueva respuesta a esta pregunta, Go tiene exactamente esta característica . Creo que es genial e inteligente (aunque me interesará ver cómo se desarrolla en la vida real) y felicitaciones al pensar en ello.

Tal como se documenta en la documentación oficial (como parte del Tour of Go, con código de ejemplo) :

Las interfaces se implementan implícitamente

Un tipo implementa una interfaz implementando sus métodos. No hay una declaración explícita de intención, ninguna palabra clave "implementa".

Las interfaces implícitas separan la definición de una interfaz de su implementación, que luego podría aparecer en cualquier paquete sin una disposición previa.


Crystal es un lenguaje estático tipo pato. Ejemplo :

def add(x, y) x + y end add(true, false)

La llamada a add causa 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 ^


¿Qué tal 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

Realmente no he compilado este código, pero creo que es viable y una C ++ - ización bastante trivial del código original propuesto (pero no funcional).