21

Many general programming languages are flexible enough to allow you to support dependency injection. Even without library or framework support. But even if a language is Turing complete enough to solve any programming problem, a language makes choices that impact what is easy and what is hard to do in them.

Is there any language that was specifically designed to make dependency injection easy, and conversely, make creating hidden dependencies hard?

Clarification:

Due to limitations of some languages (looking at you Java) many people regard assistance with wiring and construction as part of dependency injection. Here I only intend a language designed for DI to mean that dependency's are not easily hidden in side effects. Having a convention over configuration system as well would only be added gravy.

I'm not looking for a language recommendation. It's a historical question. Has any language author ever explicitly set out to do this?

  • 1
    Strongly related, if not outright duplicate: How could dependency injection be integrated into the language? – Robert Harvey May 25 '16 at 23:13
  • Whee! 3K! Now I can watch them vote to close this question. :) Thanks for the votes. – candied_orange May 25 '16 at 23:15
  • 1
    You should read that post anyway. "Does it exist" is a far less interesting question, unless you expand it to something like "how did they do it?" – Robert Harvey May 25 '16 at 23:16
  • 1
    Would Haskell count? With currying and higher-order functions you can solve most problems that DI generally solves in OOP languages and with its strictness on purity you are forced to separate sideeffects like IO etc and functions cannot magically conjure up something that they havent been passed as an argument. You dont get any convention over configuration, but on the other hand I am slowly moving away from that even in my OOP code nowadays since I have noticed that most teams cannot be trusted with that on medium-sized projects and larger. – wasatz May 26 '16 at 13:04
  • 1
    @wasatz: Yes, Haskell counts. Most "software patterns" are really just workarounds for shortcomings in the programming language. – Robert Harvey Jun 1 '16 at 23:15
21

Yes, there is indeed. Sort of.

Newspeak has no static state and no global state. This means that the only possible way to get access to a dependency is to have it explicitly injected. Obviously, this means that the language, or in the case of Newspeak more precisely the IDE needs to make dependency injection easy, otherwise the language will be unusable.

So, the language is not designed for DI, rather the necessity for DI is a consequence of the language design.

If there is no static state and no global state, then you cannot just "reach out" in to the ether and pull something out. For example, in Java, the package structure is static state. I can just say java.lang.String and I have myself the String class. That is not possible in Newspeak. Everything you work with, has to be explicitly provided to you, otherwise you just can't get at it. So, everything is a dependency, and every dependency is explicit.

You want a string? Well, you have to first ask the stdlib object to hand you the String class. Oh, but how do you get access to the stdlib? Well, you have to first ask the platform to hand you the stdlib object. Oh, but how do you get access to the platform? Well, you have to first ask someone else to hand you the platform object. Oh, but how do you get access to that someone lese? Well, you have to first ask yet another someone else to hand you the object.

How far down the rabbit hole does this go? Where does the recursion stop? All the way, actually. It doesn't stop. Then, how can you write a program in Newspeak? Well, strictly speaking, you can't!

You need some outside entity that ties it all together. In Newspeak, that entity is the IDE. The IDE sees the whole program. It can wire the disparate pieces together. The standard pattern in Newspeak is that the central class of your application has an accessor called platform, and the Newspeak IDE injects an object into that accessor that has methods which return some of the basic necessities of programming: a String class, a Number class, an Array class, and so on.

If you want to test your application, you can inject a platform object whose File method returns a class with dummy methods. If you want to deploy your application to the cloud, you inject a platform whose File class actually is backed by Amazon S3. Cross-platform GUIs work by injecting different GUI frameworks for different OSs. Newspeak even has an experimental Newspeak-to-ECMAScript compiler and HTML-backed GUI framework that allows you to port a fully-featured GUI application from native desktop into the browser with no changes, just by injecting different GUI elements.

If you want to deploy your application, the IDE can serialize the application into an on-disk object. (Unlike its ancestor, Smalltalk, Newspeak has an out-of-image object serialization format. You don't have to take the entire image with you, precisely because all dependencies are injected: the IDE knows exactly which parts of the system your application uses and which it doesn't. So, it serializes exactly the connected subgraph of the object space that comprises your application, nothing more.)

All of this works simply by taking object-orientation to the extreme: everything is a virtual method call ("message send" in Smalltalk terminology, of which Newspeak is a descendant). Even the superclass lookup is a virtual method call! Take something like

class Foo extends Bar // using Java syntax for familiarity

or, in Newspeak:

class Foo = Bar () () : ()

In Java, this will create a name Foo in the static global namespace, and look up Bar in the static global namespace and make Bar Foo's superclass. Even in Ruby, which is much more dynamic, this will still create a static constant in the global namespace.

In Newspeak, the equivalent declaration means: create a getter method named Foo and make it return a class that looks up its superclass by calling the method named Bar. Note: this is not like Ruby, where you can put any executable Ruby code as the superclass declaration, but the code will only be executed once when the class is created and the return value of that code becomes the fixed superclass. No. The method Bar is called for every single method lookup!

