Comencemos con una diferencia simple y superficial entre su enfoque y el enfoque Reader
, que es que ya no es necesario esperar en config
en ninguna parte. Digamos que definir los siguientes conceptos sinónimo de tipo vagamente inteligente:
type Configured[A] = ConfigSource => A
Ahora bien, si alguna vez necesito un ConfigSource
para alguna función, por ejemplo una función que obtiene el n 'th cliente en la lista, puede declarar que funcionan como "configurado":
def nthClient(n: Int): Configured[Client] = {
config => config.clients(n)
}
Así que estamos esencialmente tirando de un config
de la nada, cada vez que necesita uno! Huele a inyección de dependencia, ¿verdad? Ahora digamos que queremos que las edades de los primeros, segundos y terceros clientes en la lista (suponiendo que existan):
def ages: Configured[(Int, Int, Int)] =
for {
a0 <- nthClient(0)
a1 <- nthClient(1)
a2 <- nthClient(2)
} yield (a0.age, a1.age, a2.age)
Para esto, por supuesto, necesita una definición adecuada de map
y flatMap
. No entraré en eso aquí, sino que simplemente diré que Scalaz (o Rúnar's awesome NEScala talk, o Tony's que ya ha visto) le ofrece todo lo que necesita.
El punto importante aquí es que la dependencia ConfigSource
y su llamada inyección están en su mayoría ocultas. La única "pista" que podemos ver aquí es que ages
es del tipo Configured[(Int, Int, Int)]
en lugar de simplemente (Int, Int, Int)
. No necesitamos hacer referencia explícita a config
en ningún lado.
Como acotación al margen, esta es la forma en que casi siempre gusta pensar en mónadas: que ocultan su efecto así que no es contaminar el flujo de su código, mientras que declarar explícitamente el efecto en la firma de tipo.En otras palabras, no necesita repetirse demasiado: usted dice "hey, esta función se ocupa del efecto X" en el tipo de devolución de la función, y no se meta más con eso.
En este ejemplo, por supuesto, el efecto es leer desde un entorno fijo. Otro efecto monádico con el que puede estar familiarizado es el manejo de errores: podemos decir que Option
oculta la lógica de manejo de errores mientras que hace explícita la posibilidad de errores en el tipo de su método. O, más o menos lo contrario de lo que se lee, la mónada Writer
oculta lo que estamos escribiendo al hacer explícita su presencia en el sistema de tipos.
Ahora, por fin, del mismo modo que normalmente necesitamos para arrancar un marco DI (en algún lugar fuera de nuestro flujo normal de control, como en un archivo XML), también es necesario para arrancar esta curiosa mónada. Seguramente vamos a tener algún punto de entrada lógico a nuestro código, tales como:
def run: Configured[Unit] = // ...
Termina siendo bastante simple: desde Configured[A]
es sólo un sinónimo de tipo para la función ConfigSource => A
, sólo se puede aplicar la función a su "entorno":
run(ConfigFileSource)
// or
run(DatabaseSource)
Ta-da! Por lo tanto, al contrastar con el enfoque DI tradicional al estilo Java, no tenemos ninguna "magia" que ocurra aquí. La única magia, por así decirlo, está encapsulada en la definición de nuestro tipo Configured
y la forma en que se comporta como una mónada. Lo que es más importante, el tipo de sistema nos mantiene honestos sobre qué inyección de dependencia de "reino" está ocurriendo en: cualquier cosa con el tipo Configured[...]
está en el mundo DI, y cualquier cosa sin él no lo es. Simplemente no obtenemos esto en la DI de la vieja escuela, donde todo es potencialmente administrado por la magia, por lo que no se sabe qué partes de su código son seguras para reutilizar fuera de un marco DI (por ejemplo, dentro las pruebas de su unidad, o en algún otro proyecto por completo).
actualización
: me escribió un blog post que explica Reader
con mayor detalle.
'val's * puede * modificarse mediante reflexión, por lo que es posible que su biblioteca de inyección de dependencia pueda" inyectar un val " – gerferra
@gerferra ¿cuál es el punto de val modificado por reflexión, si tenemos var? –
¿por qué no hacer 'Client' una clase con un argumento, por lo que la configuración se puede pasar a instancias de' Cliente'? –