Czy istnieje język programowania zaprojektowany specjalnie do wstrzykiwania zależności?

21

Wiele ogólnych języków programowania jest wystarczająco elastycznych, aby umożliwić obsługę wstrzykiwania zależności. Nawet bez wsparcia biblioteki lub frameworka. Ale nawet jeśli język Turinga jest wystarczająco kompletny, aby rozwiązać każdy problem programistyczny, język dokonuje wyborów, które wpływają na to, co łatwe i trudne.

Czy jest jakiś język, który został specjalnie zaprojektowany, aby ułatwić wstrzykiwanie zależności, i odwrotnie, utrudnić tworzenie ukrytych zależności?

Wyjaśnienie:

Z powodu ograniczeń niektórych języków (patrząc na ciebie Javę) wiele osób uważa pomoc w okablowaniu i budowie za część wstrzykiwania zależności. Tutaj zamierzam tylko, aby język zaprojektowany dla DI oznaczał, że zależności nie są łatwo ukryte w skutkach ubocznych. Posiadanie konwencji w sprawie systemu konfiguracji również byłoby tylko dodaniem sosu.

Nie szukam rekomendacji językowej. To pytanie historyczne. Czy któryś autor języka kiedykolwiek wyraźnie to postanowił?

candied_orange
źródło
1
Silnie powiązane, jeśli nie wręcz duplikat: Jak można zintegrować wstrzykiwanie zależności z językiem?
Robert Harvey
Whee! 3K! Teraz mogę obserwować, jak głosują, by zamknąć to pytanie. :) Dzięki za głosy.
candied_orange
1
W każdym razie powinieneś przeczytać ten post. „Czy istnieje” jest znacznie mniej interesującym pytaniem, chyba że rozszerzysz je na coś w stylu „jak to zrobili?”
Robert Harvey,
1
Czy Haskell by się liczył? Dzięki funkcjom curry i wyższego rzędu możesz rozwiązać większość problemów, które DI zazwyczaj rozwiązuje w językach OOP, a ze względu na swoją czystość jesteś zmuszony do oddzielania efektów ubocznych, takich jak IO itp., A funkcje nie mogą magicznie wyczarować czegoś, czego nie zostały przekazane jako argument. Nie masz żadnych konwencji dotyczących konfiguracji, ale z drugiej strony powoli odchodzę od tego, nawet w moim kodzie OOP, ponieważ zauważyłem, że większości zespołów nie można ufać tym w projektach średnich i większych.
wasatz
1
@wasatz: Tak, liczy się Haskell. Większość „wzorców oprogramowania” to tak naprawdę tylko obejścia niedociągnięć w języku programowania.
Robert Harvey

Odpowiedzi:

21

Tak, rzeczywiście jest. Raczej.

Newspeak nie ma stanu statycznego ani globalnego. Oznacza to, że jedynym możliwym sposobem uzyskania dostępu do zależności jest jawne jej wstrzyknięcie. Oczywiście oznacza to, że język, a dokładniej IDE, musi ułatwić zastrzyk zależności, w przeciwnym razie język będzie bezużyteczny.

Tak więc język nie jest przeznaczony do DI, a konieczność DI jest konsekwencją projektu języka.

Jeśli nie ma stanu statycznego ani globalnego, nie możesz po prostu „wyciągnąć” do eteru i wyciągnąć coś. Na przykład w Javie struktura pakietu jest statyczna. Mogę tylko powiedzieć java.lang.Stringi mam Stringklasę. To nie jest możliwe w Newspeak. Wszystko , z czym pracujesz, musi być ci wyraźnie przekazane, w przeciwnym razie po prostu nie będziesz w stanie tego zrobić. Tak więc wszystko jest zależnością, a każda zależność jest jawna.

Chcesz sznurka? Cóż, najpierw musisz poprosić stdlibobiekt o przekazanie Stringklasy. Och, ale jak uzyskać dostęp do stdlib? Cóż, musisz najpierw poprosić platformo przekazanie ci stdlibobiektu. Och, ale jak uzyskać dostęp do platform? Musisz najpierw poprosić kogoś innego o przekazanie ci platformprzedmiotu. Och, ale jak uzyskać dostęp do tego, żeby ktoś się nie dowiedział? Cóż, najpierw musisz poprosić kogoś innego o przekazanie ci przedmiotu.

Jak daleko to sięga do króliczej nory? Gdzie kończy się rekurencja? Właściwie przez całą drogę. To się nie kończy. Jak zatem napisać program w Newspeak? Ściśle mówiąc, nie możesz!

Potrzebujesz jakiegoś zewnętrznego bytu, który łączy to wszystko razem. W Newspeak tym podmiotem jest IDE. IDE widzi cały program. Może łączyć ze sobą różne elementy. Standardowy wzorzec w aplikacji Newspeak polega na tym, że klasa centralna aplikacji ma wywoływany akcesor platform, a IDE Newspeak wstrzykuje do tego obiektu obiekt, który ma metody zwracające niektóre z podstawowych potrzeb programowania: Stringklasa, Numberklasa, Arrayklasa, i tak dalej.

