¿Existe un lenguaje de programación diseñado específicamente para la inyección de dependencia?

21

Muchos lenguajes de programación generales son lo suficientemente flexibles como para permitirle admitir la inyección de dependencia. Incluso sin soporte de biblioteca o marco. Pero incluso si un lenguaje es lo suficientemente completo como para resolver cualquier problema de programación, un lenguaje toma decisiones que afectan lo que es fácil y lo que es difícil de hacer en ellos.

¿Existe algún lenguaje diseñado específicamente para facilitar la inyección de dependencias y, por el contrario, dificultar la creación de dependencias ocultas?

Aclaración:

Debido a las limitaciones de algunos lenguajes (mirando Java), muchas personas consideran que la asistencia con el cableado y la construcción es parte de la inyección de dependencia. Aquí solo pretendo que un lenguaje diseñado para DI signifique que las dependencias no se ocultan fácilmente en los efectos secundarios. Tener una convención sobre el sistema de configuración también solo se agregaría salsa.

No estoy buscando una recomendación de idioma. Es una pregunta histórica. ¿Algún autor de idiomas se ha propuesto explícitamente hacer esto?

naranja confitada
fuente
1
Fuertemente relacionado, si no directamente duplicado: ¿Cómo podría integrarse la inyección de dependencia en el lenguaje?
Robert Harvey
Whee! 3K! Ahora puedo verlos votar para cerrar esta pregunta. :) Gracias por los votos.
candied_orange
1
Deberías leer esa publicación de todos modos. "¿Existe?" Es una pregunta mucho menos interesante, a menos que la expanda a algo como "¿cómo lo hicieron?"
Robert Harvey
1
¿Contaría Haskell? Con el currículum y las funciones de orden superior, puede resolver la mayoría de los problemas que DI generalmente resuelve en los lenguajes OOP y con su rigor en la pureza, se ve obligado a separar los efectos secundarios como IO, etc. y las funciones no pueden conjurar mágicamente algo que no se ha pasado como argumento. No obtienes ninguna convención sobre la configuración, pero por otro lado, me estoy alejando lentamente de eso incluso en mi código OOP hoy en día, ya que he notado que no se puede confiar en la mayoría de los equipos con eso en proyectos de tamaño mediano y más grandes.
wasatz
1
@wasatz: Sí, Haskell cuenta. La mayoría de los "patrones de software" son solo soluciones para las deficiencias en el lenguaje de programación.
Robert Harvey

Respuestas:

21

Sí, lo hay de hecho. Más o menos.

Newspeak no tiene estado estático ni estado global. Esto significa que la única forma posible de obtener acceso a una dependencia es inyectarla explícitamente. Obviamente, esto significa que el lenguaje, o en el caso de Newspeak más precisamente, el IDE necesita facilitar la inyección de dependencia, de lo contrario el lenguaje será inutilizable.

Entonces, el lenguaje no está diseñado para DI, más bien la necesidad de DI es una consecuencia del diseño del lenguaje.

Si no hay un estado estático ni un estado global, entonces no puede simplemente "alcanzar" el éter y extraer algo. Por ejemplo, en Java, la estructura del paquete es un estado estático. Solo puedo decir java.lang.Stringy tengo la Stringclase. Eso no es posible en Newspeak. Todo lo que trabajas tiene que ser provisto explícitamente, de lo contrario simplemente no puedes hacerlo. Entonces, todo es una dependencia, y cada dependencia es explícita.

¿Quieres una cuerda? Bueno, primero debes pedirle al stdlibobjeto que te entregue la Stringclase. Ah, pero ¿cómo se accede a la stdlib? Bueno, primero debes pedirle platformque te entregue el stdlibobjeto. Ah, pero ¿cómo se accede a la platform? Bueno, primero debes pedirle a alguien que te entregue el platformobjeto. Oh, pero ¿cómo se accede a alguien menos? Bueno, primero debes pedirle a otra persona que te entregue el objeto.

¿Hasta dónde llega el agujero del conejo? ¿Dónde se detiene la recursividad? Todo el camino, en realidad. No para Entonces, ¿cómo puedes escribir un programa en Newspeak? Bueno, estrictamente hablando, ¡no puedes!

Necesitas alguna entidad externa que lo una todo. En Newspeak, esa entidad es el IDE. El IDE ve todo el programa. Puede unir las piezas dispares juntas. El patrón estándar en Newspeak es que la clase central de su aplicación tiene un acceso llamado platform, y el IDE Newspeak inyecta un objeto en ese acceso que tiene métodos que devuelven algunas de las necesidades básicas de programación: una Stringclase, una Numberclase, una Arrayclase, y así.