This has some profound implications:

  • since a mixin is basically a class that doesn't know its superclass yet, and in Newspeak, the superclass is a dynamic virtual method call, and thus unknown, every class is automatically also a mixin. You get mixins for free.
  • since an inner class is just a method call that returns a class, you can override that method in a subclass of the outer class, so every class is virtual. You get virtual classes for free:

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

    Newspeak:

    class Outer = () (
      class Inner = () () : ()
    ) : ()
    
    class Sub = Outer () (
      class Inner = () () : ()
    ) : ()
    
  • since the superclass is just a method call that returns a class, you can override that method in a subclass of the outer class, inner classes defined in the superclass can have a different superclass in the subclass. You get class hierarchy inheritance for free:

    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`!!!
    }
    

    Newspeak:

    class Outer = () (
      class MyCoolArray = Array () () : ()
    ) : ()
    
    class Sub = Outer () (
      class Array = () () : ()
    ) : ()
    
  • and lastly, the most important for this discussion: since (apart from the ones you defined in your class, obviously) you can only call methods in your lexically enclosing class(es) and your superclass(es), a top-level outermost class cannot call any methods at all except the ones that are explicitly injected: a top-level class doesn't have an enclosing class whose methods it could call, and it cannot have a superclass other than the default one, because the superclass declaration is a method call, and it obviously can't go to the superclass (it is the superclass) and it also can't go to the lexically enclosing class, because there isn't any. What this means is the top-level classes are completely encapsulated, they can only access what they explicitly get injected, and they only get injected what they explicitly ask for. In other words: top-level classes are modules. You get an entire module system for free. In fact, to be more precise: top-level classes are module declarations, its instances are modules. So, you get a module system with parametric module declarations and first-class modules for free, something which many, even very sophisticated, module systems cannot do.

In order to make all of this injection painless, class declarations have an unusual structure: they consist of two declarations. One is the class constructor, which is not the constructor which constructs instances of the class, but rather the constructor that constructs the environment in which the class body runs. In a Java-like syntax, it would look something like this:

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"
}

Newspeak:

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

Note that the way a Newspeak programmer is actually going to see the class(es) is like this:Newspeak IDE displaying multiple nested classes

I can't even begin to do it justice, though. You'll have to play around with it yourself. Gilad Bracha has given a couple of talks about various aspects of the system, including modularity. He gave a really long (2hr) talk, the first hour of which is a thorough introduction to the language, including the modularity story. Chapter 2 of The Newspeak Programming Platform covers modularity. If you skim Newspeak on Squeak – A Guide for the Perplexed (aka Newspeak-101), you get a feel for the system. Newspeak by Example is a live document (i.e. it is running inside the Newspeak-on-ECMASCript port, every line of code is editable, every result is inspectable) demonstrating the basic syntax.

But really, you have to play around with it. It is just so different from all mainstream and even most non-mainstream languages that it is hard to explain, it has to be experienced.

  • 3
    Meh. Forbid the use of static state and global state, and you could say this about almost any modern programming language. – Robert Harvey May 25 '16 at 23:15
  • Curious, many of my hand made injection containers are static factories. Hadn't thought of that as a bad thing before. – candied_orange May 25 '16 at 23:30
  • @Jörg Any way you could back up this answer a little more? I've googled "newspeaklanguage.org dependency injection" and came up empty. Closest thing I could find was this: news.ycombinator.com/item?id=9620561 – candied_orange May 26 '16 at 0:29
  • @CandiedOrange: I was in the process of expanding the answer but then "real world" interfered. Is that better? – Jörg W Mittag May 26 '16 at 1:50
  • 3
    @JörgWMittag Holy crap! Well it's certainly "more". Hang on while I evaluate "better". Might need the power of a bathroom visit to get through this. – candied_orange May 26 '16 at 2:09
7

The Wake Programming language is designed to use dependency injection. Basically, it has the equivalent of a dependency injection framework baked into the language itself. Classes define the parameters they need and provide and the compiler hooks everything up.

6

It's not a practically useful language, but the system described in this paper.has an interesting effect: it allows you to write an abstract class using abstract classes/interfaces (including instantiating them) Your class can then be made concrete by substituting a subclasses of each abstract class you've used at the point of instantiation. Doing so removes the need for dependency injection in at least simple cases, for example (using a hypothetical version of Java extended with this feature) we could take this code:

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

and replace Client and its usage with:

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

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

Note that this simplifies the point of use of a dependency, rather than creation. It also allows us to avoid the Factory pattern (as a new I can be created on demand whenever we want).

  • Interesting. This reminds me of Scala's Type Members, which can also be overridden in subclasses, and left abstract in a superclass. Abstract Type Members combined with Self-Type Annotations form the basis of Scala's approach to modularity and dependency injection. I'm not at all surprised that this paper has been cited by both Martin Odersky, the designer of Scala, and Gilad Bracha, the designer of Newspeak. – Jörg W Mittag May 26 '16 at 11:32
0

I haven't used it, but the Plastic programming language's official tagline is "What happens if you take dependency injection and bake it into a programming language?". It seems pretty interesting

protected by gnat Jul 3 '18 at 5:34

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.