Jeśli chcesz przetestować aplikację, możesz wstrzyknąć platformobiekt, którego Filemetoda zwraca klasę metodami pozorowanymi. Jeśli chcesz wdrożyć aplikację w chmurze, wstrzykujesz platformę, której Fileklasa faktycznie jest wspierana przez Amazon S3. Wieloplatformowe interfejsy GUI działają poprzez wstrzykiwanie różnych struktur GUI dla różnych systemów operacyjnych. Newspeak ma nawet eksperymentalny kompilator Newspeak-to-ECMAScript i ramę GUI opartą na HTML, która pozwala na przenoszenie w pełni funkcjonalnej aplikacji GUI z natywnego pulpitu do przeglądarki bez zmian, po prostu przez wstrzyknięcie różnych elementów GUI.

Jeśli chcesz wdrożyć aplikację, IDE może serializować aplikację do obiektu na dysku. (W przeciwieństwie do swojego przodka, Smalltalk, Newspeak ma format serializacji poza obrazem. Nie musisz zabierać całego obrazu ze sobą, właśnie dlatego, że wstrzykiwane są wszystkie zależności: IDE dokładnie wie , które części systemu twoja aplikacja używa, a czego nie. Tak więc serializuje dokładnie podłączony podgrupa przestrzeni obiektów, która zawiera twoją aplikację, nic więcej.)

Wszystko to działa po prostu poprzez skrajne zorientowanie obiektowe: wszystko jest wirtualnym wywołaniem metody (termin „wysyłanie wiadomości” w terminologii Smalltalk, którego potomkiem jest Newspeak). Nawet wyszukiwanie nadklasy jest wirtualnym wywołaniem metody! Weź coś takiego

class Foo extends Bar // using Java syntax for familiarity

lub w Newspeak:

class Foo = Bar () () : ()

W Javie spowoduje to utworzenie nazwy Foow statycznej globalnej przestrzeni nazw, wyszukiwanie Barw statycznej globalnej przestrzeni nazw i tworzenie Bar Foonadklasy. Nawet w Ruby, który jest znacznie bardziej dynamiczny, nadal będzie tworzyć stałą statyczną w globalnej przestrzeni nazw.

W Newspeak równoważna deklaracja oznacza: utwórz metodę gettera o nazwie Fooi spraw, aby zwróciła klasę, która wyszukuje swoją nadklasę, wywołując metodę o nazwie Bar. Uwaga: to nie jest jak Ruby, gdzie można umieścić dowolny wykonywalny kod Ruby jako deklarację nadklasy, ale kod zostanie wykonany tylko raz, gdy klasa zostanie utworzona, a zwracana wartość tego kodu stanie się stałą nadklasą. Nie. Metoda Barjest wywoływana dla każdego pojedynczego wyszukiwania metody!

Ma to głębokie implikacje:

  • ponieważ mixin jest w zasadzie klasą, która jeszcze nie zna swojej nadklasy, a w Newspeak superklasa jest dynamicznym wirtualnym wywołaniem metody, a zatem nieznana, każda klasa automatycznie jest również miksem. Dostajesz mixiny za darmo.
  • ponieważ klasa wewnętrzna jest tylko wywołaniem metody, która zwraca klasę, możesz zastąpić tę metodę w podklasie klasy zewnętrznej, więc każda klasa jest wirtualna. Otrzymujesz wirtualne zajęcia za darmo:

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

    Gazeta:

    class Outer = () (
      class Inner = () () : ()
    ) : ()
    
    class Sub = Outer () (
      class Inner = () () : ()
    ) : ()
    
  • ponieważ nadklasa jest tylko wywołaniem metody, która zwraca klasę, można przesłonić tę metodę w podklasie klasy zewnętrznej, klasy wewnętrzne zdefiniowane w nadklasie mogą mieć inną podklasę w podklasie. Otrzymujesz dziedziczenie hierarchii klas za darmo:

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

    Gazeta:

    class Outer = () (
      class MyCoolArray = Array () () : ()
    ) : ()
    
    class Sub = Outer () (
      class Array = () () : ()
    ) : ()
    
  • i na koniec najważniejsze w tej dyskusji: ponieważ (oprócz tych, które zdefiniowałeś w swojej klasie, oczywiście) możesz wywoływać metody tylko w klasie (klasach) leksykalnie otaczającej i swojej (klasach) superklasie, najbardziej zewnętrznej klasie najwyższego poziomu nie można wywołać żadnych metod na wszystkich z wyjątkiem tych, które są wyraźnie wstrzykiwana: klasa najwyższego poziomu nie ma klasę otaczającą którego metody mogą to nazwać, i nie może mieć superklasę innego niż domyślny, ponieważ deklaracja nadklasą jest wywołanie metody i oczywiście nie może przejść do nadklasy (tak jestnadklasa), a także nie może przejść do klasy zamykającej leksykalnie, ponieważ nie ma żadnej. Oznacza to, że klasy najwyższego poziomu są całkowicie enkapsulowane, mają dostęp tylko do tego, co zostały im wstrzyknięte, i otrzymują tylko to, o co wyraźnie proszą. Innymi słowy: klasy najwyższego poziomu są modułami. Otrzymujesz cały system modułów za darmo. Mówiąc dokładniej: klasy najwyższego poziomu są deklaracjami modułów, jego instancje są modułami. Otrzymujesz więc system modułowy z parametrycznymi deklaracjami modułów i moduły pierwszej klasy za darmo, czego nie może zrobić wiele, nawet bardzo wyrafinowanych systemów modułowych.