Si desea probar su aplicación, puede inyectar un platformobjeto cuyo Filemétodo devuelva una clase con métodos ficticios. Si desea implementar su aplicación en la nube, inyecte una plataforma cuya Fileclase en realidad esté respaldada por Amazon S3. Las GUI multiplataforma funcionan mediante la inyección de diferentes marcos de GUI para diferentes sistemas operativos. Newspeak incluso tiene un compilador experimental Newspeak-to-ECMAScript y un marco GUI respaldado por HTML que le permite portar una aplicación GUI con todas las funciones desde el escritorio nativo al navegador sin cambios, simplemente inyectando diferentes elementos GUI.

Si desea implementar su aplicación, el IDE puede serializar la aplicación en un objeto en el disco. (A diferencia de su antepasado, Smalltalk, Newspeak tiene un formato de serialización de objetos fuera de imagen. No tiene que llevar la imagen completa con usted, precisamente porque se inyectan todas las dependencias: el IDE sabe exactamente qué partes del sistema su aplicación utiliza y lo que no hace. Por lo tanto, serializa exactamente el subgrafo conectado del espacio de objetos que comprende su aplicación, nada más).

Todo esto funciona simplemente llevando la orientación a objetos al extremo: todo es una llamada de método virtual ("envío de mensaje" en la terminología de Smalltalk, de la cual Newspeak es un descendiente). ¡Incluso la búsqueda de superclase es una llamada de método virtual! Toma algo como

class Foo extends Bar // using Java syntax for familiarity

o, en Newspeak:

class Foo = Bar () () : ()

En Java, esto creará un nombre Fooen el espacio de nombres global estático, y buscará Baren el espacio de nombres global estático y creará la Bar Foosuperclase. Incluso en Ruby, que es mucho más dinámico, esto seguirá creando una constante estática en el espacio de nombres global.

En Newspeak, la declaración equivalente significa: crear un método getter llamado Fooy hacer que devuelva una clase que busque su superclase llamando al método named Bar. Nota: esto no es como Ruby, donde puede poner cualquier código Ruby ejecutable como la declaración de superclase, pero el código solo se ejecutará una vez cuando se cree la clase y el valor de retorno de ese código se convierta en la superclase fija. No. ¡Se Barllama al método para cada búsqueda de método!

Esto tiene algunas implicaciones profundas:

  • dado que un mixin es básicamente una clase que todavía no conoce su superclase, y en Newspeak, la superclase es una llamada de método virtual dinámico y, por lo tanto, se desconoce, cada clase también es automáticamente un mixin. Obtienes mixins gratis.
  • Como una clase interna es solo una llamada a un método que devuelve una clase, puede anular ese método en una subclase de la clase externa, por lo que cada clase es virtual. Obtienes clases virtuales gratis:

    class Outer {
      class Inner { /* … */ }
    }
    
    class Sub extends Outer {
      override class Inner { /* … */ }
    }
    

    Neolengua:

    class Outer = () (
      class Inner = () () : ()
    ) : ()
    
    class Sub = Outer () (
      class Inner = () () : ()
    ) : ()
    
  • Como la superclase es solo una llamada a un método que devuelve una clase, puede anular ese método en una subclase de la clase externa, las clases internas definidas en la superclase pueden tener una superclase diferente en la subclase. Obtiene la herencia de jerarquía de clases de forma gratuita:

    class Outer {
      class MyCoolArray extends Array { /* … */ }
    }
    
    class Sub extends Outer {
      override class Array { /* … */ }
      // Now, for instances of `Sub`, `MyCoolArray` has a different superclass 
      // than for instances of `Outer`!!!
    }
    

    Neolengua:

    class Outer = () (
      class MyCoolArray = Array () () : ()
    ) : ()
    
    class Sub = Outer () (
      class Array = () () : ()
    ) : ()
    
  • y, por último, lo más importante para esta discusión: dado que (aparte de los que definió en su clase, obviamente) solo puede llamar a los métodos en su (s) clase (s) que encierra léxicamente y su (s) superclase (s), una clase más externa de nivel superior no puede llamar a cualquier método en absoluto , excepto los que se inyectan de forma explícita: una clase de nivel superior no tiene una clase envolvente cuyos métodos se podría llamar, y no puede tener una superclase que no sea el predeterminado, ya que la declaración superclase es una llamada al método, y obviamente no puede ir a la superclase ( esla superclase) y tampoco puede ir a la clase léxica, porque no hay ninguna. Lo que esto significa es que las clases de nivel superior están completamente encapsuladas, solo pueden acceder a lo que se les inyecta explícitamente y solo se les inyecta lo que piden explícitamente. En otras palabras: las clases de nivel superior son módulos. Obtiene un sistema de módulos completo de forma gratuita. De hecho, para ser más precisos: las clases de nivel superior son declaraciones de módulos, sus instancias son módulos. Entonces, obtienes un sistema de módulos con declaraciones de módulos paramétricos y módulos de primera clase de forma gratuita, algo que muchos sistemas de módulos, incluso muy sofisticados, no pueden hacer.

