Dependency injection & design by interface / SMI

My initial reaction is that there’s something missing / inconsistent with the example:

  • the private do_write functions that are injected by use IWriter don’t appear to be called anywhere

  • the write_with functions in TextWriter aren’t called anywhere either

  • calling a function through this mechanism requires accessing the internals of an opaque type (with nw.writer)

There are some other parts that seem odd:

  • in TextWriter, @type wrt :: IWriter - this declares wrt as a type inhabited by exactly one member, the atom :"Elixir.IWriter". You’d typically see this kind of construct used to create an enum-like with multiple atoms (@type something :: :foo | :bar | :baz) but the compiler is perfectly fine with exactly one

  • similarly, TextWriter.t() is a struct where the only expected value of the writer field is that same atom. This is consistent with the typing on TextWriter.new/1 because it only promises to accept :"Elixir.IWriter" as an argument.

  • however, that means that this doesn’t satisfy the typespec: TextWriter.new(DummyWriter) since DummyWriter is not the expected atom


Regarding the overall concept, IMO it’s reimplementing OO awkwardly with “objects” like TextWriter needing to explicitly hold a vtable-like thing.

You may find the discussions around the removal of “tuple calls” from the BEAM back in 2017 interesting: