Mônada do leitor para injeção de dependência: várias dependências, chamadas aninhadas

Quando perguntado sobre a Injeção de Dependência em Scala, muitas respostas apontam para o uso da Reader Monad, a da Scalaz ou apenas a sua. Existem vários artigos muito claros que descrevem os princípios básicos da abordagem (por exemplo,Discussão de Runar, Blog do Jason), mas não consegui encontrar um exemplo mais completo e não consigo ver as vantagens dessa abordagem, por exemplo. um DI "manual" mais tradicional (consulteo guia que escrevi) Provavelmente estou perdendo algum ponto importante, daí a pergunta.

Apenas como exemplo, vamos imaginar que temos essas classes:

trait Datastore { def runQuery(query: String): List[String] }
trait EmailServer { def sendEmail(to: String, content: String): Unit }

class FindUsers(datastore: Datastore) {
  def inactive(): Unit = ()
}

class UserReminder(findUser: FindUsers, emailServer: EmailServer) {
  def emailInactive(): Unit = ()
}

class CustomerRelations(userReminder: UserReminder) {
  def retainUsers(): Unit = {}
}

Aqui, estou modelando coisas usando classes e parâmetros de construtor, que funcionam muito bem com abordagens DI "tradicionais", mas esse design tem alguns aspectos positivos:

cada funcionalidade possui dependências claramente enumeradas. Nós assumimos que as dependências são realmente necessárias para que a funcionalidade funcione corretamenteas dependências estão ocultas nas funcionalidades, por exemplo aUserReminder não tem ideia de queFindUsers precisa de um armazenamento de dados. As funcionalidades podem estar em unidades de compilação separadasestamos usando apenas Scala puro; as implementações podem alavancar classes imutáveis, funções de ordem superior, os métodos da "lógica de negócios" podem retornar valores agrupados noIO mônada, se queremos capturar os efeitos etc.

Como isso pode ser modelado com a mônada do Reader? Seria bom manter as características acima, para que fique claro que tipo de dependências cada funcionalidade precisa e oculte as dependências de uma funcionalidade de outra. Observe que usarclasses é mais um detalhe de implementação; talvez a solução "correta" usando a mônada do Reader usasse outra coisa.

Eu encontrei umpergunta um pouco relacionada o que sugere:

usando um único objeto de ambiente com todas as dependênciasusando ambientes locaispadrão "parfait"mapas indexados por tipo

No entanto, além de ser (mas isso é subjetivo) um pouco complexo demais para uma coisa tão simples, em todas essas soluções, por exemplo, aretainUsers método (que chamaemailInactive, que chamainactive para encontrar os usuários inativos) precisaria saber sobre oDatastore dependência, para poder chamar corretamente as funções aninhadas - ou estou errado?

Em que aspectos o uso da Reader Monad para um "aplicativo de negócios" seria melhor do que apenas o uso de parâmetros de construtores?

questionAnswers(2)

yourAnswerToTheQuestion