Para que toda esta inyección sea indolora, las declaraciones de clase tienen una estructura inusual: consisten en dos declaraciones. Uno es el constructor de la clase, que no es el constructor que construye las instancias de la clase, sino el constructor que construye el entorno en el que se ejecuta el cuerpo de la clase. En una sintaxis similar a Java, se vería así:

class Foo(platform) extends Bar {
  Array  = platform.collections.Array
  String = platform.lang.String
  File   = platform.io.File
| // separator between class constructor and class body
  class MyArray extends Array { /* … */ }
  // Array refers to the method defined above which in turn gets it from the 
  // platform object that was passed into the class "somehow"
}

Neolengua:

class Foo using: platform = Bar (
  Array = platform collections Array
  String = platform streams String 
  File = platform files ExternalReadWriteStream
) (
  class MyArray = Array () () : ()
) : ()

Tenga en cuenta que la forma en que un programador de Newspeak realmente verá las clases es así:Newspeak IDE que muestra múltiples clases anidadas

Sin embargo, ni siquiera puedo comenzar a hacerle justicia. Tendrás que jugar con eso tú mismo. Gilad Bracha ha dado un par de charlas sobre varios aspectos del sistema, incluida la modularidad. Dio una charla realmente larga (2 horas) , la primera hora de las cuales es una introducción completa al lenguaje, incluida la historia de la modularidad. El capítulo 2 de la plataforma de programación Newspeak cubre la modularidad. Si hojeas Newspeak en Squeak: una guía para Perplexed (también conocido como Newspeak-101) , obtienes una idea del sistema. Newspeak by Example es un documento en vivo (es decir, se ejecuta dentro del puerto Newspeak-on-ECMASCript, cada línea de código es editable, cada resultado es inspeccionable) que demuestra la sintaxis básica.

Pero realmente, tienes que jugar con eso. Es tan diferente de todos los idiomas principales e incluso de la mayoría de los idiomas no convencionales que es difícil de explicar, tiene que ser experimentado.

Jörg W Mittag
fuente
3
Meh Prohibir el uso del estado estático y el estado global, y podría decir esto sobre casi cualquier lenguaje de programación moderno.
Robert Harvey
Curioso, muchos de mis envases de inyección hechos a mano son fábricas estáticas. No había pensado en eso como algo malo antes.
candied_orange
@ Jörg ¿Alguna forma de respaldar esta respuesta un poco más? Busqué en Google "inyección de dependencia de newspeaklanguage.org" y salí vacío. Lo más cercano que pude encontrar fue esto: news.ycombinator.com/item?id=9620561
candied_orange
@CandiedOrange: estaba en el proceso de expandir la respuesta pero luego "el mundo real" interfirió. ¿Eso está mejor?
Jörg W Mittag
3
@ JörgWMittag Santa mierda! Bueno, ciertamente es "más". Espera mientras evalúo "mejor". Podría necesitar el poder de una visita al baño para superar esto.
candied_orange
7

El lenguaje de programación Wake está diseñado para usar la inyección de dependencia. Básicamente, tiene el equivalente de un marco de inyección de dependencia integrado en el lenguaje mismo. Clases definen los parámetros que needy providey los ganchos del compilador todo.

Winston Ewert
fuente
6

No es un lenguaje prácticamente útil, pero el sistema descrito en este documento tiene un efecto interesante: le permite escribir una clase abstracta usando clases / interfaces abstractas (incluidas las instancias). Su clase puede concretarse sustituyendo una subclase de cada clase abstracta que has usado en el punto de instanciación. Al hacerlo, se elimina la necesidad de la inyección de dependencia en al menos casos simples, por ejemplo (usando una versión hipotética de Java extendida con esta función) podríamos tomar este código:

public interface I {
    void something ();
)

public class Concrete implements I {
    public void something () { ... }
}

public class Client {
    I myI;
    public Client (I injected) { myI = injected; }
    ...
}

...

    Client c = new Client (new Concrete());
    ...

y reemplazar el Cliente y su uso con:

public class Client {
   I myI = new I();
   ...
}

   Client c = new Client { I -> Concrete } ();

Tenga en cuenta que esto simplifica el punto de uso de una dependencia, en lugar de la creación. También nos permite evitar el patrón Factory (como nuevo, puedo ser creado a pedido cuando lo deseemos).

Jules
fuente
Interesante. Esto me recuerda a los miembros tipo de Scala, que también se pueden anular en subclases y dejar abstractos en una superclase. Los miembros de tipo abstracto combinados con las anotaciones de tipo automático forman la base del enfoque de Scala para la inyección de modularidad y dependencia. No me sorprende en absoluto que este documento haya sido citado por Martin Odersky , el diseñador de Scala, y Gilad Bracha , el diseñador de Newspeak.
Jörg W Mittag
0

No lo he usado, pero el eslogan oficial del lenguaje de programación Plastic es " ¿Qué sucede si tomas la inyección de dependencia y la conviertes en un lenguaje de programación? ". Parece bastante interesante

B1CL0PS
fuente