Aby uczynić cały ten zastrzyk bezbolesnym, deklaracje klasowe mają niezwykłą strukturę: składają się z dwóch deklaracji. Jednym z nich jest konstruktor klasy, który nie jest konstruktorem konstruującym instancje klasy, ale konstruktorem konstruującym środowisko, w którym działa ciało klasy. W składni podobnej do Java wyglądałby mniej więcej tak:

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

Gazeta:

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

Zauważ, że sposób, w jaki programista Newspeak rzeczywiście zobaczy klasy (klasy), wygląda następująco:Newspeak IDE wyświetla wiele zagnieżdżonych klas

Ale nie mogę nawet zacząć tego robić sprawiedliwie. Będziesz musiał się z tym bawić. Gilad Bracha wygłosił kilka rozmów na temat różnych aspektów systemu, w tym modułowości. Wygłosił naprawdę długą (2- godzinną ) rozmowę , której pierwsza godzina to dokładne wprowadzenie do języka, w tym historia modułowości. Rozdział 2 platformy programistycznej Newspeak dotyczy modułowości. Jeśli przejrzysz Newspeak na Squeak - A Guide for the Confplexed (alias Newspeak-101) , poczujesz system. Przykład Gazety według Przykładu jest dokumentem na żywo (tzn. Działa wewnątrz portu Newspeak-on-ECMASCript, każdy wiersz kodu jest edytowalny, każdy wynik można sprawdzić), wykazując podstawową składnię.

Ale tak naprawdę musisz się z tym bawić. To jest po prostu tak różne od wszystkich nurtu, a nawet większość języków spoza głównego nurtu, że jest to trudne do wyjaśnienia, to musi być doświadczony.

Jörg W Mittag
źródło
3
Meh Zabrania się używania stanu statycznego i globalnego, a można to powiedzieć o prawie każdym nowoczesnym języku programowania.
Robert Harvey,
Ciekawe, że wiele moich ręcznie wykonanych pojemników do iniekcji to statyczne fabryki. Wcześniej nie uważał tego za coś złego.
candied_orange
@ Jörg W jakikolwiek sposób możesz jeszcze raz wykonać kopię zapasową tej odpowiedzi? Poszukałem „wstrzykiwania zależności Newspeaklanguage.org” i wyszedłem pusty. Najbliższa rzecz, jaką mogłem znaleźć, to: news.ycombinator.com/item?id=9620561
candied_orange
@CandiedOrange: Byłem w trakcie poszerzania odpowiedzi, ale potem „prawdziwy świat” przeszkodził. Czy to jest lepsze?
Jörg W Mittag
3
@ JörgWMittag Holy crap! Cóż, to z pewnością „więcej”. Poczekaj, aż ocenię „lepiej”. Może potrzebować mocy wizyty w łazience, aby się z tym pogodzić.
candied_orange
7

Język programowania Wake został zaprojektowany do użycia wstrzykiwania zależności. Zasadniczo ma odpowiednik struktury wstrzykiwania zależności upieczonej w samym języku. Klasy definiują parametry, które oni needi providekompilator rejestrują.

Winston Ewert
źródło
6

Nie jest to praktycznie przydatny język, ale system opisany w tym artykule ma interesujący efekt: pozwala napisać klasę abstrakcyjną za pomocą klas / interfejsów abstrakcyjnych (w tym ich tworzenie). Następnie można stworzyć konkretną klasę, zastępując podklasy każdą klasę abstrakcyjną, której użyłeś w momencie tworzenia wystąpienia. Eliminuje to potrzebę wstrzykiwania zależności w co najmniej prostych przypadkach, na przykład (używając hipotetycznej wersji Javy rozszerzonej o tę funkcję) możemy pobrać ten kod:

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

i zamień Klienta i jego użycie na:

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

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

Zauważ, że upraszcza to użycie zależności zamiast tworzenia. Pozwala nam to również uniknąć wzorca fabrycznego (jako nowy mogę być tworzony na żądanie, kiedy tylko chcemy).

Jules
źródło
Ciekawy. Przypomina mi to członków typu Scali, które można również zastąpić w podklasach i pozostawić abstrakcję w nadklasie. Elementy typu abstrakcyjnego w połączeniu z adnotacjami typu własnego stanowią podstawę podejścia Scali do wstrzykiwania modułowości i zależności. Wcale nie jestem zaskoczony, że ten artykuł zacytowali zarówno Martin Odersky , projektant Scali, jak i Gilad Bracha , projektant Newspeak.
Jörg W Mittag
0

Nie korzystałem z niego, ale oficjalnym hasłem języka programowania Plastic jest „ Co się stanie, jeśli weźmiesz zastrzyk zależności i upieczesz go w języku programowania? ”. To wydaje się dość interesujące

B1CL0PS
źródło