This page is no longer maintained — Please continue to the home page at www.scala-lang.org

Dependency Injection

48 replies
Ross Rose
Joined: 2011-09-10,
User offline. Last seen 7 hours 26 min ago.

A debate started on the scala-language group that I am moving to this
group as it is more fitting here. The matter of DI was discussed, and
I am actively porting the DI portions of Spring to scala to answer a
question of practicality. In this effort, the resulting solution
(which I have dubbed "Recoil") may end up not looking anything like
the original framework. The requirements that spawned this are a
matter of practicality around updating large solutions that have
already been put into production use. My goal is to see if an object-
functional framework can be devised that allows for zero updates to
existing solutions so as to maintain a single codebase within a global
scale enterprise. It is hoped that such a solution will allow for
domain specific rules to be injected in order to meet the needs of
multiple different domains without apriori knowledge of said rules.

Billy

Tony Morris 2
Joined: 2009-03-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Dependency Injection

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 09/12/2011 09:38 PM, Billy wrote:
> A debate started on the scala-language group that I am moving to this
> group as it is more fitting here. The matter of DI was discussed, and
> I am actively porting the DI portions of Spring to scala to answer a
> question of practicality. In this effort, the resulting solution
> (which I have dubbed "Recoil") may end up not looking anything like
> the original framework. The requirements that spawned this are a
> matter of practicality around updating large solutions that have
> already been put into production use. My goal is to see if an object-
> functional framework can be devised that allows for zero updates to
> existing solutions so as to maintain a single codebase within a global
> scale enterprise. It is hoped that such a solution will allow for
> domain specific rules to be injected in order to meet the needs of
> multiple different domains without apriori knowledge of said rules.
>
> Billy

[moving to scala-debate per agreed request]

I don't recall your constraints so cannot answer your question.

Someone (I forget who) recently wrote (I forget where): "DI is just
socially acceptable global variables." This is mostly true -- I say
mostly because I think the adverb "globally" is redundant and
misleading. That is to say, there is no such thing as a global variable.
All variables are scoped to some context and it is the extent of this
context that is a measure of detriment. This is why you hear people
talking about "keeping their side-effects local." This wishful-thinking
almost never eventuates because side-effects are pervasive. I am
side-tracking here, but going back to the original topic briefly.

In the absence of my awareness of your constraints, I can point out what
it is that most people want when they think they want DI, and in fact,
do not, ever (it is one of many forms of masochism in programming --
bare with me).

First, let us consider a general Scala program and generalise it. This
is just an arbitrary program -- I am trying to make it as convoluted as
possible so that you can go back to a real program and apply the same
reasoning. Importantly, this program is side-effect free at this point.

val a = e1
val b = e2(a)
val c = e3(a, b)
val d = e2(b)

OK, now I am going to generalise it by running the same program, but in
a for-comprehension. We do this by following these rules:
1) Remove the 'val' keyword
2) The = symbol becomes <-
3) We wrap the program in for and yield

I am going to create a data type that simply wraps a value and provides
flatMap and map methods so I can do this:

case class Id[A](i: A) {
def map[B](f: A => B) = Id(f(i))
def flatMap[B](f: A => Id[B]) = f(i)
}

...and since I don't want to explicitly wrap/unwrap my values with Id, I
am going to provide an implicit for in and out:

object Id {
implicit def IdIn[A](a: A) = Id(a)
implicit def IdOut[A](a: Id[A]) = a.i
}

OK, so now let's translate our program:

for {
a <- e1
b <- e2(a)
c <- e3(a, b)
d <- e2(b)
} yield d

Now that you accept that any program can be written this way, let us
step away for a moment and address the idea of DI. There are usually two
variations on DI:
1) The "configuration" (or context) is done and the application must
start by first initialising this context, then the application may run.
The application then reads from the configuration during run-time but
does not modify it. If this order is altered, you end up with a broken
program. A "DI" container attempts to promise you that no such thing
will occur -- this is essentially what the selling point is.

This dependency on explicit execution order is directly anti-thetical to
the functional programming thesis. This is a consequence of there being
a widely-scoped variable that kind-of pretends otherwise.

If you turn your head just a little, you can see this is a somewhat
degenerate notion of what is called "uniqueness typing." I digress.

2) Same as above, however, not only is the application permitted to read
the configuration, but it is also permitted to *write* to it. This means
that the application depends on *more* explicit execution order and the
possibility of bugs increases even more.

Imagine if I said, "you know what, turn all that DI stuff off, we are
going to initialise our values up front and pass them all the way
through the application." You would surely protest, "but that is so
clumsy!" and you'd be right, but only at first glance.

You see, there is a way to pass these values through quite neatly and
no, this is not using Scala's implicit keyword (which is insufficient),
this is something else. OK, so let's first start by thinking about case
1) above where the application only has read access to some context. I
will name this context, "Context", it is a data type that is
somewhere-or-other that we would like to pass through our application --
but no writes to it. I'm sure you can imagine what Context would really

Ross Rose
Joined: 2011-09-10,
User offline. Last seen 7 hours 26 min ago.
Re: Dependency Injection

On Sep 12, 2011, at 6:53, Tony Morris wrote:

> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> On 09/12/2011 09:38 PM, Billy wrote:
>> A debate started on the scala-language group that I am moving to this
>> group as it is more fitting here. The matter of DI was discussed, and
>> I am actively porting the DI portions of Spring to scala to answer a
>> question of practicality. In this effort, the resulting solution
>> (which I have dubbed "Recoil") may end up not looking anything like
>> the original framework. The requirements that spawned this are a
>> matter of practicality around updating large solutions that have
>> already been put into production use. My goal is to see if an object-
>> functional framework can be devised that allows for zero updates to
>> existing solutions so as to maintain a single codebase within a global
>> scale enterprise. It is hoped that such a solution will allow for
>> domain specific rules to be injected in order to meet the needs of
>> multiple different domains without apriori knowledge of said rules.
>>
>> Billy
>
> [moving to scala-debate per agreed request]
>
> I don't recall your constraints so cannot answer your question.
>
> Someone (I forget who) recently wrote (I forget where): "DI is just
> socially acceptable global variables." This is mostly true -- I say
> mostly because I think the adverb "globally" is redundant and
> misleading. That is to say, there is no such thing as a global variable.
> All variables are scoped to some context and it is the extent of this
> context that is a measure of detriment. This is why you hear people
> talking about "keeping their side-effects local." This wishful-thinking
> almost never eventuates because side-effects are pervasive. I am
> side-tracking here, but going back to the original topic briefly.
>
> In the absence of my awareness of your constraints, I can point out what
> it is that most people want when they think they want DI, and in fact,
> do not, ever (it is one of many forms of masochism in programming --
> bare with me).
>
> First, let us consider a general Scala program and generalise it. This
> is just an arbitrary program -- I am trying to make it as convoluted as
> possible so that you can go back to a real program and apply the same
> reasoning. Importantly, this program is side-effect free at this point.
>
> val a = e1
> val b = e2(a)
> val c = e3(a, b)
> val d = e2(b)
>
> OK, now I am going to generalise it by running the same program, but in
> a for-comprehension. We do this by following these rules:
> 1) Remove the 'val' keyword
> 2) The = symbol becomes <-
> 3) We wrap the program in for and yield
>
> I am going to create a data type that simply wraps a value and provides
> flatMap and map methods so I can do this:
>
> case class Id[A](i: A) {
> def map[B](f: A => B) = Id(f(i))
> def flatMap[B](f: A => Id[B]) = f(i)
> }
>
> ...and since I don't want to explicitly wrap/unwrap my values with Id, I
> am going to provide an implicit for in and out:
>
> object Id {
> implicit def IdIn[A](a: A) = Id(a)
> implicit def IdOut[A](a: Id[A]) = a.i
> }
>
> OK, so now let's translate our program:
>
> for {
> a <- e1
> b <- e2(a)
> c <- e3(a, b)
> d <- e2(b)
> } yield d
>
> Now that you accept that any program can be written this way, let us
> step away for a moment and address the idea of DI. There are usually two
> variations on DI:
> 1) The "configuration" (or context) is done and the application must
> start by first initialising this context, then the application may run.
> The application then reads from the configuration during run-time but
> does not modify it. If this order is altered, you end up with a broken
> program. A "DI" container attempts to promise you that no such thing
> will occur -- this is essentially what the selling point is.
>
> This dependency on explicit execution order is directly anti-thetical to
> the functional programming thesis. This is a consequence of there being
> a widely-scoped variable that kind-of pretends otherwise.
>
> If you turn your head just a little, you can see this is a somewhat
> degenerate notion of what is called "uniqueness typing." I digress.
>
> 2) Same as above, however, not only is the application permitted to read
> the configuration, but it is also permitted to *write* to it. This means
> that the application depends on *more* explicit execution order and the
> possibility of bugs increases even more.
>
> Imagine if I said, "you know what, turn all that DI stuff off, we are
> going to initialise our values up front and pass them all the way
> through the application." You would surely protest, "but that is so
> clumsy!" and you'd be right, but only at first glance.
>
> You see, there is a way to pass these values through quite neatly and
> no, this is not using Scala's implicit keyword (which is insufficient),
> this is something else. OK, so let's first start by thinking about case
> 1) above where the application only has read access to some context. I
> will name this context, "Context", it is a data type that is
> somewhere-or-other that we would like to pass through our application --
> but no writes to it. I'm sure you can imagine what Context would really
> be -- feel free to make it up for the use-case.
>
> So, our values that were once mere values, are now computed as if they
> have access to a Context. We can denote this with a data type:
>
> case class ComputedWithContext[A](cx: Context => A)
>
> So we now have "first-class" values computed with a Context, rather than
> being mere values. We can now create these by accessing a Context "as if
> it were passed" -- that is to say, although we don't yet have a Context,
> we may create values that access that Context (when it is eventually
> passed) by wrapping a function (or a trait if you prefer).
>
> This is simple and straight-forward enough. But watch this:
>
> case class ComputedWithContext[A](cx: Context => A) {
> def map[B](f: A => B): ComputedWithContext[B] = ComputedWithContext(f
> compose cx)
> def flatMap[B](f: A => ComputedWithContext[B]): ComputedWithContext[B]
> = ComputedWithContext(c => f(cx(c)) cx c)
> }
>
> We see here that ComputedWithContext happens to have pretty handy map
> and flatMap methods. What can we do with them?
>
> OK, so suppose our program above is a little different to the original
> in that actually, our expressions (e1, e2 and e3) require a Context, so
> each of them becomes become ComputedWithContext[T] where previously they
> were just the type T (they may be all different values for T or same --
> no matter).
>
> For example, e1 may have been an Int where now it is a
> ComputedWithContext[Int] and e2 may have been a String where now it is a
> ComputedWithContext[String]. You get the point.
>
> Here is how our program looks:
>
> for {
> a <- e1
> b <- e2(a)
> c <- e3(a, b)
> d <- e2(b)
> } yield d
>
> This is precisely the same program syntax. The type of this expression
> is ComputedWithContext[T] where the type T depends on the value d. In
> other words, we may pass a Context in to this value and it gets
> "threaded" through our program and our program *doesn't change* if we
> write it in this general form. We may "stack these layers" on top of
> what started as Id and our program remains unaltered. The "theory" of
> doing this is quite involved, mostly because it is kick-arse interesting
> and we could talk about it some time, but that's another story!
>
> Importantly, there are no variables here. Not one and not a pretend
> value that is actually a variable at application time (which I'm sure
> you've been reminded of more than once when using DI).
>
> So, this is how we deal with passing read-only context through our
> application:
> * without being clumsy by explicitly passing it
> * being quite efficient and readable in fact!
> * without using variables that leads to program bugs and difficulty
> reading and debugging code
>
> How do we deal with read and write values (case 2)? Well, we need a new
> different data type for that:
>
> case class WriteWithContext[A](cx: Context => (A, Context))
>
> Notice how this is the same data type as before except the function can
> now produce a *new* Context as well as the computed value (paired). This
> is to say, we may "modify" the Context as it is threaded through. But
> what about map and flatMap, can we write those? Of course:
>
> case class WriteWithContext[A](cx: Context => (A, Context)) {
> def map[B](f: A => B): WriteWithContext[B] = WriteWithContext(c => val
> (a, cc) = cx(c); (f(a), cc))
> def flatMap[B](f: A => WriteWithContext[B]): WriteWithContext[B] =
> WriteWithContext(c => { val (a, cc) = cx(c); f(a) cx cc })
> }
>
> Don't get too carried away with reading those methods, but just note
> that flatMap "threads the Context through whatever the function is,
> which may be modifying it."
>
> OK, so now if we suppose that our expressions (e1, e2, e3) actually had
> access to the Context, but were also able to "modify" it by returning a
> new Context (or just leaving it alone, for which there is library
> support of course), then our program would look like this:
>
> for {
> a <- e1
> b <- e2(a)
> c <- e3(a, b)
> d <- e2(b)
> } yield d
>
> Yep, exactly the same as before. So now we have a value that we can pass
> in a Context and it is threaded through the program, potentially
> "modifying" the Context as it is threaded through and we get a value and
> the resulting Context at the end. We may wish to drop either of these --
> in practice, the Context often gets dropped, since it was only need to
> compute the value -- and of course, there is library support for that.
>
> So hopefully now you see that DI can be replaced by a superior
> programming model, at least for this example, and I promise, for any
> example. We just have to come to terms with a few data types and
> abstractions and we can kick that baby to the gutter where it belongs.
>
> Hope that helps!
>
>
> - --
> Tony Morris
> http://tmorris.net/
>
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.11 (GNU/Linux)
> Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
>
> iQEcBAEBAgAGBQJObfLOAAoJEPxHMY3rBz0Ps/oH/iXV+fEk9hGdUN0TbdcqR3Fh
> QlwVc3VN9El4jLb2yQdopL+93Tov3EYn1beVcC1R7ptI75jtrmRcppPMaJdRUFnN
> jeDvqXghac0+evt8zoaK2GqGq1H3R8eG6kdx5pBjf+0PCiJS9RziRQpITb+5Kob2
> 0I6MSe+beNe7UcVX7HGp9oGVx56CTaieh2R+H6LtGlhwahh//6BBeEyLflIGdc4w
> 8O8oJwfxT5lnsn2aXsxveB+zkywNyl+dxPEk83o5E3AVIKCvaEnRTKmwd4LsHIQM
> D/KVVyaPSKyvziEaVNwAlsukC3LoYg+MBhq9jRAJ65wCEhB+M8oUHNjgnDDbloA=
> =J7D1
> -----END PGP SIGNATURE-----

This approach seems very nice. What if the ComputedWithContext type is externalized and the classes that implement it are dynamically loaded based upon the environment it runs in (i.e. the domain)?

Chris Marshall
Joined: 2009-06-17,
User offline. Last seen 44 weeks 3 days ago.
RE: Dependency Injection
I think this is a fantastic email; however there is one caveat - you imply that the "program" is unchanged, which is true for the semantic structure you have defined. 
It is not true for the program constructs themselves, though (i.e. e1, e2 and e3), where the ComputedWithContext permeates both their input and output. 
What is more, a program which uses many constructs e1, e2 ... eN, all of which are ComputedWithContext, for which, upon fulfilling some new requirement, *a single one* must be changed write to its context too, you must change the input and output arguments to all e1 ... eN.
Perhaps with Haskell this is trivial because of the level of inference going on, but it is a concern in scala, no, that a tiny change might result in hours of development overhead? Are these reasonable observations?
Chris

> Date: Mon, 12 Sep 2011 21:53:50 +1000
> From: tonymorris [at] gmail [dot] com
> To: scala-debate [at] googlegroups [dot] com
> Subject: Re: [scala-debate] Dependency Injection
>
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> On 09/12/2011 09:38 PM, Billy wrote:
> > A debate started on the scala-language group that I am moving to this
> > group as it is more fitting here. The matter of DI was discussed, and
> > I am actively porting the DI portions of Spring to scala to answer a
> > question of practicality. In this effort, the resulting solution
> > (which I have dubbed "Recoil") may end up not looking anything like
> > the original framework. The requirements that spawned this are a
> > matter of practicality around updating large solutions that have
> > already been put into production use. My goal is to see if an object-
> > functional framework can be devised that allows for zero updates to
> > existing solutions so as to maintain a single codebase within a global
> > scale enterprise. It is hoped that such a solution will allow for
> > domain specific rules to be injected in order to meet the needs of
> > multiple different domains without apriori knowledge of said rules.
> >
> > Billy
>
> [moving to scala-debate per agreed request]
>
> I don't recall your constraints so cannot answer your question.
>
> Someone (I forget who) recently wrote (I forget where): "DI is just
> socially acceptable global variables." This is mostly true -- I say
> mostly because I think the adverb "globally" is redundant and
> misleading. That is to say, there is no such thing as a global variable.
> All variables are scoped to some context and it is the extent of this
> context that is a measure of detriment. This is why you hear people
> talking about "keeping their side-effects local." This wishful-thinking
> almost never eventuates because side-effects are pervasive. I am
> side-tracking here, but going back to the original topic briefly.
>
> In the absence of my awareness of your constraints, I can point out what
> it is that most people want when they think they want DI, and in fact,
> do not, ever (it is one of many forms of masochism in programming --
> bare with me).
>
> First, let us consider a general Scala program and generalise it. This
> is just an arbitrary program -- I am trying to make it as convoluted as
> possible so that you can go back to a real program and apply the same
> reasoning. Importantly, this program is side-effect free at this point.
>
> val a = e1
> val b = e2(a)
> val c = e3(a, b)
> val d = e2(b)
>
> OK, now I am going to generalise it by running the same program, but in
> a for-comprehension. We do this by following these rules:
> 1) Remove the 'val' keyword
> 2) The = symbol becomes <-
> 3) We wrap the program in for and yield
>
> I am going to create a data type that simply wraps a value and provides
> flatMap and map methods so I can do this:
>
> case class Id[A](i: A) {
> def map[B](f: A => B) = Id(f(i))
> def flatMap[B](f: A => Id[B]) = f(i)
> }
>
> ...and since I don't want to explicitly wrap/unwrap my values with Id, I
> am going to provide an implicit for in and out:
>
> object Id {
> implicit def IdIn[A](a: A) = Id(a)
> implicit def IdOut[A](a: Id[A]) = a.i
> }
>
> OK, so now let's translate our program:
>
> for {
> a <- e1
> b <- e2(a)
> c <- e3(a, b)
> d <- e2(b)
> } yield d
>
> Now that you accept that any program can be written this way, let us
> step away for a moment and address the idea of DI. There are usually two
> variations on DI:
> 1) The "configuration" (or context) is done and the application must
> start by first initialising this context, then the application may run.
> The application then reads from the configuration during run-time but
> does not modify it. If this order is altered, you end up with a broken
> program. A "DI" container attempts to promise you that no such thing
> will occur -- this is essentially what the selling point is.
>
> This dependency on explicit execution order is directly anti-thetical to
> the functional programming thesis. This is a consequence of there being
> a widely-scoped variable that kind-of pretends otherwise.
>
> If you turn your head just a little, you can see this is a somewhat
> degenerate notion of what is called "uniqueness typing." I digress.
>
> 2) Same as above, however, not only is the application permitted to read
> the configuration, but it is also permitted to *write* to it. This means
> that the application depends on *more* explicit execution order and the
> possibility of bugs increases even more.
>
> Imagine if I said, "you know what, turn all that DI stuff off, we are
> going to initialise our values up front and pass them all the way
> through the application." You would surely protest, "but that is so
> clumsy!" and you'd be right, but only at first glance.
>
> You see, there is a way to pass these values through quite neatly and
> no, this is not using Scala's implicit keyword (which is insufficient),
> this is something else. OK, so let's first start by thinking about case
> 1) above where the application only has read access to some context. I
> will name this context, "Context", it is a data type that is
> somewhere-or-other that we would like to pass through our application --
> but no writes to it. I'm sure you can imagine what Context would really
> be -- feel free to make it up for the use-case.
>
> So, our values that were once mere values, are now computed as if they
> have access to a Context. We can denote this with a data type:
>
> case class ComputedWithContext[A](cx: Context => A)
>
> So we now have "first-class" values computed with a Context, rather than
> being mere values. We can now create these by accessing a Context "as if
> it were passed" -- that is to say, although we don't yet have a Context,
> we may create values that access that Context (when it is eventually
> passed) by wrapping a function (or a trait if you prefer).
>
> This is simple and straight-forward enough. But watch this:
>
> case class ComputedWithContext[A](cx: Context => A) {
> def map[B](f: A => B): ComputedWithContext[B] = ComputedWithContext(f
> compose cx)
> def flatMap[B](f: A => ComputedWithContext[B]): ComputedWithContext[B]
> = ComputedWithContext(c => f(cx(c)) cx c)
> }
>
> We see here that ComputedWithContext happens to have pretty handy map
> and flatMap methods. What can we do with them?
>
> OK, so suppose our program above is a little different to the original
> in that actually, our expressions (e1, e2 and e3) require a Context, so
> each of them becomes become ComputedWithContext[T] where previously they
> were just the type T (they may be all different values for T or same --
> no matter).
>
> For example, e1 may have been an Int where now it is a
> ComputedWithContext[Int] and e2 may have been a String where now it is a
> ComputedWithContext[String]. You get the point.
>
> Here is how our program looks:
>
> for {
> a <- e1
> b <- e2(a)
> c <- e3(a, b)
> d <- e2(b)
> } yield d
>
> This is precisely the same program syntax. The type of this expression
> is ComputedWithContext[T] where the type T depends on the value d. In
> other words, we may pass a Context in to this value and it gets
> "threaded" through our program and our program *doesn't change* if we
> write it in this general form. We may "stack these layers" on top of
> what started as Id and our program remains unaltered. The "theory" of
> doing this is quite involved, mostly because it is kick-arse interesting
> and we could talk about it some time, but that's another story!
>
> Importantly, there are no variables here. Not one and not a pretend
> value that is actually a variable at application time (which I'm sure
> you've been reminded of more than once when using DI).
>
> So, this is how we deal with passing read-only context through our
> application:
> * without being clumsy by explicitly passing it
> * being quite efficient and readable in fact!
> * without using variables that leads to program bugs and difficulty
> reading and debugging code
>
> How do we deal with read and write values (case 2)? Well, we need a new
> different data type for that:
>
> case class WriteWithContext[A](cx: Context => (A, Context))
>
> Notice how this is the same data type as before except the function can
> now produce a *new* Context as well as the computed value (paired). This
> is to say, we may "modify" the Context as it is threaded through. But
> what about map and flatMap, can we write those? Of course:
>
> case class WriteWithContext[A](cx: Context => (A, Context)) {
> def map[B](f: A => B): WriteWithContext[B] = WriteWithContext(c => val
> (a, cc) = cx(c); (f(a), cc))
> def flatMap[B](f: A => WriteWithContext[B]): WriteWithContext[B] =
> WriteWithContext(c => { val (a, cc) = cx(c); f(a) cx cc })
> }
>
> Don't get too carried away with reading those methods, but just note
> that flatMap "threads the Context through whatever the function is,
> which may be modifying it."
>
> OK, so now if we suppose that our expressions (e1, e2, e3) actually had
> access to the Context, but were also able to "modify" it by returning a
> new Context (or just leaving it alone, for which there is library
> support of course), then our program would look like this:
>
> for {
> a <- e1
> b <- e2(a)
> c <- e3(a, b)
> d <- e2(b)
> } yield d
>
> Yep, exactly the same as before. So now we have a value that we can pass
> in a Context and it is threaded through the program, potentially
> "modifying" the Context as it is threaded through and we get a value and
> the resulting Context at the end. We may wish to drop either of these --
> in practice, the Context often gets dropped, since it was only need to
> compute the value -- and of course, there is library support for that.
>
> So hopefully now you see that DI can be replaced by a superior
> programming model, at least for this example, and I promise, for any
> example. We just have to come to terms with a few data types and
> abstractions and we can kick that baby to the gutter where it belongs.
>
> Hope that helps!
>
>
> - --
> Tony Morris
> http://tmorris.net/
>
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.11 (GNU/Linux)
> Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
>
> iQEcBAEBAgAGBQJObfLOAAoJEPxHMY3rBz0Ps/oH/iXV+fEk9hGdUN0TbdcqR3Fh
> QlwVc3VN9El4jLb2yQdopL+93Tov3EYn1beVcC1R7ptI75jtrmRcppPMaJdRUFnN
> jeDvqXghac0+evt8zoaK2GqGq1H3R8eG6kdx5pBjf+0PCiJS9RziRQpITb+5Kob2
> 0I6MSe+beNe7UcVX7HGp9oGVx56CTaieh2R+H6LtGlhwahh//6BBeEyLflIGdc4w
> 8O8oJwfxT5lnsn2aXsxveB+zkywNyl+dxPEk83o5E3AVIKCvaEnRTKmwd4LsHIQM
> D/KVVyaPSKyvziEaVNwAlsukC3LoYg+MBhq9jRAJ65wCEhB+M8oUHNjgnDDbloA=
> =J7D1
> -----END PGP SIGNATURE-----
Razvan Cojocaru 3
Joined: 2010-07-28,
User offline. Last seen 42 years 45 weeks ago.
RE: Dependency Injection

Nice!! - you should turn this email into a blog (or a series of). Seriously,
I find it very clear and a nice progression... although now, unlike when I
was staring at elephants, I kind of know what you're talking about ;)

If this is how the book is going to be, I can't wait!

Cheers,
Razie

-----Original Message-----
From: scala-debate [at] googlegroups [dot] com [mailto:scala-debate [at] googlegroups [dot] com]
On Behalf Of Tony Morris
Sent: September-12-11 7:54 AM
To: scala-debate [at] googlegroups [dot] com
Subject: Re: [scala-debate] Dependency Injection

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 09/12/2011 09:38 PM, Billy wrote:
> A debate started on the scala-language group that I am moving to this
> group as it is more fitting here. The matter of DI was discussed, and
> I am actively porting the DI portions of Spring to scala to answer a
> question of practicality. In this effort, the resulting solution
> (which I have dubbed "Recoil") may end up not looking anything like
> the original framework. The requirements that spawned this are a
> matter of practicality around updating large solutions that have
> already been put into production use. My goal is to see if an object-
> functional framework can be devised that allows for zero updates to
> existing solutions so as to maintain a single codebase within a global
> scale enterprise. It is hoped that such a solution will allow for
> domain specific rules to be injected in order to meet the needs of
> multiple different domains without apriori knowledge of said rules.
>
> Billy

[moving to scala-debate per agreed request]

I don't recall your constraints so cannot answer your question.

Someone (I forget who) recently wrote (I forget where): "DI is just socially
acceptable global variables." This is mostly true -- I say mostly because I
think the adverb "globally" is redundant and misleading. That is to say,
there is no such thing as a global variable.
All variables are scoped to some context and it is the extent of this
context that is a measure of detriment. This is why you hear people talking
about "keeping their side-effects local." This wishful-thinking almost never
eventuates because side-effects are pervasive. I am side-tracking here, but
going back to the original topic briefly.

In the absence of my awareness of your constraints, I can point out what it
is that most people want when they think they want DI, and in fact, do not,
ever (it is one of many forms of masochism in programming -- bare with me).

First, let us consider a general Scala program and generalise it. This is
just an arbitrary program -- I am trying to make it as convoluted as
possible so that you can go back to a real program and apply the same
reasoning. Importantly, this program is side-effect free at this point.

val a = e1
val b = e2(a)
val c = e3(a, b)
val d = e2(b)

OK, now I am going to generalise it by running the same program, but in a
for-comprehension. We do this by following these rules:
1) Remove the 'val' keyword
2) The = symbol becomes <-
3) We wrap the program in for and yield

I am going to create a data type that simply wraps a value and provides
flatMap and map methods so I can do this:

case class Id[A](i: A) {
def map[B](f: A => B) = Id(f(i))
def flatMap[B](f: A => Id[B]) = f(i)
}

...and since I don't want to explicitly wrap/unwrap my values with Id, I am
going to provide an implicit for in and out:

object Id {
implicit def IdIn[A](a: A) = Id(a)
implicit def IdOut[A](a: Id[A]) = a.i
}

OK, so now let's translate our program:

for {
a <- e1
b <- e2(a)
c <- e3(a, b)
d <- e2(b)
} yield d

Now that you accept that any program can be written this way, let us step
away for a moment and address the idea of DI. There are usually two
variations on DI:
1) The "configuration" (or context) is done and the application must start
by first initialising this context, then the application may run.
The application then reads from the configuration during run-time but does
not modify it. If this order is altered, you end up with a broken program. A
"DI" container attempts to promise you that no such thing will occur -- this
is essentially what the selling point is.

This dependency on explicit execution order is directly anti-thetical to the
functional programming thesis. This is a consequence of there being a
widely-scoped variable that kind-of pretends otherwise.

If you turn your head just a little, you can see this is a somewhat
degenerate notion of what is called "uniqueness typing." I digress.

2) Same as above, however, not only is the application permitted to read the
configuration, but it is also permitted to *write* to it. This means that
the application depends on *more* explicit execution order and the
possibility of bugs increases even more.

Imagine if I said, "you know what, turn all that DI stuff off, we are going
to initialise our values up front and pass them all the way through the
application." You would surely protest, "but that is so clumsy!" and you'd
be right, but only at first glance.

You see, there is a way to pass these values through quite neatly and no,
this is not using Scala's implicit keyword (which is insufficient), this is
something else. OK, so let's first start by thinking about case
1) above where the application only has read access to some context. I will
name this context, "Context", it is a data type that is somewhere-or-other
that we would like to pass through our application -- but no writes to it.
I'm sure you can imagine what Context would really be -- feel free to make
it up for the use-case.

So, our values that were once mere values, are now computed as if they have
access to a Context. We can denote this with a data type:

case class ComputedWithContext[A](cx: Context => A)

So we now have "first-class" values computed with a Context, rather than
being mere values. We can now create these by accessing a Context "as if it
were passed" -- that is to say, although we don't yet have a Context, we may
create values that access that Context (when it is eventually
passed) by wrapping a function (or a trait if you prefer).

This is simple and straight-forward enough. But watch this:

case class ComputedWithContext[A](cx: Context => A) {
def map[B](f: A => B): ComputedWithContext[B] = ComputedWithContext(f
compose cx)
def flatMap[B](f: A => ComputedWithContext[B]): ComputedWithContext[B] =
ComputedWithContext(c => f(cx(c)) cx c) }

We see here that ComputedWithContext happens to have pretty handy map and
flatMap methods. What can we do with them?

OK, so suppose our program above is a little different to the original in
that actually, our expressions (e1, e2 and e3) require a Context, so each of
them becomes become ComputedWithContext[T] where previously they were just
the type T (they may be all different values for T or same -- no matter).

For example, e1 may have been an Int where now it is a
ComputedWithContext[Int] and e2 may have been a String where now it is a
ComputedWithContext[String]. You get the point.

Here is how our program looks:

for {
a <- e1
b <- e2(a)
c <- e3(a, b)
d <- e2(b)
} yield d

This is precisely the same program syntax. The type of this expression is
ComputedWithContext[T] where the type T depends on the value d. In other
words, we may pass a Context in to this value and it gets "threaded" through
our program and our program *doesn't change* if we write it in this general
form. We may "stack these layers" on top of what started as Id and our
program remains unaltered. The "theory" of doing this is quite involved,
mostly because it is kick-arse interesting and we could talk about it some
time, but that's another story!

Importantly, there are no variables here. Not one and not a pretend value
that is actually a variable at application time (which I'm sure you've been
reminded of more than once when using DI).

So, this is how we deal with passing read-only context through our
application:
* without being clumsy by explicitly passing it
* being quite efficient and readable in fact!
* without using variables that leads to program bugs and difficulty reading
and debugging code

How do we deal with read and write values (case 2)? Well, we need a new
different data type for that:

case class WriteWithContext[A](cx: Context => (A, Context))

Notice how this is the same data type as before except the function can now
produce a *new* Context as well as the computed value (paired). This is to
say, we may "modify" the Context as it is threaded through. But what about
map and flatMap, can we write those? Of course:

case class WriteWithContext[A](cx: Context => (A, Context)) {
def map[B](f: A => B): WriteWithContext[B] = WriteWithContext(c => val (a,
cc) = cx(c); (f(a), cc))
def flatMap[B](f: A => WriteWithContext[B]): WriteWithContext[B] =
WriteWithContext(c => { val (a, cc) = cx(c); f(a) cx cc }) }

Don't get too carried away with reading those methods, but just note that
flatMap "threads the Context through whatever the function is, which may be
modifying it."

OK, so now if we suppose that our expressions (e1, e2, e3) actually had
access to the Context, but were also able to "modify" it by returning a new
Context (or just leaving it alone, for which there is library support of
course), then our program would look like this:

for {
a <- e1
b <- e2(a)
c <- e3(a, b)
d <- e2(b)
} yield d

Yep, exactly the same as before. So now we have a value that we can pass in
a Context and it is threaded through the program, potentially "modifying"
the Context as it is threaded through and we get a value and the resulting
Context at the end. We may wish to drop either of these -- in practice, the
Context often gets dropped, since it was only need to compute the value --
and of course, there is library support for that.

So hopefully now you see that DI can be replaced by a superior programming
model, at least for this example, and I promise, for any example. We just
have to come to terms with a few data types and abstractions and we can kick
that baby to the gutter where it belongs.

Hope that helps!

- --
Tony Morris
http://tmorris.net/

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iQEcBAEBAgAGBQJObfLOAAoJEPxHMY3rBz0Ps/oH/iXV+fEk9hGdUN0TbdcqR3Fh
QlwVc3VN9El4jLb2yQdopL+93Tov3EYn1beVcC1R7ptI75jtrmRcppPMaJdRUFnN
jeDvqXghac0+evt8zoaK2GqGq1H3R8eG6kdx5pBjf+0PCiJS9RziRQpITb+5Kob2
0I6MSe+beNe7UcVX7HGp9oGVx56CTaieh2R+H6LtGlhwahh//6BBeEyLflIGdc4w
8O8oJwfxT5lnsn2aXsxveB+zkywNyl+dxPEk83o5E3AVIKCvaEnRTKmwd4LsHIQM
D/KVVyaPSKyvziEaVNwAlsukC3LoYg+MBhq9jRAJ65wCEhB+M8oUHNjgnDDbloA=
=J7D1
-----END PGP SIGNATURE-----

Nikolay Artamonov
Joined: 2010-09-08,
User offline. Last seen 42 years 45 weeks ago.
Re: Dependency Injection

Thanks, Tony! Was it Reader and Writer monads explained? Can we say
that these monads are FP alternative to DI? By the way, are there
equivalents in Scalaz to haskell's type classes MonadReader/
MonadWriter?

On Sep 12, 3:53 pm, Tony Morris wrote:
> Someone (I forget who) recently wrote (I forget where): "DI is just
> socially acceptable global variables." This is mostly true -- I say
> mostly because I think the adverb "globally" is redundant and
> misleading. That is to say, there is no such thing as a global variable.
> All variables are scoped to some context and it is the extent of this
> context that is a measure of detriment. This is why you hear people
> talking about "keeping their side-effects local." This wishful-thinking
> almost never eventuates because side-effects are pervasive. I am
> side-tracking here, but going back to the original topic briefly.
>
> In the absence of my awareness of your constraints, I can point out what
> it is that most people want when they think they want DI, and in fact,
> do not, ever (it is one of many forms of masochism in programming --
> bare with me).
>
> First, let us consider a general Scala program and generalise it. This
> is just an arbitrary program -- I am trying to make it as convoluted as
> possible so that you can go back to a real program and apply the same
> reasoning. Importantly, this program is side-effect free at this point.
>
> val a = e1
> val b = e2(a)
> val c = e3(a, b)
> val d = e2(b)
>
> OK, now I am going to generalise it by running the same program, but in
> a for-comprehension. We do this by following these rules:
> 1) Remove the 'val' keyword
> 2) The = symbol becomes <-
> 3) We wrap the program in for and yield
>
> I am going to create a data type that simply wraps a value and provides
> flatMap and map methods so I can do this:
>
> case class Id[A](i: A) {
>   def map[B](f: A => B) = Id(f(i))
>   def flatMap[B](f: A => Id[B]) = f(i)
>
> }
>
> ...and since I don't want to explicitly wrap/unwrap my values with Id, I
> am going to provide an implicit for in and out:
>
> object Id {
>   implicit def IdIn[A](a: A) = Id(a)
>   implicit def IdOut[A](a: Id[A]) = a.i
>
> }
>
> OK, so now let's translate our program:
>
> for {
>   a <- e1
>   b <- e2(a)
>   c <- e3(a, b)
>   d <- e2(b)
>
> } yield d
>
> Now that you accept that any program can be written this way, let us
> step away for a moment and address the idea of DI. There are usually two
> variations on DI:
> 1) The "configuration" (or context) is done and the application must
> start by first initialising this context, then the application may run.
> The application then reads from the configuration during run-time but
> does not modify it. If this order is altered, you end up with a broken
> program. A "DI" container attempts to promise you that no such thing
> will occur -- this is essentially what the selling point is.
>
> This dependency on explicit execution order is directly anti-thetical to
> the functional programming thesis. This is a consequence of there being
> a widely-scoped variable that kind-of pretends otherwise.
>
> If you turn your head just a little, you can see this is a somewhat
> degenerate notion of what is called "uniqueness typing." I digress.
>
> 2) Same as above, however, not only is the application permitted to read
> the configuration, but it is also permitted to *write* to it. This means
> that the application depends on *more* explicit execution order and the
> possibility of bugs increases even more.
>
> Imagine if I said, "you know what, turn all that DI stuff off, we are
> going to initialise our values up front and pass them all the way
> through the application." You would surely protest, "but that is so
> clumsy!" and you'd be right, but only at first glance.
>
> You see, there is a way to pass these values through quite neatly and
> no, this is not using Scala's implicit keyword (which is insufficient),
> this is something else. OK, so let's first start by thinking about case
> 1) above where the application only has read access to some context. I
> will name this context, "Context", it is a data type that is
> somewhere-or-other that we would like to pass through our application --
> but no writes to it. I'm sure you can imagine what Context would really
> be -- feel free to make it up for the use-case.
>
> So, our values that were once mere values, are now computed as if they
> have access to a Context. We can denote this with a data type:
>
> case class ComputedWithContext[A](cx: Context => A)
>
> So we now have "first-class" values computed with a Context, rather than
> being mere values. We can now create these by accessing a Context "as if
> it were passed" -- that is to say, although we don't yet have a Context,
> we may create values that access that Context (when it is eventually
> passed) by wrapping a function (or a trait if you prefer).
>
> This is simple and straight-forward enough. But watch this:
>
> case class ComputedWithContext[A](cx: Context => A) {
>   def map[B](f: A => B): ComputedWithContext[B] = ComputedWithContext(f
> compose cx)
>   def flatMap[B](f: A => ComputedWithContext[B]): ComputedWithContext[B]
> = ComputedWithContext(c => f(cx(c)) cx c)
>
> }
>
> We see here that ComputedWithContext happens to have pretty handy map
> and flatMap methods. What can we do with them?
>
> OK, so suppose our program above is a little different to the original
> in that actually, our expressions (e1, e2 and e3) require a Context, so
> each of them becomes become ComputedWithContext[T] where previously they
> were just the type T (they may be all different values for T or same --
> no matter).
>
> For example, e1 may have been an Int where now it is a
> ComputedWithContext[Int] and e2 may have been a String where now it is a
> ComputedWithContext[String]. You get the point.
>
> Here is how our program looks:
>
> for {
>   a <- e1
>   b <- e2(a)
>   c <- e3(a, b)
>   d <- e2(b)
>
> } yield d
>
> This is precisely the same program syntax. The type of this expression
> is ComputedWithContext[T] where the type T depends on the value d. In
> other words, we may pass a Context in to this value and it gets
> "threaded" through our program and our program *doesn't change* if we
> write it in this general form. We may "stack these layers" on top of
> what started as Id and our program remains unaltered. The "theory" of
> doing this is quite involved, mostly because it is kick-arse interesting
> and we could talk about it some time, but that's another story!
>
> Importantly, there are no variables here. Not one and not a pretend
> value that is actually a variable at application time (which I'm sure
> you've been reminded of more than once when using DI).
>
> So, this is how we deal with passing read-only context through our
> application:
> * without being clumsy by explicitly passing it
> * being quite efficient and readable in fact!
> * without using variables that leads to program bugs and difficulty
> reading and debugging code
>
> How do we deal with read and write values (case 2)? Well, we need a new
> different data type for that:
>
> case class WriteWithContext[A](cx: Context => (A, Context))
>
> Notice how this is the same data type as before except the function can
> now produce a *new* Context as well as the computed value (paired). This
> is to say, we may "modify" the Context as it is threaded through. But
> what about map and flatMap, can we write those? Of course:
>
> case class WriteWithContext[A](cx: Context => (A, Context)) {
>   def map[B](f: A => B): WriteWithContext[B] = WriteWithContext(c => val
> (a, cc) = cx(c); (f(a), cc))
>   def flatMap[B](f: A => WriteWithContext[B]): WriteWithContext[B] =
> WriteWithContext(c => { val (a, cc) = cx(c); f(a) cx cc })
>
> }
>
> Don't get too carried away with reading those methods, but just note
> that flatMap "threads the Context through whatever the function is,
> which may be modifying it."
>
> OK, so now if we suppose that our expressions (e1, e2, e3) actually had
> access to the Context, but were also able to "modify" it by returning a
> new Context (or just leaving it alone, for which there is library
> support of course), then our program would look like this:
>
> for {
>   a <- e1
>   b <- e2(a)
>   c <- e3(a, b)
>   d <- e2(b)
>
> } yield d
>
> Yep, exactly the same as before. So now we have a value that we can pass
> in a Context and it is threaded through the program, potentially
> "modifying" the Context as it is threaded through and we get a value and
> the resulting Context at the end. We may wish to drop either of these --
> in practice, the Context often gets dropped, since it was only need to
> compute the value -- and of course, there is library support for that.
>
> So hopefully now you see that DI can be replaced by a superior
> programming model, at least for this example, and I promise, for any
> example. We just have to come to terms with a few data types and
> abstractions and we can kick that baby to the gutter where it belongs.
>
> Hope that helps!

Andreas Scheinert
Joined: 2011-02-11,
User offline. Last seen 42 years 45 weeks ago.
Re: Dependency Injection

Hi Nikolay!
That would be the ReaderMonad see here :
http://permalink.gmane.org/gmane.comp.lang.scala.user/36914
And see scalaz examples for more :)

On 12 Sep., 20:06, Nikolay Artamonov wrote:
> Thanks, Tony! Was it Reader and Writer monads explained? Can we say
> that these monads are FP alternative to DI? By the way, are there
> equivalents in Scalaz to haskell's type classes MonadReader/
> MonadWriter?
>
> On Sep 12, 3:53 pm, Tony Morris wrote:
>
>
>
>
>
>
>
> > Someone (I forget who) recently wrote (I forget where): "DI is just
> > socially acceptable global variables." This is mostly true -- I say
> > mostly because I think the adverb "globally" is redundant and
> > misleading. That is to say, there is no such thing as a global variable.
> > All variables are scoped to some context and it is the extent of this
> > context that is a measure of detriment. This is why you hear people
> > talking about "keeping their side-effects local." This wishful-thinking
> > almost never eventuates because side-effects are pervasive. I am
> > side-tracking here, but going back to the original topic briefly.
>
> > In the absence of my awareness of your constraints, I can point out what
> > it is that most people want when they think they want DI, and in fact,
> > do not, ever (it is one of many forms of masochism in programming --
> > bare with me).
>
> > First, let us consider a general Scala program and generalise it. This
> > is just an arbitrary program -- I am trying to make it as convoluted as
> > possible so that you can go back to a real program and apply the same
> > reasoning. Importantly, this program is side-effect free at this point.
>
> > val a = e1
> > val b = e2(a)
> > val c = e3(a, b)
> > val d = e2(b)
>
> > OK, now I am going to generalise it by running the same program, but in
> > a for-comprehension. We do this by following these rules:
> > 1) Remove the 'val' keyword
> > 2) The = symbol becomes <-
> > 3) We wrap the program in for and yield
>
> > I am going to create a data type that simply wraps a value and provides
> > flatMap and map methods so I can do this:
>
> > case class Id[A](i: A) {
> >   def map[B](f: A => B) = Id(f(i))
> >   def flatMap[B](f: A => Id[B]) = f(i)
>
> > }
>
> > ...and since I don't want to explicitly wrap/unwrap my values with Id, I
> > am going to provide an implicit for in and out:
>
> > object Id {
> >   implicit def IdIn[A](a: A) = Id(a)
> >   implicit def IdOut[A](a: Id[A]) = a.i
>
> > }
>
> > OK, so now let's translate our program:
>
> > for {
> >   a <- e1
> >   b <- e2(a)
> >   c <- e3(a, b)
> >   d <- e2(b)
>
> > } yield d
>
> > Now that you accept that any program can be written this way, let us
> > step away for a moment and address the idea of DI. There are usually two
> > variations on DI:
> > 1) The "configuration" (or context) is done and the application must
> > start by first initialising this context, then the application may run.
> > The application then reads from the configuration during run-time but
> > does not modify it. If this order is altered, you end up with a broken
> > program. A "DI" container attempts to promise you that no such thing
> > will occur -- this is essentially what the selling point is.
>
> > This dependency on explicit execution order is directly anti-thetical to
> > the functional programming thesis. This is a consequence of there being
> > a widely-scoped variable that kind-of pretends otherwise.
>
> > If you turn your head just a little, you can see this is a somewhat
> > degenerate notion of what is called "uniqueness typing." I digress.
>
> > 2) Same as above, however, not only is the application permitted to read
> > the configuration, but it is also permitted to *write* to it. This means
> > that the application depends on *more* explicit execution order and the
> > possibility of bugs increases even more.
>
> > Imagine if I said, "you know what, turn all that DI stuff off, we are
> > going to initialise our values up front and pass them all the way
> > through the application." You would surely protest, "but that is so
> > clumsy!" and you'd be right, but only at first glance.
>
> > You see, there is a way to pass these values through quite neatly and
> > no, this is not using Scala's implicit keyword (which is insufficient),
> > this is something else. OK, so let's first start by thinking about case
> > 1) above where the application only has read access to some context. I
> > will name this context, "Context", it is a data type that is
> > somewhere-or-other that we would like to pass through our application --
> > but no writes to it. I'm sure you can imagine what Context would really
> > be -- feel free to make it up for the use-case.
>
> > So, our values that were once mere values, are now computed as if they
> > have access to a Context. We can denote this with a data type:
>
> > case class ComputedWithContext[A](cx: Context => A)
>
> > So we now have "first-class" values computed with a Context, rather than
> > being mere values. We can now create these by accessing a Context "as if
> > it were passed" -- that is to say, although we don't yet have a Context,
> > we may create values that access that Context (when it is eventually
> > passed) by wrapping a function (or a trait if you prefer).
>
> > This is simple and straight-forward enough. But watch this:
>
> > case class ComputedWithContext[A](cx: Context => A) {
> >   def map[B](f: A => B): ComputedWithContext[B] = ComputedWithContext(f
> > compose cx)
> >   def flatMap[B](f: A => ComputedWithContext[B]): ComputedWithContext[B]
> > = ComputedWithContext(c => f(cx(c)) cx c)
>
> > }
>
> > We see here that ComputedWithContext happens to have pretty handy map
> > and flatMap methods. What can we do with them?
>
> > OK, so suppose our program above is a little different to the original
> > in that actually, our expressions (e1, e2 and e3) require a Context, so
> > each of them becomes become ComputedWithContext[T] where previously they
> > were just the type T (they may be all different values for T or same --
> > no matter).
>
> > For example, e1 may have been an Int where now it is a
> > ComputedWithContext[Int] and e2 may have been a String where now it is a
> > ComputedWithContext[String]. You get the point.
>
> > Here is how our program looks:
>
> > for {
> >   a <- e1
> >   b <- e2(a)
> >   c <- e3(a, b)
> >   d <- e2(b)
>
> > } yield d
>
> > This is precisely the same program syntax. The type of this expression
> > is ComputedWithContext[T] where the type T depends on the value d. In
> > other words, we may pass a Context in to this value and it gets
> > "threaded" through our program and our program *doesn't change* if we
> > write it in this general form. We may "stack these layers" on top of
> > what started as Id and our program remains unaltered. The "theory" of
> > doing this is quite involved, mostly because it is kick-arse interesting
> > and we could talk about it some time, but that's another story!
>
> > Importantly, there are no variables here. Not one and not a pretend
> > value that is actually a variable at application time (which I'm sure
> > you've been reminded of more than once when using DI).
>
> > So, this is how we deal with passing read-only context through our
> > application:
> > * without being clumsy by explicitly passing it
> > * being quite efficient and readable in fact!
> > * without using variables that leads to program bugs and difficulty
> > reading and debugging code
>
> > How do we deal with read and write values (case 2)? Well, we need a new
> > different data type for that:
>
> > case class WriteWithContext[A](cx: Context => (A, Context))
>
> > Notice how this is the same data type as before except the function can
> > now produce a *new* Context as well as the computed value (paired). This
> > is to say, we may "modify" the Context as it is threaded through. But
> > what about map and flatMap, can we write those? Of course:
>
> > case class WriteWithContext[A](cx: Context => (A, Context)) {
> >   def map[B](f: A => B): WriteWithContext[B] = WriteWithContext(c => val
> > (a, cc) = cx(c); (f(a), cc))
> >   def flatMap[B](f: A => WriteWithContext[B]): WriteWithContext[B] =
> > WriteWithContext(c => { val (a, cc) = cx(c); f(a) cx cc })
>
> > }
>
> > Don't get too carried away with reading those methods, but just note
> > that flatMap "threads the Context through whatever the function is,
> > which may be modifying it."
>
> > OK, so now if we suppose that our expressions (e1, e2, e3) actually had
> > access to the Context, but were also able to "modify" it by returning a
> > new Context (or just leaving it alone, for which there is library
> > support of course), then our program would look like this:
>
> > for {
> >   a <- e1
> >   b <- e2(a)
> >   c <- e3(a, b)
> >   d <- e2(b)
>
> > } yield d
>
> > Yep, exactly the same as before. So now we have a value that we can pass
> > in a Context and it is threaded through the program, potentially
> > "modifying" the Context as it is threaded through and we get a value and
> > the resulting Context at the end. We may wish to drop either of these --
> > in practice, the Context often gets dropped, since it was only need to
> > compute the value -- and of course, there is library support for that.
>
> > So hopefully now you see that DI can be replaced by a superior
> > programming model, at least for this example, and I promise, for any
> > example. We just have to come to terms with a few data types and
> > abstractions and we can kick that baby to the gutter where it belongs.
>
> > Hope that helps!

Russ P.
Joined: 2009-01-31,
User offline. Last seen 1 year 26 weeks ago.
Re: Dependency Injection
Tony,

That looks interesting. I'm wondering how I can apply it to my problem and what exactly it can do for me. I'm developing an R&D prototype for tactical conflict detection and resolution for air traffic (can be used as an alerting aid for air traffic controllers or can automatically compute resolution maneuvers). It has a couple of dozen switches and parameters.

My first approach was to just create a top-level object called "Config" that contains all the switches and parameters, and access it essentially as a global. That works, but it has a couple of limitations. First, several of my classes depend on one or more values in it, so those classes are not as conveniently reusable as they would otherwise be. That is not necessarily a major issue for me. The more important issue for me is that, to change any switch or parameter, the user needs to recompile. That is not always convenient for someone else running my code (in a simulation, for example).

My current approach eliminates the need to recompile. Instead of using an object, I use a class called Config that has defaults for all switches and parameters and a constructor that accepts command-line arguments to override those defaults if desired. My main program passes the command-line arguments, if any are provided,  to an instance of the Config class, and I use my class called "CommandLine" to parse those arguments. This allows the user to set a few important switches and parameters without recompiling (most of the parameters are rarely varied, so they don't need to be configurable from the command line).

As far as I can tell, the only problem with this approach is that several of my other classes still depend on the resulting Config object. Again, that is not a major issue for me, but it will probably be inconvenient if someone else tries to reuse those classes for another application. Can your approach solve that problem? Or can it perhaps solve another problem that I have overlooked entirely?

In case it helps to clarify the issue, my configuration file follows. Thanks.

--Russ P.

import types._
import Scalar._
import ATMunits._

class Config(com: CommandLine) {

    def this(args: Array[Text]=Array()) = this(new CommandLine(args))

    val trimFlightIDs = com.Bool("trimFlightIDs", false)
        // remove appended Host ID from each flight ID
    val ignoreHostIDs = com.Bool("ignoreHostIDs", true)
        // ignore appended Host IDs when checking for conflicts

    //------------------------------------------------------------------
    // Velocity Filtering
    //------------------------------------------------------------------

    val useTrackFilter = com.Bool("useTrackFilter", false)
        // use first-order TSAFE TrackFilter (otherwise use input velocity)
    val useAltFilter = com.Bool("useAltFilter", true)
        // use first-order TSAFE AltFilter (otherwise use input alt rate)
 
    val alphaTrackFilter = 0.75 // first-order filter parameter (1=no filtering)
    val alphaAltFilter = 1.0 // first-order filter parameter (1=no filtering)

    //------------------------------------------------------------------
    // Altitude Parameters
    //------------------------------------------------------------------

    val cruiseAltTol = 200.1 * ft // cruise altitude tolerance
    val levelRateLimit = 800 * fpm // altitude rate limit for leveloff detection

    val minAltDelay = 6 * sec // min delay to start a climb or descent
    val maxAltDelay = 36 * sec // max delay to start a climb or descent

    val vertAccelNom = 0.1 * gacc // max vertical acceleration or deceleration
    val vertAccelMax = 0.15 * gacc // max vertical acceleration or deceleration

    //------------------------------------------------------------------
    // Flightplan Parameters
    //------------------------------------------------------------------

    val bankAngle = 20 * deg // nominal bank angle for flightplan turn arcs

    val crossTrackTol = 6 * nmi // cross-track tolerance
    val courseDevTol = 30 * deg // course deviation tolerance

    val convergeAngle = 10 * deg // angle of convergence to flightplan route
    val minTurnRadFactor = 0.5 // min turn-radius reduction factor to make next waypoint

    //------------------------------------------------------------------
    // Trajectory Prediction
    //------------------------------------------------------------------

    val predictionPeriod = com.Real("predictionPeriod", 5) * sec
        // limit prediction updates (for ADS-B)
    val predictionTimeStep = com.Real("predictionTimeStep", 6) * sec
        // trajectory prediction time step

    val useWindData = com.Bool("useWindData", true) // use wind data?

    val maxPredTime = 3 * Min // maximum prediction time horizon
    val maxNonCruisePredTime = 2.5 * Min // max prediction time when not in cruise

    val pathPredTimeDR = 2 * Min // dead-reckoning path prediction time limit
    val pathPredTimeShortDR = 1.5 * Min // "short" dead-reckoning path prediction time limit

    val pathPredTimeFP = 3 * Min // flightplan-based path prediction time limit

    val altPredTimeDR = 3 * Min // dead-reckoning altitude prediction time limit
    val altPredTimeFP = 1.5 * Min // flightplan-based alt prediction time limit

    val altPredTimeTransDR = 1.5 * Min // max DR alt pred time in alt transient
    val altPredTimeTransFP = 2.0 * Min // max FP alt pred time in alt transient

    val useTurnDetection = false // 1 for unplanned turn detection
    val pathPredTimeTD = 1.5 * Min // prediction time limit for turn detection

    //------------------------------------------------------------------
    // Conflict Detection
    //------------------------------------------------------------------

    val conflictCheckPeriod = com.Real("ConflictCheckPeriod", 10) * sec
        // limit conflict checks (for ADS-B)

    val HSM = com.Real("HSM", 5) * nmi // horizontal separation minimum (legal)
    val SRT = com.Real("SRT", 1.1) // separation-ratio threshold for alerting

    val conflictHoldTime = 30 * sec // time after last alert at which conflict is dropped

    val useFAR = com.Bool("useFAR", true) // if true, use false alert reduction methods
    val FARbyTime = (1.5 * Min, 0.8) // false-alert reduction by time
    val FARbyAngle = (20 * deg, 1.0) // false-alert reduction by angle
    val FARmaxTime = 1 * Min // time window for second alert of "2 of 3" rule
    val FARminTime = 1 * Min // min time to predicted LoS for "2 of 3" rule

    val checkSafeTurnRange = false // preemptive check of unplanned turns

    val skipTRACONpairs = com.Bool("skipTRACONpairs", false)
        // if true, don't check for conflicts if both flights owned by TRACON

    //------------------------------------------------------------------
    // Critical Leveloff Detection
    //------------------------------------------------------------------

    val useCLdetection = com.Bool("CLdetection", true) // critical leveloff detection
    val CL_SRT = 0.8 // separation-ratio threshold for critical leveloff detection
    val CLtimeLimit = 1.5 * Min // critical leveloff prediction time limit
    val CLaltLimit = 1.5 * kft // overshoot altitude limit

    //------------------------------------------------------------------
    // Aircraft models
    //------------------------------------------------------------------

    val slowAltRateMode = AltRateMode.slow
    val fastAltRateMode = AltRateMode.nom // choose slow, nom, or fast
    val nomAltRateMode = AltRateMode.nom

    val defaultClimbRates = Map[AltRateMode, Scalar](
        AltRateMode.slow -> 1500 * fpm,
        AltRateMode.fast -> 3500 * fpm,
        AltRateMode.nom -> 2000 * fpm)

    val defaultDescentRates = Map[AltRateMode, Scalar](
        AltRateMode.slow -> -1500 * fpm,
        AltRateMode.fast -> -2500 * fpm,
        AltRateMode.nom -> -2000 * fpm)

    val descentRateFactors = (0.8, 1.2)
    }


Jim Powers
Joined: 2011-01-24,
User offline. Last seen 36 weeks 2 days ago.
Re: Dependency Injection
On Mon, Sep 12, 2011 at 11:32 AM, Chris Marshall <oxbow_lakes [at] hotmail [dot] com> wrote:
I think this is a fantastic email; however there is one caveat - you imply that the "program" is unchanged, which is true for the semantic structure you have defined. 
It is not true for the program constructs themselves, though (i.e. e1, e2 and e3), where the ComputedWithContext permeates both their input and output. 
What is more, a program which uses many constructs e1, e2 ... eN, all of which are ComputedWithContext, for which, upon fulfilling some new requirement, *a single one* must be changed write to its context too, you must change the input and output arguments to all e1 ... eN.

Not always.  Suppose that ComputedWithContext took on some new property (like a second input context or something) that was only used by eI and eK but otherwise ignored.  Call that new context ComputedWithExpandedContext One could use an implicit conversions to give all the unmodified e(s) the inputs they are looking for while giving eI and eK the full data set from ComputedWithExpandedContext.  
Perhaps with Haskell this is trivial because of the level of inference going on, but it is a concern in scala, no, that a tiny change might result in hours of development overhead? Are these reasonable observations?

In Haskell I think you would have to explicitly lift the unmodified e(s) into the new ComputedWithExpandedContext.
Clearly there are cases where pain could be injected like altering the input (read-only) context in incompatible ways.
I'm sure that there may exist even better ways to address this issue, but it will be often the case that you can accommodate small input changes with small code changes.  There is an excellent discussion of this in Wadler's "The Essence of Functional Programming".
-- Jim Powers
Ross Rose
Joined: 2011-09-10,
User offline. Last seen 7 hours 26 min ago.
Re: Dependency Injection

I don't recall your constraints so cannot answer your question.

The devil's in the details. For those not familiar with the constraints around a solution for my argument, here they are as posted previously:
{quote}Perhaps a use case for DI might be in order. Scenario: developing a (very) large solution in field F for a specific domain D that provides service type T. A new domain D2 is later acquired that falls in F and must provide T. Every aspect about T is the same except for the underlying business rules that apply to the data for D2. Without DI, one would be left maintaining an entirely new branch of T as a separate solution rather than simply injecting those classes which apply the rules for the domain. Now, compound this with new domains being added every year or two (sometimes multiple at one time).
{/quote}
Now, whenever a change to the codebase occurs (within the enterprise), it must be redeployed to all domains to maintain a level environment due to support and maintenance. Thus, recompiling the entire solution for a few altered rules, regardless where they reside in the targeted source, results in all domains needing redeployed. Said deployment can consume up to 20+ man hours _per domain_. DI solves this because the rules for each domain Dx are the only portion that gets compiled and added to any new domains.
From a purist FP stance, DI may be anti-thetical. However, scala is not a pure FP platform and pure FP cannot solve this matter (of recompilation and redeployment) to the best of my knowledge. Thus, I turn to scala's object-functional nature to leverage it as best as possible given the constraints that it may not be a pure FP solution. The results, I hope, will be the best DI container for the foreseeable future that allows for functional solutions to problems in a way that has never before been seen.
Billy
alois.cochard
Joined: 2010-03-19,
User offline. Last seen 34 weeks 3 hours ago.
Re: Dependency Injection
Chris Marshall
Joined: 2009-06-17,
User offline. Last seen 44 weeks 3 days ago.
RE: Dependency Injection
Of course!
scala> implicit def WriteWithContext_is_ComputedWithContext[A](w : WriteWithContext[A]) : ComputedWithContext[A] = ComputedWithContext[A](cx => w.cx(cx)._1)WriteWithContext_is_ComputedWithContext: [A](w: WriteWithContext[A])ComputedWithContext[A]

scala> implicit def ComputedWithContext_is_WriteWithContext[A](c: ComputedWithContext[A]):  WriteWithContext[A] =  WriteWithContext[A](cx => c.cx(cx) -> cx)ComputedWithContext_is_WriteWithContext: [A](c: ComputedWithContext[A])WriteWithContext[A]

Chris

From: jim [at] casapowers [dot] com
Date: Mon, 12 Sep 2011 20:24:30 -0400
Subject: Re: [scala-debate] Dependency Injection
To: oxbow_lakes [at] hotmail [dot] com
CC: tmorris [at] tmorris [dot] net; scala-debate [at] googlegroups [dot] com

On Mon, Sep 12, 2011 at 11:32 AM, Chris Marshall <oxbow_lakes [at] hotmail [dot] com> wrote:
I think this is a fantastic email; however there is one caveat - you imply that the "program" is unchanged, which is true for the semantic structure you have defined. 
It is not true for the program constructs themselves, though (i.e. e1, e2 and e3), where the ComputedWithContext permeates both their input and output. 
What is more, a program which uses many constructs e1, e2 ... eN, all of which are ComputedWithContext, for which, upon fulfilling some new requirement, *a single one* must be changed write to its context too, you must change the input and output arguments to all e1 ... eN.

Not always.  Suppose that ComputedWithContext took on some new property (like a second input context or something) that was only used by eI and eK but otherwise ignored.  Call that new context ComputedWithExpandedContext One could use an implicit conversions to give all the unmodified e(s) the inputs they are looking for while giving eI and eK the full data set from ComputedWithExpandedContext.  
Perhaps with Haskell this is trivial because of the level of inference going on, but it is a concern in scala, no, that a tiny change might result in hours of development overhead? Are these reasonable observations?

In Haskell I think you would have to explicitly lift the unmodified e(s) into the new ComputedWithExpandedContext.
Clearly there are cases where pain could be injected like altering the input (read-only) context in incompatible ways.
I'm sure that there may exist even better ways to address this issue, but it will be often the case that you can accommodate small input changes with small code changes.  There is an excellent discussion of this in Wadler's "The Essence of Functional Programming".
-- Jim Powers
marius
Joined: 2008-08-31,
User offline. Last seen 3 years 19 weeks ago.
Re: Dependency Injection
That's how the state monad is born :) .. and the state monad is a beautiful example of DI done right !

Thanks,
Marius

On Mon, Sep 12, 2011 at 2:53 PM, Tony Morris <tonymorris [at] gmail [dot] com> wrote:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 09/12/2011 09:38 PM, Billy wrote:
> A debate started on the scala-language group that I am moving to this
> group as it is more fitting here. The matter of DI was discussed, and
> I am actively porting the DI portions of Spring to scala to answer a
> question of practicality. In this effort, the resulting solution
> (which I have dubbed "Recoil") may end up not looking anything like
> the original framework. The requirements that spawned this are a
> matter of practicality around updating large solutions that have
> already been put into production use. My goal is to see if an object-
> functional framework can be devised that allows for zero updates to
> existing solutions so as to maintain a single codebase within a global
> scale enterprise. It is hoped that such a solution will allow for
> domain specific rules to be injected in order to meet the needs of
> multiple different domains without apriori knowledge of said rules.
>
> Billy

[moving to scala-debate per agreed request]

I don't recall your constraints so cannot answer your question.

Someone (I forget who) recently wrote (I forget where): "DI is just
socially acceptable global variables." This is mostly true -- I say
mostly because I think the adverb "globally" is redundant and
misleading. That is to say, there is no such thing as a global variable.
All variables are scoped to some context and it is the extent of this
context that is a measure of detriment. This is why you hear people
talking about "keeping their side-effects local." This wishful-thinking
almost never eventuates because side-effects are pervasive. I am
side-tracking here, but going back to the original topic briefly.

In the absence of my awareness of your constraints, I can point out what
it is that most people want when they think they want DI, and in fact,
do not, ever (it is one of many forms of masochism in programming --
bare with me).

First, let us consider a general Scala program and generalise it. This
is just an arbitrary program -- I am trying to make it as convoluted as
possible so that you can go back to a real program and apply the same
reasoning. Importantly, this program is side-effect free at this point.

val a = e1
val b = e2(a)
val c = e3(a, b)
val d = e2(b)

OK, now I am going to generalise it by running the same program, but in
a for-comprehension. We do this by following these rules:
1) Remove the 'val' keyword
2) The = symbol becomes <-
3) We wrap the program in for and yield

I am going to create a data type that simply wraps a value and provides
flatMap and map methods so I can do this:

case class Id[A](i: A) {
 def map[B](f: A => B) = Id(f(i))
 def flatMap[B](f: A => Id[B]) = f(i)
}

...and since I don't want to explicitly wrap/unwrap my values with Id, I
am going to provide an implicit for in and out:

object Id {
 implicit def IdIn[A](a: A) = Id(a)
 implicit def IdOut[A](a: Id[A]) = a.i
}

OK, so now let's translate our program:

for {
 a <- e1
 b <- e2(a)
 c <- e3(a, b)
 d <- e2(b)
} yield d

Now that you accept that any program can be written this way, let us
step away for a moment and address the idea of DI. There are usually two
variations on DI:
1) The "configuration" (or context) is done and the application must
start by first initialising this context, then the application may run.
The application then reads from the configuration during run-time but
does not modify it. If this order is altered, you end up with a broken
program. A "DI" container attempts to promise you that no such thing
will occur -- this is essentially what the selling point is.

This dependency on explicit execution order is directly anti-thetical to
the functional programming thesis. This is a consequence of there being
a widely-scoped variable that kind-of pretends otherwise.

If you turn your head just a little, you can see this is a somewhat
degenerate notion of what is called "uniqueness typing." I digress.

2) Same as above, however, not only is the application permitted to read
the configuration, but it is also permitted to *write* to it. This means
that the application depends on *more* explicit execution order and the
possibility of bugs increases even more.

Imagine if I said, "you know what, turn all that DI stuff off, we are
going to initialise our values up front and pass them all the way
through the application." You would surely protest, "but that is so
clumsy!" and you'd be right, but only at first glance.

You see, there is a way to pass these values through quite neatly and
no, this is not using Scala's implicit keyword (which is insufficient),
this is something else. OK, so let's first start by thinking about case
1) above where the application only has read access to some context. I
will name this context, "Context", it is a data type that is
somewhere-or-other that we would like to pass through our application --
but no writes to it. I'm sure you can imagine what Context would really
Erik Hetzner
Joined: 2010-12-22,
User offline. Last seen 42 years 45 weeks ago.
Re: Dependency Injection

At Mon, 12 Sep 2011 13:11:13 -0700,
Russ Paielli wrote:
>
> Tony,
>
> That looks interesting. I'm wondering how I can apply it to my problem and
> what exactly it can do for me. I'm developing an R&D prototype for tactical
> conflict detection and resolution for air traffic (can be used as an
> alerting aid for air traffic controllers or can automatically compute
> resolution maneuvers). It has a couple of dozen switches and parameters.
>
> My first approach was to just create a top-level object called "Config" that
> contains all the switches and parameters, and access it essentially as a
> global. That works, but it has a couple of limitations. First, several of my
> classes depend on one or more values in it, so those classes are not as
> conveniently reusable as they would otherwise be. That is not necessarily a
> major issue for me. The more important issue for me is that, to change any
> switch or parameter, the user needs to recompile. That is not always
> convenient for someone else running my code (in a simulation, for example).
>
> My current approach eliminates the need to recompile. Instead of using an
> object, I use a class called Config that has defaults for all switches and
> parameters and a constructor that accepts command-line arguments to override
> those defaults if desired. My main program passes the command-line
> arguments, if any are provided, to an instance of the Config class, and I
> use my class called "CommandLine" to parse those arguments. This allows the
> user to set a few important switches and parameters without recompiling
> (most of the parameters are rarely varied, so they don't need to be
> configurable from the command line).

Hi Russ,

You can also use something along the lines of my ssconf [1] to compile
a config file at runtime.

trait MyConfig extends SSConfig {
val alphaTrackFilter = new Value(0.75);
...
}

And your config file looks like this (it will be compiled at runtime):

alphaTrackFilter := 0.80

best, Erik

1. http://bitbucket.org/egh/ssconf

Ross Rose
Joined: 2011-09-10,
User offline. Last seen 7 hours 26 min ago.
Re: Dependency Injection

These are the exact types of examples and ideas I hoped this discussion would kick up...

Billy

On Sep 13, 2011, at 11:22, Erik Hetzner wrote:

> At Mon, 12 Sep 2011 13:11:13 -0700,
> Russ Paielli wrote:
>>
>> Tony,
>>
>> That looks interesting. I'm wondering how I can apply it to my problem and
>> what exactly it can do for me. I'm developing an R&D prototype for tactical
>> conflict detection and resolution for air traffic (can be used as an
>> alerting aid for air traffic controllers or can automatically compute
>> resolution maneuvers). It has a couple of dozen switches and parameters.
>>
>> My first approach was to just create a top-level object called "Config" that
>> contains all the switches and parameters, and access it essentially as a
>> global. That works, but it has a couple of limitations. First, several of my
>> classes depend on one or more values in it, so those classes are not as
>> conveniently reusable as they would otherwise be. That is not necessarily a
>> major issue for me. The more important issue for me is that, to change any
>> switch or parameter, the user needs to recompile. That is not always
>> convenient for someone else running my code (in a simulation, for example).
>>
>> My current approach eliminates the need to recompile. Instead of using an
>> object, I use a class called Config that has defaults for all switches and
>> parameters and a constructor that accepts command-line arguments to override
>> those defaults if desired. My main program passes the command-line
>> arguments, if any are provided, to an instance of the Config class, and I
>> use my class called "CommandLine" to parse those arguments. This allows the
>> user to set a few important switches and parameters without recompiling
>> (most of the parameters are rarely varied, so they don't need to be
>> configurable from the command line).
>
> Hi Russ,
>
> You can also use something along the lines of my ssconf [1] to compile
> a config file at runtime.
>
> trait MyConfig extends SSConfig {
> val alphaTrackFilter = new Value(0.75);
> ...
> }
>
> And your config file looks like this (it will be compiled at runtime):
>
> alphaTrackFilter := 0.80
>
> best, Erik
>
> 1. http://bitbucket.org/egh/ssconf
> Sent from my free software system .

Russ P.
Joined: 2009-01-31,
User offline. Last seen 1 year 26 weeks ago.
Re: Dependency Injection

Interesting. I didn't even realize that it is possible to compile a
file at runtime in Scala. So if it fails to compile, is it a compile
error or a runtime error? Also, is ssconf considered an internal DSL
or an external DSL (or neither)? Thanks.

--Russ P.

> Hi Russ,
>
> You can also use something along the lines of my ssconf [1] to compile
> a config file at runtime.
>
>   trait MyConfig extends SSConfig {
>     val alphaTrackFilter = new Value(0.75);
>     ...
>   }
>
> And your config file looks like this (it will be compiled at runtime):
>
>   alphaTrackFilter := 0.80
>
> best, Erik
>
> 1.http://bitbucket.org/egh/ssconf
>
> Sent from my free software system .

Erik Hetzner
Joined: 2010-12-22,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Dependency Injection

At Tue, 13 Sep 2011 16:38:44 -0700 (PDT),
Russ P. wrote:
>
> Interesting. I didn't even realize that it is possible to compile a
> file at runtime in Scala. So if it fails to compile, is it a compile
> error or a runtime error? Also, is ssconf considered an internal DSL
> or an external DSL (or neither)? Thanks.

Hi Russ,

You need to bundle the scala-compiler jar with your runtime, but it’s
the same thing the REPL does, basically.

Here is what a syntax error looks like (it throws a
java.lang.Exception, so you can catch errors when loading your
configs):

/tmp/configurator3129450973169847382scala:4: error: not found: value XXX
traitTest := XXX;

I suppose it would be an internal DSL, because the syntax is
(almost-)pure scala, (I say almost because we append some wrappers
around the text so that config files can look like:

foo := 1;
bar := true;

without the boilerplate object definition wrapped around it.

best, Erik

Tony Morris 2
Joined: 2009-03-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Dependency Injection
On 09/13/2011 01:32 AM, Chris Marshall wrote:
> I think this is a fantastic email; however there is one caveat - you imply that the "program" is unchanged, which is true for the semantic structure you have defined.
>
> It is not true for the program /constructs /themselves, though (i.e. e1, e2 and e3), where the ComputedWithContext permeates both their input and output.

Correct. By "changed" I just mean the syntax and I only mean to imply that therefore, there is some "common pattern" going on here.

>
> What is more, a program which uses many constructs e1, e2 ... eN, all of which are ComputedWithContext, for which, upon fulfilling some new requirement, *a single one* must be changed write to its context too, you must change the input and output arguments to all e1 ... eN.

I'm not sure what you mean here. We have a high degree of composability, so any new requirements are easily catered for. This is very much unlike traditional DI, where there is a high likelihood of explosion due to the brittle nature of programs that depend on explicit order of execution.

>
> Perhaps with Haskell this is trivial because of the level of inference going on, but it is a concern in scala, no, that a tiny change might result in hours of development overhead? Are these reasonable observations?

Only to the extent that scala's type inference is lacking compared to haskell in general. This is a minor concern compared to getting the overall picture right. In practice, you typically end up putting in more effort to the library code to alleviate burden on the client code and this just becomes a bit trickier with scala, but nothing too concerning.

>
> Chris
>
>
> > Date: Mon, 12 Sep 2011 21:53:50 +1000
> > From: tonymorris [at] gmail [dot] com
> > To: scala-debate [at] googlegroups [dot] com
> > Subject: Re: [scala-debate] Dependency Injection
> >
On 09/12/2011 09:38 PM, Billy wrote:
> A debate started on the scala-language group that I am moving to this
> group as it is more fitting here. The matter of DI was discussed, and
> I am actively porting the DI portions of Spring to scala to answer a
> question of practicality. In this effort, the resulting solution
> (which I have dubbed "Recoil") may end up not looking anything like
> the original framework. The requirements that spawned this are a
> matter of practicality around updating large solutions that have
> already been put into production use. My goal is to see if an object-
> functional framework can be devised that allows for zero updates to
> existing solutions so as to maintain a single codebase within a global
> scale enterprise. It is hoped that such a solution will allow for
> domain specific rules to be injected in order to meet the needs of
> multiple different domains without apriori knowledge of said rules.

> Billy

[moving to scala-debate per agreed request]

I don't recall your constraints so cannot answer your question.

Someone (I forget who) recently wrote (I forget where): "DI is just
socially acceptable global variables." This is mostly true -- I say
mostly because I think the adverb "globally" is redundant and
misleading. That is to say, there is no such thing as a global variable.
All variables are scoped to some context and it is the extent of this
context that is a measure of detriment. This is why you hear people
talking about "keeping their side-effects local." This wishful-thinking
almost never eventuates because side-effects are pervasive. I am
side-tracking here, but going back to the original topic briefly.

In the absence of my awareness of your constraints, I can point out what
it is that most people want when they think they want DI, and in fact,
do not, ever (it is one of many forms of masochism in programming --
bare with me).

First, let us consider a general Scala program and generalise it. This
is just an arbitrary program -- I am trying to make it as convoluted as
possible so that you can go back to a real program and apply the same
reasoning. Importantly, this program is side-effect free at this point.

val a = e1
val b = e2(a)
val c = e3(a, b)
val d = e2(b)

OK, now I am going to generalise it by running the same program, but in
a for-comprehension. We do this by following these rules:
1) Remove the 'val' keyword
2) The = symbol becomes <-
3) We wrap the program in for and yield

I am going to create a data type that simply wraps a value and provides
flatMap and map methods so I can do this:

case class Id[A](i: A) {
def map[B](f: A => B) = Id(f(i))
def flatMap[B](f: A => Id[B]) = f(i)
}

...and since I don't want to explicitly wrap/unwrap my values with Id, I
am going to provide an implicit for in and out:

object Id {
implicit def IdIn[A](a: A) = Id(a)
implicit def IdOut[A](a: Id[A]) = a.i
}

OK, so now let's translate our program:

for {
a <- e1
b <- e2(a)
c <- e3(a, b)
d <- e2(b)
} yield d

Now that you accept that any program can be written this way, let us
step away for a moment and address the idea of DI. There are usually two
variations on DI:
1) The "configuration" (or context) is done and the application must
start by first initialising this context, then the application may run.
The application then reads from the configuration during run-time but
does not modify it. If this order is altered, you end up with a broken
program. A "DI" container attempts to promise you that no such thing
will occur -- this is essentially what the selling point is.

This dependency on explicit execution order is directly anti-thetical to
the functional programming thesis. This is a consequence of there being
a widely-scoped variable that kind-of pretends otherwise.

If you turn your head just a little, you can see this is a somewhat
degenerate notion of what is called "uniqueness typing." I digress.

2) Same as above, however, not only is the application permitted to read
the configuration, but it is also permitted to *write* to it. This means
that the application depends on *more* explicit execution order and the
possibility of bugs increases even more.

Imagine if I said, "you know what, turn all that DI stuff off, we are
going to initialise our values up front and pass them all the way
through the application." You would surely protest, "but that is so
clumsy!" and you'd be right, but only at first glance.

You see, there is a way to pass these values through quite neatly and
no, this is not using Scala's implicit keyword (which is insufficient),
this is something else. OK, so let's first start by thinking about case
1) above where the application only has read access to some context. I
will name this context, "Context", it is a data type that is
somewhere-or-other that we would like to pass through our application --
but no writes to it. I'm sure you can imagine what Context would really
be -- feel free to make it up for the use-case.

So, our values that were once mere values, are now computed as if they
have access to a Context. We can denote this with a data type:

case class ComputedWithContext[A](cx: Context => A)

So we now have "first-class" values computed with a Context, rather than
being mere values. We can now create these by accessing a Context "as if
it were passed" -- that is to say, although we don't yet have a Context,
we may create values that access that Context (when it is eventually
passed) by wrapping a function (or a trait if you prefer).

This is simple and straight-forward enough. But watch this:

case class ComputedWithContext[A](cx: Context => A) {
def map[B](f: A => B): ComputedWithContext[B] = ComputedWithContext(f
compose cx)
def flatMap[B](f: A => ComputedWithContext[B]): ComputedWithContext[B]
= ComputedWithContext(c => f(cx(c)) cx c)
}

We see here that ComputedWithContext happens to have pretty handy map
and flatMap methods. What can we do with them?

OK, so suppose our program above is a little different to the original
in that actually, our expressions (e1, e2 and e3) require a Context, so
each of them becomes become ComputedWithContext[T] where previously they
were just the type T (they may be all different values for T or same --
no matter).

For example, e1 may have been an Int where now it is a
ComputedWithContext[Int] and e2 may have been a String where now it is a
ComputedWithContext[String]. You get the point.

Here is how our program looks:

for {
a <- e1
b <- e2(a)
c <- e3(a, b)
d <- e2(b)
} yield d

This is precisely the same program syntax. The type of this expression
is ComputedWithContext[T] where the type T depends on the value d. In
other words, we may pass a Context in to this value and it gets
"threaded" through our program and our program *doesn't change* if we
write it in this general form. We may "stack these layers" on top of
what started as Id and our program remains unaltered. The "theory" of
doing this is quite involved, mostly because it is kick-arse interesting
and we could talk about it some time, but that's another story!

Importantly, there are no variables here. Not one and not a pretend
value that is actually a variable at application time (which I'm sure
you've been reminded of more than once when using DI).

So, this is how we deal with passing read-only context through our
application:
* without being clumsy by explicitly passing it
* being quite efficient and readable in fact!
* without using variables that leads to program bugs and difficulty
reading and debugging code

How do we deal with read and write values (case 2)? Well, we need a new
different data type for that:

case class WriteWithContext[A](cx: Context => (A, Context))

Notice how this is the same data type as before except the function can
now produce a *new* Context as well as the computed value (paired). This
is to say, we may "modify" the Context as it is threaded through. But
what about map and flatMap, can we write those? Of course:

case class WriteWithContext[A](cx: Context => (A, Context)) {
def map[B](f: A => B): WriteWithContext[B] = WriteWithContext(c => val
(a, cc) = cx(c); (f(a), cc))
def flatMap[B](f: A => WriteWithContext[B]): WriteWithContext[B] =
WriteWithContext(c => { val (a, cc) = cx(c); f(a) cx cc })
}

Don't get too carried away with reading those methods, but just note
that flatMap "threads the Context through whatever the function is,
which may be modifying it."

OK, so now if we suppose that our expressions (e1, e2, e3) actually had
access to the Context, but were also able to "modify" it by returning a
new Context (or just leaving it alone, for which there is library
support of course), then our program would look like this:

for {
a <- e1
b <- e2(a)
c <- e3(a, b)
d <- e2(b)
} yield d

Yep, exactly the same as before. So now we have a value that we can pass
in a Context and it is threaded through the program, potentially
"modifying" the Context as it is threaded through and we get a value and
the resulting Context at the end. We may wish to drop either of these --
in practice, the Context often gets dropped, since it was only need to
compute the value -- and of course, there is library support for that.

So hopefully now you see that DI can be replaced by a superior
programming model, at least for this example, and I promise, for any
example. We just have to come to terms with a few data types and
abstractions and we can kick that baby to the gutter where it belongs.

Hope that helps!



--
Tony Morris
http://tmorris.net/


Tony Morris 2
Joined: 2009-03-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Dependency Injection

On 09/13/2011 04:06 AM, Nikolay Artamonov wrote:
> Thanks, Tony! Was it Reader and Writer monads explained? Can we say
> that these monads are FP alternative to DI? By the way, are there
> equivalents in Scalaz to haskell's type classes MonadReader/
> MonadWriter?

You're very close.

There are three "traditional" types of data structure that capture these
ideas:

1) Reader -- reads an environment to compute but does not modify it.
2) Writer -- writes to an environment without any inspection/read of
that environment (think: logging)
3) State -- reads and environment and potentially modifies it.

There is also a fourth that combines all three called RWS or
ReaderWriterState in Scalaz7. This is probably a closer description of
dependency injection itself, but the explanation would have got a bit
confusing. Further, all four of these can (and often are) be "lifted" to
thread yet another environment through.

The two cases I gave were Reader and State.

Scalaz hasn't implemented MonadWriter and MonadReader type-classes for
no other reason than nobody has got to it yet! Going to be that one? :)

> On Sep 12, 3:53 pm, Tony Morris wrote:
>> Someone (I forget who) recently wrote (I forget where): "DI is just
>> socially acceptable global variables." This is mostly true -- I say
>> mostly because I think the adverb "globally" is redundant and
>> misleading. That is to say, there is no such thing as a global variable.
>> All variables are scoped to some context and it is the extent of this
>> context that is a measure of detriment. This is why you hear people
>> talking about "keeping their side-effects local." This wishful-thinking
>> almost never eventuates because side-effects are pervasive. I am
>> side-tracking here, but going back to the original topic briefly.
>>
>> In the absence of my awareness of your constraints, I can point out what
>> it is that most people want when they think they want DI, and in fact,
>> do not, ever (it is one of many forms of masochism in programming --
>> bare with me).
>>
>> First, let us consider a general Scala program and generalise it. This
>> is just an arbitrary program -- I am trying to make it as convoluted as
>> possible so that you can go back to a real program and apply the same
>> reasoning. Importantly, this program is side-effect free at this point.
>>
>> val a = e1
>> val b = e2(a)
>> val c = e3(a, b)
>> val d = e2(b)
>>
>> OK, now I am going to generalise it by running the same program, but in
>> a for-comprehension. We do this by following these rules:
>> 1) Remove the 'val' keyword
>> 2) The = symbol becomes <-
>> 3) We wrap the program in for and yield
>>
>> I am going to create a data type that simply wraps a value and provides
>> flatMap and map methods so I can do this:
>>
>> case class Id[A](i: A) {
>> def map[B](f: A => B) = Id(f(i))
>> def flatMap[B](f: A => Id[B]) = f(i)
>>
>> }
>>
>> ...and since I don't want to explicitly wrap/unwrap my values with Id, I
>> am going to provide an implicit for in and out:
>>
>> object Id {
>> implicit def IdIn[A](a: A) = Id(a)
>> implicit def IdOut[A](a: Id[A]) = a.i
>>
>> }
>>
>> OK, so now let's translate our program:
>>
>> for {
>> a <- e1
>> b <- e2(a)
>> c <- e3(a, b)
>> d <- e2(b)
>>
>> } yield d
>>
>> Now that you accept that any program can be written this way, let us
>> step away for a moment and address the idea of DI. There are usually two
>> variations on DI:
>> 1) The "configuration" (or context) is done and the application must
>> start by first initialising this context, then the application may run.
>> The application then reads from the configuration during run-time but
>> does not modify it. If this order is altered, you end up with a broken
>> program. A "DI" container attempts to promise you that no such thing
>> will occur -- this is essentially what the selling point is.
>>
>> This dependency on explicit execution order is directly anti-thetical to
>> the functional programming thesis. This is a consequence of there being
>> a widely-scoped variable that kind-of pretends otherwise.
>>
>> If you turn your head just a little, you can see this is a somewhat
>> degenerate notion of what is called "uniqueness typing." I digress.
>>
>> 2) Same as above, however, not only is the application permitted to read
>> the configuration, but it is also permitted to *write* to it. This means
>> that the application depends on *more* explicit execution order and the
>> possibility of bugs increases even more.
>>
>> Imagine if I said, "you know what, turn all that DI stuff off, we are
>> going to initialise our values up front and pass them all the way
>> through the application." You would surely protest, "but that is so
>> clumsy!" and you'd be right, but only at first glance.
>>
>> You see, there is a way to pass these values through quite neatly and
>> no, this is not using Scala's implicit keyword (which is insufficient),
>> this is something else. OK, so let's first start by thinking about case
>> 1) above where the application only has read access to some context. I
>> will name this context, "Context", it is a data type that is
>> somewhere-or-other that we would like to pass through our application --
>> but no writes to it. I'm sure you can imagine what Context would really
>> be -- feel free to make it up for the use-case.
>>
>> So, our values that were once mere values, are now computed as if they
>> have access to a Context. We can denote this with a data type:
>>
>> case class ComputedWithContext[A](cx: Context => A)
>>
>> So we now have "first-class" values computed with a Context, rather than
>> being mere values. We can now create these by accessing a Context "as if
>> it were passed" -- that is to say, although we don't yet have a Context,
>> we may create values that access that Context (when it is eventually
>> passed) by wrapping a function (or a trait if you prefer).
>>
>> This is simple and straight-forward enough. But watch this:
>>
>> case class ComputedWithContext[A](cx: Context => A) {
>> def map[B](f: A => B): ComputedWithContext[B] = ComputedWithContext(f
>> compose cx)
>> def flatMap[B](f: A => ComputedWithContext[B]): ComputedWithContext[B]
>> = ComputedWithContext(c => f(cx(c)) cx c)
>>
>> }
>>
>> We see here that ComputedWithContext happens to have pretty handy map
>> and flatMap methods. What can we do with them?
>>
>> OK, so suppose our program above is a little different to the original
>> in that actually, our expressions (e1, e2 and e3) require a Context, so
>> each of them becomes become ComputedWithContext[T] where previously they
>> were just the type T (they may be all different values for T or same --
>> no matter).
>>
>> For example, e1 may have been an Int where now it is a
>> ComputedWithContext[Int] and e2 may have been a String where now it is a
>> ComputedWithContext[String]. You get the point.
>>
>> Here is how our program looks:
>>
>> for {
>> a <- e1
>> b <- e2(a)
>> c <- e3(a, b)
>> d <- e2(b)
>>
>> } yield d
>>
>> This is precisely the same program syntax. The type of this expression
>> is ComputedWithContext[T] where the type T depends on the value d. In
>> other words, we may pass a Context in to this value and it gets
>> "threaded" through our program and our program *doesn't change* if we
>> write it in this general form. We may "stack these layers" on top of
>> what started as Id and our program remains unaltered. The "theory" of
>> doing this is quite involved, mostly because it is kick-arse interesting
>> and we could talk about it some time, but that's another story!
>>
>> Importantly, there are no variables here. Not one and not a pretend
>> value that is actually a variable at application time (which I'm sure
>> you've been reminded of more than once when using DI).
>>
>> So, this is how we deal with passing read-only context through our
>> application:
>> * without being clumsy by explicitly passing it
>> * being quite efficient and readable in fact!
>> * without using variables that leads to program bugs and difficulty
>> reading and debugging code
>>
>> How do we deal with read and write values (case 2)? Well, we need a new
>> different data type for that:
>>
>> case class WriteWithContext[A](cx: Context => (A, Context))
>>
>> Notice how this is the same data type as before except the function can
>> now produce a *new* Context as well as the computed value (paired). This
>> is to say, we may "modify" the Context as it is threaded through. But
>> what about map and flatMap, can we write those? Of course:
>>
>> case class WriteWithContext[A](cx: Context => (A, Context)) {
>> def map[B](f: A => B): WriteWithContext[B] = WriteWithContext(c => val
>> (a, cc) = cx(c); (f(a), cc))
>> def flatMap[B](f: A => WriteWithContext[B]): WriteWithContext[B] =
>> WriteWithContext(c => { val (a, cc) = cx(c); f(a) cx cc })
>>
>> }
>>
>> Don't get too carried away with reading those methods, but just note
>> that flatMap "threads the Context through whatever the function is,
>> which may be modifying it."
>>
>> OK, so now if we suppose that our expressions (e1, e2, e3) actually had
>> access to the Context, but were also able to "modify" it by returning a
>> new Context (or just leaving it alone, for which there is library
>> support of course), then our program would look like this:
>>
>> for {
>> a <- e1
>> b <- e2(a)
>> c <- e3(a, b)
>> d <- e2(b)
>>
>> } yield d
>>
>> Yep, exactly the same as before. So now we have a value that we can pass
>> in a Context and it is threaded through the program, potentially
>> "modifying" the Context as it is threaded through and we get a value and
>> the resulting Context at the end. We may wish to drop either of these --
>> in practice, the Context often gets dropped, since it was only need to
>> compute the value -- and of course, there is library support for that.
>>
>> So hopefully now you see that DI can be replaced by a superior
>> programming model, at least for this example, and I promise, for any
>> example. We just have to come to terms with a few data types and
>> abstractions and we can kick that baby to the gutter where it belongs.
>>
>> Hope that helps!

Tony Morris 2
Joined: 2009-03-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Dependency Injection
On 09/13/2011 03:11 AM, Razvan Cojocaru wrote:
> Nice!! - you should turn this email into a blog (or a series of). Seriously,
> I find it very clear and a nice progression... although now, unlike when I
> was staring at elephants, I kind of know what you're talking about ;)
>
> If this is how the book is going to be, I can't wait!

Glad it helps mate.

I'm not sure what such a series would look like and I don't want to be "the guy who destroyed the merits of dependency injection", for no other reason than I don't have the time to endure the consequences! I'd rather just share ideas with those who care and I am glad you're one of them :)


--
Tony Morris
http://tmorris.net/


Tony Morris 2
Joined: 2009-03-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Dependency Injection
On 09/13/2011 11:07 AM, Ross Rose wrote:
D4BF00AD-52A9-4FC1-BBF7-C69F597FC316 [at] gmail [dot] com" type="cite">
I don't recall your constraints so cannot answer your question.

The devil's in the details. For those not familiar with the constraints around a solution for my argument, here they are as posted previously:
{quote} Perhaps a use case for DI might be in order. Scenario: developing a (very) large solution in field F for a specific domain D that provides service type T. A new domain D2 is later acquired that falls in F and must provide T. Every aspect about T is the same except for the underlying business rules that apply to the data for D2. Without DI, one would be left maintaining an entirely new branch of T as a separate solution rather than simply injecting those classes which apply the rules for the domain. Now, compound this with new domains being added every year or two (sometimes multiple at one time).
{/quote}

You're talking about program composition in general here. This is the very thesis of functional programming.

D4BF00AD-52A9-4FC1-BBF7-C69F597FC316 [at] gmail [dot] com" type="cite">
Now, whenever a change to the codebase occurs (within the enterprise), it must be redeployed to all domains to maintain a level environment due to support and maintenance. Thus, recompiling the entire solution for a few altered rules, regardless where they reside in the targeted source, results in all domains needing redeployed. Said deployment can consume up to 20+ man hours _per domain_. DI solves this because the rules for each domain Dx are the only portion that gets compiled and added to any new domains.

Sorry, but DI not only doesn't solve it, but proactively destroys it. I'm hoping my earlier introduction at least sets off an alarm that maybe this is true -- driving it all the way would be a considerably longer essay.

D4BF00AD-52A9-4FC1-BBF7-C69F597FC316 [at] gmail [dot] com" type="cite">
From a purist FP stance, DI may be anti-thetical. However, scala is not a pure FP platform and pure FP cannot solve this matter (of recompilation and redeployment) to the best of my knowledge.

Actually, there is a very useful subset of Scala that is pure functional, but this is quite beside the point.

Pure functional programming doesn't solve this matter -- because it *is* this matter. You're either achieving this goal and doing pure functional programming or you're not achieving this goal and you're not doing functional programming. This is the very thesis of functional programming only stated very briefly. This might help:

1) http://www.cs.utexas.edu/~shmat/courses/cs345/whyfp.pdf
2) http://homepages.inf.ed.ac.uk/wadler/papers/essence/essence.ps

I'd rather not get into the matter of "needing to recompile" because it causes too much flaming without any substance. There is also a great paper on the topic and I cannot google it up -- if someone can remind me, I'd most appreciate it.


D4BF00AD-52A9-4FC1-BBF7-C69F597FC316 [at] gmail [dot] com" type="cite"> Thus, I turn to scala's object-functional nature to leverage it as best as possible given the constraints that it may not be a pure FP solution. The results, I hope, will be the best DI container for the foreseeable future that allows for functional solutions to problems in a way that has never before been seen.

The best DI container is the one that does not exist in my opinion. I hope I've given a reasonable enough introduction into why I hold this opinion.

D4BF00AD-52A9-4FC1-BBF7-C69F597FC316 [at] gmail [dot] com" type="cite">
Billy


-- 
Tony Morris
http://tmorris.net/

Tony Morris 2
Joined: 2009-03-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Dependency Injection

On 09/13/2011 06:11 AM, Russ Paielli wrote:
> Tony,
>
> That looks interesting. I'm wondering how I can apply it to my problem
> and what exactly it can do for me. I'm developing an R&D prototype for
> tactical conflict detection and resolution for air traffic (can be
> used as an alerting aid for air traffic controllers or can
> automatically compute resolution maneuvers). It has a couple of dozen
> switches and parameters.
>
> My first approach was to just create a top-level object called
> "Config" that contains all the switches and parameters, and access it
> essentially as a global. That works, but it has a couple of
> limitations. First, several of my classes depend on one or more values
> in it, so those classes are not as conveniently reusable as they would
> otherwise be. That is not necessarily a major issue for me. The more
> important issue for me is that, to change any switch or parameter, the
> user needs to recompile. That is not always convenient for someone
> else running my code (in a simulation, for example).

This sounds like case 1) aka "Reader." Happy to help if you can give
specifics, but it's really not much more than building a library around
a function as given.

>
> My current approach eliminates the need to recompile. Instead of using
> an object, I use a class called Config that has defaults for all
> switches and parameters and a constructor that accepts command-line
> arguments to override those defaults if desired. My main program
> passes the command-line arguments, if any are provided, to an
> instance of the Config class, and I use my class called "CommandLine"
> to parse those arguments. This allows the user to set a few important
> switches and parameters without recompiling (most of the parameters
> are rarely varied, so they don't need to be configurable from the
> command line).

The whole "need to recompile" problem can get tricky and is also quite
contentious (makes a great fire-starter). I have my own opinions on
this, which are also shared by a few others, but all I can say for now
is: try to avoid it if you can, and if you cannot, push it way way to
the outside. Hope that helps. Sorry for the dodge.

Tony Morris 2
Joined: 2009-03-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Dependency Injection
On 09/13/2011 11:05 PM, Marius Danciu wrote:
> That's how the state monad is born :) .. and the state monad is a beautiful example of DI done right !

*high-five*

To give you a taste of how I would progress the argument if required, I'd use ReaderWriterStateT. You might call this "DI done right", but I prefer to just call it "solving problems, no really, for real" or some such.


trait Functor[F[_]] // todo
trait Monad[F[_]] // todo

trait ReaderWriterStateT[R, W, S, F[_], A] {
  val runT: R => S => F[(A, S, W)]

  def map[B](f: A => B)(implicit z: Functor[F]): ReaderWriterStateT[R, W, S, F, B] =
    error("todo")

  def flatMap[B](f: A => ReaderWriterStateT[R, W, S, F, B])(implicit z: Monad[F]): ReaderWriterStateT[R, W, S, F, B] =
    error("todo")
}

case class Id[A](a: A)

object ReaderWriterStateT {
  type ReaderWriterState[R, W, S, A] =
    ReaderWriterStateT[R, W, S, Id, A]
}



--
Tony Morris
http://tmorris.net/


Russ P.
Joined: 2009-01-31,
User offline. Last seen 1 year 26 weeks ago.
Re: Dependency Injection

On Sep 12, 4:53 am, Tony Morris wrote:

> You see, there is a way to pass these values through quite neatly and
> no, this is not using Scala's implicit keyword (which is insufficient),
> this is something else. OK, so let's first start by thinking about case
> 1) above where the application only has read access to some context. I
> will name this context, "Context", it is a data type that is
> somewhere-or-other that we would like to pass through our application --
> but no writes to it. I'm sure you can imagine what Context would really
> be -- feel free to make it up for the use-case.
>
> So, our values that were once mere values, are now computed as if they
> have access to a Context. We can denote this with a data type:
>
> case class ComputedWithContext[A](cx: Context => A)
>
> So we now have "first-class" values computed with a Context, rather than
> being mere values. We can now create these by accessing a Context "as if
> it were passed" -- that is to say, although we don't yet have a Context,
> we may create values that access that Context (when it is eventually
> passed) by wrapping a function (or a trait if you prefer).
>
> This is simple and straight-forward enough. But watch this:
>
> case class ComputedWithContext[A](cx: Context => A) {
>   def map[B](f: A => B): ComputedWithContext[B] = ComputedWithContext(f
> compose cx)
>   def flatMap[B](f: A => ComputedWithContext[B]): ComputedWithContext[B]
> = ComputedWithContext(c => f(cx(c)) cx c)
>
> }
>
> We see here that ComputedWithContext happens to have pretty handy map
> and flatMap methods. What can we do with them?
>
> OK, so suppose our program above is a little different to the original
> in that actually, our expressions (e1, e2 and e3) require a Context, so
> each of them becomes become ComputedWithContext[T] where previously they
> were just the type T (they may be all different values for T or same --
> no matter).
>
> For example, e1 may have been an Int where now it is a
> ComputedWithContext[Int] and e2 may have been a String where now it is a
> ComputedWithContext[String]. You get the point.
>
> Here is how our program looks:
>
> for {
>   a <- e1
>   b <- e2(a)
>   c <- e3(a, b)
>   d <- e2(b)
>
> } yield d
>
> This is precisely the same program syntax. The type of this expression
> is ComputedWithContext[T] where the type T depends on the value d. In
> other words, we may pass a Context in to this value and it gets
> "threaded" through our program and our program *doesn't change* if we
> write it in this general form. We may "stack these layers" on top of
> what started as Id and our program remains unaltered. The "theory" of
> doing this is quite involved, mostly because it is kick-arse interesting
> and we could talk about it some time, but that's another story!
>
> Importantly, there are no variables here. Not one and not a pretend
> value that is actually a variable at application time (which I'm sure
> you've been reminded of more than once when using DI).
>
> So, this is how we deal with passing read-only context through our
> application:
> * without being clumsy by explicitly passing it
> * being quite efficient and readable in fact!
> * without using variables that leads to program bugs and difficulty
> reading and debugging code

I'm still struggling to understand exactly what this can do for me. I
must be missing something, because the three objectives Tony listed
above seem to me to be easy to achieve if you never need to change
parameter values or if you are allowed to recompile. (I'm assuming
that all parameters and switches are constant for each run but could
change between runs). Tony didn't explicitly state those requirements,
or did I miss them? He said something about the undesirability of
recompiling, but did he say that the parameter values may need to be
changed (between runs)?

If the parameter values never need to change, then the problem is
simple. All you need is a global config object (Tony's "Context"?)
that everything has access to and depends on. As far as I can tell,
that gets you what Tony claims to have achieved above: no clumsy
passing of values, efficient, readable, and no variables.

As I explained in my earlier reply to this message, I'm trying to set
up a read-only conflguration for an application, where the switches
and parameter values are all constant for each run. I would like to

1. be able to flip switches and change parameter values (between runs)
without recompiling

2. keep my configured classes independent of any global configuration
object (excluding their own companion object) so they can be re-used
in another application without modification or recompilation

3. avoid variables

By using my own straightforward methods, I seem to be able to achieve
any two of these three objectives but not all three. Can Tony's method
achieve all three? If so, then I'd say he has something significant.
If not, then it seems to me that his approach is nothing more than an
alternative (and more complicated) way to achieve something that can
be done fairly easily. I'd like to know which it is.

--Russ P.

http://RussP.us

John Nilsson
Joined: 2008-12-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Dependency Injection
On Fri, Sep 16, 2011 at 9:07 AM, Russ P. <russ [dot] paielli [at] gmail [dot] com> wrote:

1. be able to flip switches and change parameter values (between runs)
without recompiling

2. keep my configured classes independent of any global configuration
object (excluding their own companion object) so they can be re-used
in another application without modification or recompilation

3. avoid variables

By using my own straightforward methods, I seem to be able to achieve
any two of these three objectives but not all three.

In what way does 2 fail? Or the other way, how do you achieve this when dropping 1 or 3 ?

BR,
John
Russ P.
Joined: 2009-01-31,
User offline. Last seen 1 year 26 weeks ago.
Re: Re: Dependency Injection
On Fri, Sep 16, 2011 at 12:46 AM, John Nilsson <john [at] milsson [dot] nu> wrote:
On Fri, Sep 16, 2011 at 9:07 AM, Russ P. <russ [dot] paielli [at] gmail [dot] com> wrote:

1. be able to flip switches and change parameter values (between runs)
without recompiling

2. keep my configured classes independent of any global configuration
object (excluding their own companion object) so they can be re-used
in another application without modification or recompilation

3. avoid variables

By using my own straightforward methods, I seem to be able to achieve
any two of these three objectives but not all three.

In what way does 2 fail? Or the other way, how do you achieve this when dropping 1 or 3 ?


I can use command-line arguments to set switches and parameter values without recompiling. Then I have two options:

1. I can pass those values to the other classes to configure them. But to do that, I need a variable in the receiving class.

2. I can save the command-line arguments (or the resulting config object) in the same object that holds main. But then the other classes need to access that object to read their configuration. This allows me to avoid variables, but then those classes depend on the "main" object. That's not quite what I want.

Of the two, I think option 1 is preferable.

--Russ P.

--
http://RussP.us
Tony Morris 2
Joined: 2009-03-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Dependency Injection
On 09/16/2011 06:10 PM, Russ Paielli wrote:
w [at] mail [dot] gmail [dot] com" type="cite">On Fri, Sep 16, 2011 at 12:46 AM, John Nilsson <john [at] milsson [dot] nu" rel="nofollow">john [at] milsson [dot] nu> wrote:
On Fri, Sep 16, 2011 at 9:07 AM, Russ P. <russ [dot] paielli [at] gmail [dot] com" target="_blank" rel="nofollow">russ [dot] paielli [at] gmail [dot] com> wrote:

1. be able to flip switches and change parameter values (between runs)
without recompiling

2. keep my configured classes independent of any global configuration
object (excluding their own companion object) so they can be re-used
in another application without modification or recompilation

3. avoid variables

By using my own straightforward methods, I seem to be able to achieve
any two of these three objectives but not all three.

In what way does 2 fail? Or the other way, how do you achieve this when dropping 1 or 3 ?


I can use command-line arguments to set switches and parameter values without recompiling. Then I have two options:

1. I can pass those values to the other classes to configure them. But to do that, I need a variable in the receiving class.

2. I can save the command-line arguments (or the resulting config object) in the same object that holds main. But then the other classes need to access that object to read their configuration. This allows me to avoid variables, but then those classes depend on the "main" object. That's not quite what I want.

Of the two, I think option 1 is preferable.

--Russ P.

--
http://RussP.us
I think you missed the point of the essay. There are other options.

-- 
Tony Morris
http://tmorris.net/

Russ P.
Joined: 2009-01-31,
User offline. Last seen 1 year 26 weeks ago.
Re: Re: Dependency Injection
On Fri, Sep 16, 2011 at 1:11 AM, Tony Morris <tonymorris [at] gmail [dot] com> wrote:
On 09/16/2011 06:10 PM, Russ Paielli wrote:
On Fri, Sep 16, 2011 at 12:46 AM, John Nilsson <john [at] milsson [dot] nu> wrote:
On Fri, Sep 16, 2011 at 9:07 AM, Russ P. <russ [dot] paielli [at] gmail [dot] com> wrote:

1. be able to flip switches and change parameter values (between runs)
without recompiling

2. keep my configured classes independent of any global configuration
object (excluding their own companion object) so they can be re-used
in another application without modification or recompilation

3. avoid variables

By using my own straightforward methods, I seem to be able to achieve
any two of these three objectives but not all three.

In what way does 2 fail? Or the other way, how do you achieve this when dropping 1 or 3 ?


I can use command-line arguments to set switches and parameter values without recompiling. Then I have two options:

1. I can pass those values to the other classes to configure them. But to do that, I need a variable in the receiving class.

2. I can save the command-line arguments (or the resulting config object) in the same object that holds main. But then the other classes need to access that object to read their configuration. This allows me to avoid variables, but then those classes depend on the "main" object. That's not quite what I want.

Of the two, I think option 1 is preferable.

--Russ P.

--
http://RussP.us
I think you missed the point of the essay. There are other options.

Maybe so, but I'd still like to know if the method you outlined can satisfy the three requirements I listed above. If so, can you provide a simple example to demonstrate it? It would be useful to me and probably others too. Sorry if I am just dense.

--Russ P.

--
http://RussP.us
John Nilsson
Joined: 2008-12-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Dependency Injection
I don't think I follow completely. You want to reference some configuration, but you don't want a dependency on it? The mere fact that you are referencing it is a dependency no matter what technique you use to resolve it.

In any case, if you want a more "hidden" reference you can maybe create factories that does "new MyConfiguredClass with TheConfiguration" for you.

Or you can move the dependency from the classes as such and move it to the operation on the class. def aConfigurableOperation(i:Input, cfg: Config)
or even
def aConfigurableOperation(i:Input)(implicit cfg:Config)
or a variation on that theme
implicit def input2configuredOps(i:Input):ConfiguredOpsOn[Input]


BR,
John


On Fri, Sep 16, 2011 at 10:10 AM, Russ Paielli <russ [dot] paielli [at] gmail [dot] com> wrote:
On Fri, Sep 16, 2011 at 12:46 AM, John Nilsson <john [at] milsson [dot] nu> wrote:
On Fri, Sep 16, 2011 at 9:07 AM, Russ P. <russ [dot] paielli [at] gmail [dot] com> wrote:

1. be able to flip switches and change parameter values (between runs)
without recompiling

2. keep my configured classes independent of any global configuration
object (excluding their own companion object) so they can be re-used
in another application without modification or recompilation

3. avoid variables

By using my own straightforward methods, I seem to be able to achieve
any two of these three objectives but not all three.

In what way does 2 fail? Or the other way, how do you achieve this when dropping 1 or 3 ?


I can use command-line arguments to set switches and parameter values without recompiling. Then I have two options:

1. I can pass those values to the other classes to configure them. But to do that, I need a variable in the receiving class.

2. I can save the command-line arguments (or the resulting config object) in the same object that holds main. But then the other classes need to access that object to read their configuration. This allows me to avoid variables, but then those classes depend on the "main" object. That's not quite what I want.

Of the two, I think option 1 is preferable.

--Russ P.

--
http://RussP.us

nicolas.oury@gm...
Joined: 2011-02-13,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Dependency Injection
On Fri, Sep 16, 2011 at 12:46 AM, John Nilsson <john [at] milsson [dot] nu> wrote:
On Fri, Sep 16, 2011 at 9:07 AM, Russ P. <russ [dot] paielli [at] gmail [dot] com> wrote:

1. be able to flip switches and change parameter values (between runs)
without recompiling

2. keep my configured classes independent of any global configuration
object (excluding their own companion object) so they can be re-used
in another application without modification or recompilation

3. avoid variables


Have you tried something like that:
import java.lang.ThreadLocal
class Parameter[A](initial:  A) {    val tl = new ThreadLocal[A] {        override def initialValue() : A = {            initial         }      }    def apply() = tl.get()    def withValue[B](a : A, p : => B){       val old = tl.get()       tl.set(a)        try{           p          }       finally{tl.set(old)}    }}
With a companion object:
object Parameter {  def getConfiguration[A](name : String)(implicit m : Manifest[A]) : Option[A] =  None      // should read the configuration here  def apply[A](name : String,  default : => A) : Parameter[A] =    new Parameter(getConfiguration(name).getOrElse(default))      def apply[A](name : String): Parameter[A] =        apply[A](name, error ("Configuration Error. Not found or not valid: " ++ name))}
It allows to separate the concern of reading config file.It also allow change locally a parameter, which can be useful.  And it prevents to modify it.
Best,
Nicolas.

Viktor Klang
Joined: 2008-12-17,
User offline. Last seen 1 year 27 weeks ago.
Re: Re: Dependency Injection


On Fri, Sep 16, 2011 at 11:55 AM, nicolas [dot] oury [at] gmail [dot] com <nicolas [dot] oury [at] gmail [dot] com> wrote:
On Fri, Sep 16, 2011 at 12:46 AM, John Nilsson <john [at] milsson [dot] nu> wrote:
On Fri, Sep 16, 2011 at 9:07 AM, Russ P. <russ [dot] paielli [at] gmail [dot] com> wrote:

1. be able to flip switches and change parameter values (between runs)
without recompiling

2. keep my configured classes independent of any global configuration
object (excluding their own companion object) so they can be re-used
in another application without modification or recompilation

3. avoid variables


Have you tried something like that:
import java.lang.ThreadLocal
class Parameter[A](initial:  A) {    val tl = new ThreadLocal[A] {        override def initialValue() : A = {            initial         }      }    def apply() = tl.get()    def withValue[B](a : A, p : => B){       val old = tl.get()       tl.set(a)        try{           p          }       finally{tl.set(old)}    }}

Not that I don't like inventing wheels, but what's wrong with: http://www.scala-lang.org/api/current/scala/util/DynamicVariable.html
 
With a companion object:
object Parameter {  def getConfiguration[A](name : String)(implicit m : Manifest[A]) : Option[A] =  None      // should read the configuration here  def apply[A](name : String,  default : => A) : Parameter[A] =    new Parameter(getConfiguration(name).getOrElse(default))       def apply[A](name : String): Parameter[A] =        apply[A](name, error ("Configuration Error. Not found or not valid: " ++ name))}
It allows to separate the concern of reading config file.It also allow change locally a parameter, which can be useful.  And it prevents to modify it.
Best,
Nicolas.




--
Viktor Klang

Akka Tech LeadTypesafe - Enterprise-Grade Scala from the Experts

Twitter: @viktorklang
Chris Marshall
Joined: 2009-06-17,
User offline. Last seen 44 weeks 3 days ago.
RE: Re: Dependency Injection
Russ - quite clearly your global-state is not "safe" in the same manner as Tony's example WiteWithContext

The other issue is that, with a global config singleton, you can have no way of knowing whether a given method needs any config access; because it is not expressed through the type system:
  def calculateCollisionProbability(ctx: ComputedWithContext[Context]) : Double
Quite clearly describes what this function needs access to in order to perform its task
Chris

> Date: Fri, 16 Sep 2011 00:07:14 -0700
> Subject: [scala-debate] Re: Dependency Injection
> From: russ [dot] paielli [at] gmail [dot] com
>
>
> I'm still struggling to understand exactly what this can do for me. I
> must be missing something, because the three objectives Tony listed
> above seem to me to be easy to achieve if you never need to change
> parameter values or if you are allowed to recompile. (I'm assuming
> that all parameters and switches are constant for each run but could
> change between runs). Tony didn't explicitly state those requirements,
> or did I miss them? He said something about the undesirability of
> recompiling, but did he say that the parameter values may need to be
> changed (between runs)?
>
> If the parameter values never need to change, then the problem is
> simple. All you need is a global config object (Tony's "Context"?)
> that everything has access to and depends on. As far as I can tell,
> that gets you what Tony claims to have achieved above: no clumsy
> passing of values, efficient, readable, and no variables.
>
> As I explained in my earlier reply to this message, I'm trying to set
> up a read-only conflguration for an application, where the switches
> and parameter values are all constant for each run. I would like to
>
> 1. be able to flip switches and change parameter values (between runs)
> without recompiling
>
> 2. keep my configured classes independent of any global configuration
> object (excluding their own companion object) so they can be re-used
> in another application without modification or recompilation
>
> 3. avoid variables
>
> By using my own straightforward methods, I seem to be able to achieve
> any two of these three objectives but not all three. Can Tony's method
> achieve all three? If so, then I'd say he has something significant.
> If not, then it seems to me that his approach is nothing more than an
> alternative (and more complicated) way to achieve something that can
> be done fairly easily. I'd like to know which it is.
>
> --Russ P.
>
> http://RussP.us
Razvan Cojocaru 3
Joined: 2010-07-28,
User offline. Last seen 42 years 45 weeks ago.
RE: Re: Dependency Injection

Here's a practical application of this approach versus the static object:
reloading configuration at run-time.

To make sure you have a consistent view of the configuration for the
duration of one process (thread) you need make sure that on a thread, only
one version of that configuration is used. This solution will pass that
specific version to all members.

You could use a ThreadLocal copy, or come up with your own context passed
around, but you're now on a sliperry slope...

What I would really like to see is a monad that can do this across actors,
eh? Assume the steps of processing are undertaken by individual actors not
simple functions that I can chain in a for, but actors chained by
messages... AND, when I see that, I'd like it distributed too :)

Thinking through this problem in depth you'll find that you've just began to
scratch the surface :)

-----Original Message-----
From: scala-debate [at] googlegroups [dot] com [mailto:scala-debate [at] googlegroups [dot] com]
On Behalf Of Russ P.
Sent: September-16-11 3:07 AM
To: scala-debate
Subject: [scala-debate] Re: Dependency Injection

On Sep 12, 4:53 am, Tony Morris wrote:

> You see, there is a way to pass these values through quite neatly and
> no, this is not using Scala's implicit keyword (which is
> insufficient), this is something else. OK, so let's first start by
> thinking about case
> 1) above where the application only has read access to some context. I
> will name this context, "Context", it is a data type that is
> somewhere-or-other that we would like to pass through our application

Tony Morris 2
Joined: 2009-03-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Dependency Injection

On 16/09/11 20:08, √iktor Ҡlang wrote:
>> Have you tried something like that:
>>
>> import java.lang.ThreadLocal
>>
>> class Parameter[A](initial: A) {
>> val tl = new ThreadLocal[A] {
>> override def initialValue() : A = {
>> initial
>> }
>> }
>> def apply() = tl.get()
>> def withValue[B](a : A, p : => B){
>> val old = tl.get()
>> tl.set(a)
>> try{
>> p
>> }
>> finally{tl.set(old)}
>> }
>> }
>>
>>
:(
> Not that I don't like inventing wheels, but what's wrong with:
> http://www.scala-lang.org/api/current/scala/util/DynamicVariable.html
:(

Was I really that unclear?

Viktor Klang
Joined: 2008-12-17,
User offline. Last seen 1 year 27 weeks ago.
Re: Re: Dependency Injection


2011/9/16 Tony Morris <tonymorris [at] gmail [dot] com>
On 16/09/11 20:08, √iktor Ҡlang wrote:
>> Have you tried something like that:
>>
>> import java.lang.ThreadLocal
>>
>> class Parameter[A](initial:  A) {
>>     val tl = new ThreadLocal[A] {
>>         override def initialValue() : A = {
>>             initial
>>         }
>>       }
>>     def apply() = tl.get()
>>     def withValue[B](a : A, p : => B){
>>        val old = tl.get()
>>        tl.set(a)
>>        try{
>>            p
>>          }
>>        finally{tl.set(old)}
>>     }
>> }
>>
>>
:(
> Not that I don't like inventing wheels, but what's wrong with:
> http://www.scala-lang.org/api/current/scala/util/DynamicVariable.html
:(

Was I really that unclear?

Was probably me that was unclear since it wasn't a reply to you...
 

--
Tony Morris
http://tmorris.net/





--
Viktor Klang

Akka Tech LeadTypesafe - Enterprise-Grade Scala from the Experts

Twitter: @viktorklang
Viktor Klang
Joined: 2008-12-17,
User offline. Last seen 1 year 27 weeks ago.
Re: Re: Dependency Injection


On Fri, Sep 16, 2011 at 2:36 PM, Razvan Cojocaru <pub [at] razie [dot] com> wrote:
Here's a practical application of this approach versus the static object:
reloading configuration at run-time.

To make sure you have a consistent view of the configuration for the
duration of one process (thread) you need make sure that on a thread, only
one version of that configuration is used. This solution will pass that
specific version to all members.

You could use a ThreadLocal copy, or come up with your own context passed
around, but you're now on a sliperry slope...

What I would really like to see is a monad that can do this across actors,
eh? Assume the steps of processing are undertaken by individual actors not
simple functions that I can chain in a for, but actors chained by
messages... AND, when I see that, I'd like it distributed too :)

Consensus and distributed means PITA
 

Thinking through this problem in depth you'll find that you've just began to
scratch the surface :)

-----Original Message-----
From: scala-debate [at] googlegroups [dot] com [mailto:scala-debate [at] googlegroups [dot] com]
On Behalf Of Russ P.
Sent: September-16-11 3:07 AM
To: scala-debate
Subject: [scala-debate] Re: Dependency Injection

On Sep 12, 4:53 am, Tony Morris <tonymor [dot] [dot] [dot] [at] gmail [dot] com> wrote:

> You see, there is a way to pass these values through quite neatly and
> no, this is not using Scala's implicit keyword (which is
> insufficient), this is something else. OK, so let's first start by
> thinking about case
> 1) above where the application only has read access to some context. I
> will name this context, "Context", it is a data type that is
> somewhere-or-other that we would like to pass through our application
> -- but no writes to it. I'm sure you can imagine what Context would
> really be -- feel free to make it up for the use-case.
>
> So, our values that were once mere values, are now computed as if they
> have access to a Context. We can denote this with a data type:
>
> case class ComputedWithContext[A](cx: Context => A)
>
> So we now have "first-class" values computed with a Context, rather
> than being mere values. We can now create these by accessing a Context
> "as if it were passed" -- that is to say, although we don't yet have a
> Context, we may create values that access that Context (when it is
> eventually
> passed) by wrapping a function (or a trait if you prefer).
>
> This is simple and straight-forward enough. But watch this:
>
> case class ComputedWithContext[A](cx: Context => A) {
>   def map[B](f: A => B): ComputedWithContext[B] =
> ComputedWithContext(f compose cx)
>   def flatMap[B](f: A => ComputedWithContext[B]):
> ComputedWithContext[B] = ComputedWithContext(c => f(cx(c)) cx c)
>
> }
>
> We see here that ComputedWithContext happens to have pretty handy map
> and flatMap methods. What can we do with them?
>
> OK, so suppose our program above is a little different to the original
> in that actually, our expressions (e1, e2 and e3) require a Context,
> so each of them becomes become ComputedWithContext[T] where previously
> they were just the type T (they may be all different values for T or
> same -- no matter).
>
> For example, e1 may have been an Int where now it is a
> ComputedWithContext[Int] and e2 may have been a String where now it is
> a ComputedWithContext[String]. You get the point.
>
> Here is how our program looks:
>
> for {
>   a <- e1
>   b <- e2(a)
>   c <- e3(a, b)
>   d <- e2(b)
>
> } yield d
>
> This is precisely the same program syntax. The type of this expression
> is ComputedWithContext[T] where the type T depends on the value d. In
> other words, we may pass a Context in to this value and it gets
> "threaded" through our program and our program *doesn't change* if we
> write it in this general form. We may "stack these layers" on top of
> what started as Id and our program remains unaltered. The "theory" of
> doing this is quite involved, mostly because it is kick-arse
> interesting and we could talk about it some time, but that's another
story!
>
> Importantly, there are no variables here. Not one and not a pretend
> value that is actually a variable at application time (which I'm sure
> you've been reminded of more than once when using DI).
>
> So, this is how we deal with passing read-only context through our
> application:
> * without being clumsy by explicitly passing it
> * being quite efficient and readable in fact!
> * without using variables that leads to program bugs and difficulty
> reading and debugging code


I'm still struggling to understand exactly what this can do for me. I must
be missing something, because the three objectives Tony listed above seem to
me to be easy to achieve if you never need to change parameter values or if
you are allowed to recompile. (I'm assuming that all parameters and switches
are constant for each run but could change between runs). Tony didn't
explicitly state those requirements, or did I miss them? He said something
about the undesirability of recompiling, but did he say that the parameter
values may need to be changed (between runs)?

If the parameter values never need to change, then the problem is simple.
All you need is a global config object (Tony's "Context"?) that everything
has access to and depends on. As far as I can tell, that gets you what Tony
claims to have achieved above: no clumsy passing of values, efficient,
readable, and no variables.

As I explained in my earlier reply to this message, I'm trying to set up a
read-only conflguration for an application, where the switches and parameter
values are all constant for each run. I would like to

1. be able to flip switches and change parameter values (between runs)
without recompiling

2. keep my configured classes independent of any global configuration object
(excluding their own companion object) so they can be re-used in another
application without modification or recompilation

3. avoid variables

By using my own straightforward methods, I seem to be able to achieve any
two of these three objectives but not all three. Can Tony's method achieve
all three? If so, then I'd say he has something significant.
If not, then it seems to me that his approach is nothing more than an
alternative (and more complicated) way to achieve something that can be done
fairly easily. I'd like to know which it is.

--Russ P.

http://RussP.us




--
Viktor Klang

Akka Tech LeadTypesafe - Enterprise-Grade Scala from the Experts

Twitter: @viktorklang
Razvan Cojocaru 3
Joined: 2010-07-28,
User offline. Last seen 42 years 45 weeks ago.
RE: Re: Dependency Injection

Usually that’s the case, but it’s a little bit simplified for this narrow purpose.

 

If the version number V of the configuration required for that logical thread accompanies the messages for that work unit, every node could retrieve the proper V for that part of the work unit, especially if they agree to keep the last few versions for a while and then you need this backed up by some common git as a fallback – it may get hairy to implement fully, but not that hard for a 99% success rate.

 

From: √iktor Ҡlang [mailto:viktor [dot] klang [at] gmail [dot] com]
Sent: September-16-11 8:50 AM
To: Razvan Cojocaru
Cc: Russ P.; scala-debate
Subject: Re: [scala-debate] Re: Dependency Injection

 

 

On Fri, Sep 16, 2011 at 2:36 PM, Razvan Cojocaru <pub [at] razie [dot] com> wrote:

Here's a practical application of this approach versus the static object:
reloading configuration at run-time.

To make sure you have a consistent view of the configuration for the
duration of one process (thread) you need make sure that on a thread, only
one version of that configuration is used. This solution will pass that
specific version to all members.

You could use a ThreadLocal copy, or come up with your own context passed
around, but you're now on a sliperry slope...

What I would really like to see is a monad that can do this across actors,
eh? Assume the steps of processing are undertaken by individual actors not
simple functions that I can chain in a for, but actors chained by
messages... AND, when I see that, I'd like it distributed too :)


Consensus and distributed means PITA
 


Thinking through this problem in depth you'll find that you've just began to
scratch the surface :)

-----Original Message-----
From: scala-debate [at] googlegroups [dot] com [mailto:scala-debate [at] googlegroups [dot] com]
On Behalf Of Russ P.
Sent: September-16-11 3:07 AM
To: scala-debate

Subject: [scala-debate] Re: Dependency Injection

On Sep 12, 4:53 am, Tony Morris <tonymor [dot] [dot] [dot] [at] gmail [dot] com> wrote:

> You see, there is a way to pass these values through quite neatly and
> no, this is not using Scala's implicit keyword (which is
> insufficient), this is something else. OK, so let's first start by
> thinking about case
> 1) above where the application only has read access to some context. I
> will name this context, "Context", it is a data type that is
> somewhere-or-other that we would like to pass through our application
> -- but no writes to it. I'm sure you can imagine what Context would
> really be -- feel free to make it up for the use-case.
>
> So, our values that were once mere values, are now computed as if they
> have access to a Context. We can denote this with a data type:
>
> case class ComputedWithContext[A](cx: Context => A)
>
> So we now have "first-class" values computed with a Context, rather
> than being mere values. We can now create these by accessing a Context
> "as if it were passed" -- that is to say, although we don't yet have a
> Context, we may create values that access that Context (when it is
> eventually
> passed) by wrapping a function (or a trait if you prefer).
>
> This is simple and straight-forward enough. But watch this:
>
> case class ComputedWithContext[A](cx: Context => A) {
>   def map[B](f: A => B): ComputedWithContext[B] =
> ComputedWithContext(f compose cx)
>   def flatMap[B](f: A => ComputedWithContext[B]):
> ComputedWithContext[B] = ComputedWithContext(c => f(cx(c)) cx c)
>
> }
>
> We see here that ComputedWithContext happens to have pretty handy map
> and flatMap methods. What can we do with them?
>
> OK, so suppose our program above is a little different to the original
> in that actually, our expressions (e1, e2 and e3) require a Context,
> so each of them becomes become ComputedWithContext[T] where previously
> they were just the type T (they may be all different values for T or
> same -- no matter).
>
> For example, e1 may have been an Int where now it is a
> ComputedWithContext[Int] and e2 may have been a String where now it is
> a ComputedWithContext[String]. You get the point.
>
> Here is how our program looks:
>
> for {
>   a <- e1
>   b <- e2(a)
>   c <- e3(a, b)
>   d <- e2(b)
>
> } yield d
>
> This is precisely the same program syntax. The type of this expression
> is ComputedWithContext[T] where the type T depends on the value d. In
> other words, we may pass a Context in to this value and it gets
> "threaded" through our program and our program *doesn't change* if we
> write it in this general form. We may "stack these layers" on top of
> what started as Id and our program remains unaltered. The "theory" of
> doing this is quite involved, mostly because it is kick-arse
> interesting and we could talk about it some time, but that's another
story!
>
> Importantly, there are no variables here. Not one and not a pretend
> value that is actually a variable at application time (which I'm sure
> you've been reminded of more than once when using DI).
>
> So, this is how we deal with passing read-only context through our
> application:
> * without being clumsy by explicitly passing it
> * being quite efficient and readable in fact!
> * without using variables that leads to program bugs and difficulty
> reading and debugging code


I'm still struggling to understand exactly what this can do for me. I must
be missing something, because the three objectives Tony listed above seem to
me to be easy to achieve if you never need to change parameter values or if
you are allowed to recompile. (I'm assuming that all parameters and switches
are constant for each run but could change between runs). Tony didn't
explicitly state those requirements, or did I miss them? He said something
about the undesirability of recompiling, but did he say that the parameter
values may need to be changed (between runs)?

If the parameter values never need to change, then the problem is simple.
All you need is a global config object (Tony's "Context"?) that everything
has access to and depends on. As far as I can tell, that gets you what Tony
claims to have achieved above: no clumsy passing of values, efficient,
readable, and no variables.

As I explained in my earlier reply to this message, I'm trying to set up a
read-only conflguration for an application, where the switches and parameter
values are all constant for each run. I would like to

1. be able to flip switches and change parameter values (between runs)
without recompiling

2. keep my configured classes independent of any global configuration object
(excluding their own companion object) so they can be re-used in another
application without modification or recompilation

3. avoid variables

By using my own straightforward methods, I seem to be able to achieve any
two of these three objectives but not all three. Can Tony's method achieve
all three? If so, then I'd say he has something significant.
If not, then it seems to me that his approach is nothing more than an
alternative (and more complicated) way to achieve something that can be done
fairly easily. I'd like to know which it is.

--Russ P.

http://RussP.us




--
Viktor Klang

Akka Tech Lead

Typesafe - Enterprise-Grade Scala from the Experts

Twitter: @viktorklang

 

Nikolay Artamonov
Joined: 2010-09-08,
User offline. Last seen 42 years 45 weeks ago.
Re: Dependency Injection

Thanks, Andreas!

On Sep 13, 12:02 am, Andreas Scheinert
wrote:
> Hi Nikolay!
> That would be the ReaderMonad see here :http://permalink.gmane.org/gmane.comp.lang.scala.user/36914
> And see scalaz examples for more :)

Alex Cruise
Joined: 2008-12-17,
User offline. Last seen 2 years 26 weeks ago.
Re: Re: Dependency Injection
On Fri, Sep 16, 2011 at 12:07 AM, Russ P. <russ [dot] paielli [at] gmail [dot] com> wrote:
As I explained in my earlier reply to this message, I'm trying to set up a read-only conflguration for an application, where the switches
and parameter values are all constant for each run. I would like to

1. be able to flip switches and change parameter values (between runs)
without recompiling

2. keep my configured classes independent of any global configuration
object (excluding their own companion object) so they can be re-used
in another application without modification or recompilation

3. avoid variables

By using my own straightforward methods, I seem to be able to achieve
any two of these three objectives but not all three. Can Tony's method
achieve all three? If so, then I'd say he has something significant.
If not, then it seems to me that his approach is nothing more than an
alternative (and more complicated) way to achieve something that can
be done fairly easily. I'd like to know which it is.

How about this?  Only a sketch, haven't attempted to compile; margin too narrow etc.
abstract class ConfigurationItem[T](val value: T)
case class SwallowLading(unladen: Boolean) extends ConfigurationItem[Boolean](unladen)case class SwallowOrigin(african: Boolean) extends ConfigurationItem[Boolean](african)
trait Configuration extends Iterable[ConfigurationItem[_]] { // Iterable if you care, or don't bother  implicit def swallowLading: SwallowLading  implicit def swallowOrigin: SwallowOrigin
  def iterator = List(swallowLading, swallowOrigin).iterator // Not very DRY, it'd sure be nice to do declarations from sequence pattern matches the way we can from tuple pattern matches! }
final class ServiceThatCaresAboutSwallows(implicit lading: SwallowLading, implicit origin: SwallowOrigin) {  val unladen: Boolean = lading.value  val african: Boolean = origin.value
  def doStuff = error("TODO")}
object TheSystem {  def config: Configuration = error("TODO")
  import config._
  val service = new ServiceThatCaresAboutSwallows() // Get the implicit values from the configuration, without coupling to the configuration}
-0xe1a
Russ P.
Joined: 2009-01-31,
User offline. Last seen 1 year 26 weeks ago.
Re: Re: Dependency Injection
On Fri, Sep 16, 2011 at 2:42 AM, John Nilsson <john [at] milsson [dot] nu> wrote:
I don't think I follow completely. You want to reference some configuration, but you don't want a dependency on it? The mere fact that you are referencing it is a dependency no matter what technique you use to resolve it.

The idea is that I want the configured classes to be re-usable in another application without any changes or recompilation. So the configured classes should have their own default configuration that can be overridden by main if desired. To reconfigure them for a particular application or run, main should "push" the new configuration to them. They should not "pull" the configuration from main because that makes them dependent on one particular main and therefore degrades re-usability. But main cannot pushes the configuration to them unless their configuration is a variable. This is the dilemma.
 

In any case, if you want a more "hidden" reference you can maybe create factories that does "new MyConfiguredClass with TheConfiguration" for you.

Or you can move the dependency from the classes as such and move it to the operation on the class. def aConfigurableOperation(i:Input, cfg: Config)
or even
def aConfigurableOperation(i:Input)(implicit cfg:Config)
or a variation on that theme
implicit def input2configuredOps(i:Input):ConfiguredOpsOn[Input]


Thanks for the suggestion, but I'm not sure that answers the question of how to change the configuration (between runs) without recompiling.

--Russ
 

--
http://RussP.us
Russ P.
Joined: 2009-01-31,
User offline. Last seen 1 year 26 weeks ago.
Re: Re: Dependency Injection
On Fri, Sep 16, 2011 at 3:05 AM, Chris Marshall <oxbow_lakes [at] hotmail [dot] com> wrote:
Russ - quite clearly your global-state is not "safe" in the same manner as Tony's example WiteWithContext


Unfortunately, it's not clear to me.

 
The other issue is that, with a global config singleton, you can have no way of knowing whether a given method needs any config access; because it is not expressed through the type system:
  def calculateCollisionProbability(ctx: ComputedWithContext[Context]) : Double
Quite clearly describes what this function needs access to in order to perform its task

I fail to grasp how that is fundamentally any different than

import Config._

or

val result = f(a, b, Config.someParameter)

where Config is a just a "global" immutable object. Either way, it seems to me that you have a dependence on something that is external to the classes that you are trying to configure. What am I missing?

--Russ P.

--
http://RussP.us
marius
Joined: 2008-08-31,
User offline. Last seen 3 years 19 weeks ago.
Re: Dependency Injection
Neat !

On Thu, Sep 15, 2011 at 8:24 AM, Tony Morris <tonymorris [at] gmail [dot] com> wrote:
On 09/13/2011 11:05 PM, Marius Danciu wrote:
> That's how the state monad is born :) .. and the state monad is a beautiful example of DI done right !

*high-five*

To give you a taste of how I would progress the argument if required, I'd use ReaderWriterStateT. You might call this "DI done right", but I prefer to just call it "solving problems, no really, for real" or some such.


trait Functor[F[_]] // todo
trait Monad[F[_]] // todo

trait ReaderWriterStateT[R, W, S, F[_], A] {
  val runT: R => S => F[(A, S, W)]

  def map[B](f: A => B)(implicit z: Functor[F]): ReaderWriterStateT[R, W, S, F, B] =
    error("todo")

  def flatMap[B](f: A => ReaderWriterStateT[R, W, S, F, B])(implicit z: Monad[F]): ReaderWriterStateT[R, W, S, F, B] =
    error("todo")
}

case class Id[A](a: A)

object ReaderWriterStateT {
  type ReaderWriterState[R, W, S, A] =
    ReaderWriterStateT[R, W, S, Id, A]
}



--
Tony Morris
http://tmorris.net/



Ross Rose
Joined: 2011-09-10,
User offline. Last seen 7 hours 26 min ago.
Re: Dependency Injection
"So hopefully now you see that DI can be replaced by a superior
programming model, at least for this example, and I promise, for any
example."
You are totally correct, no need to discuss this any further. I have taken the ConfigReader example in your presentation and separated the Configuration case class out into its own file (.class), added a Configuration object to it, and extended that object with a trait that contains business rules which the case class uses to populate itself with. Here is the Main.scala file:

abstract class ConfigReader[A] {

  def apply(config: Configuration): A

  def map[B](f: A => B): ConfigReader[B] =

    new ConfigReader[B] {

      def apply(c: Configuration) =

        f(ConfigReader.this.apply(c))

    }

  def flatMap[B](f: A => ConfigReader[B]): ConfigReader[B] =

    new ConfigReader[B] {

      def apply(c: Configuration) =

        f(ConfigReader.this.apply(c))(c)

    }

}


object Main {


    def lift4ConfigReader[A, B, C, D, E](f: A => B => C => D => E):

        ConfigReader[A] =>

        ConfigReader[B] =>

        ConfigReader[C] =>

        ConfigReader[D] =>

        ConfigReader[E] =

        a => b => c => d=>

        for{

            aa <- a

            bb <- b

            cc <- c

            dd <- d

        } yield f(aa)(bb)(cc)(dd)


        def main(args: Array[String]) {


          // utility construction

          def configReader[A](k: Configuration => A): ConfigReader[A] =

            new ConfigReader[A] {

              def apply(c: Configuration) = k(c)

          }

          val hostname = configReader(_.hostname)

          val port = configReader(_.port)

          val outfile = configReader(_.outfile)

          val fullpath = configReader(_.fullPath)

          val r = for {

              h <- hostname

              p <- port

              o <- outfile

              f <- fullpath

            } yield "Hello there " + h + ":" + p +

                    "! Want to write to " + o + "?" + " Full path is: " + f

          val conf =

            Configuration()

          println(r(conf))

        }

}


Here is the Configuration.scala file:


trait BusinessRules {

def strToInt(s: String): Int = {

val i: Int = Integer.parseInt(s.toString())

i

}

def intToString(i: Int): String = {

val s: String = "" + i

s

}

}


object Configuration extends BusinessRules {

def getHostname: String = {

"localhost"

}

def port: Int = 80

def getOutfile: String = {

"/etc/hosts"

}


override def toString: String = {

"ftp://" + getHostname + ":" + port + getOutfile

}

}


case class Configuration(

        hostname: String = Configuration.getHostname,

        port: String = Configuration.intToString(Configuration.port),

        outfile: String = Configuration.getOutfile,

        fullPath: String = Configuration.toString()

        )


Russ P.
Joined: 2009-01-31,
User offline. Last seen 1 year 26 weeks ago.
Re: Dependency Injection
Does that allow you to change configuration parameter values between runs without recompiling?

If not, then what does all that boilerplate buy for you that you can't get with

object Configuration {


    val Hostname: String = "localhost"

val port: Int = 80

  val getOutfile: String = "/etc/hosts"

    }


Sorry if that's a dumb question.

--Russ P.


On Sun, Sep 18, 2011 at 6:08 AM, Billy <rossrose69 [at] gmail [dot] com> wrote:
"So hopefully now you see that DI can be replaced by a superior
programming model, at least for this example, and I promise, for any
example."
You are totally correct, no need to discuss this any further. I have taken the ConfigReader example in your presentation and separated the Configuration case class out into its own file (.class), added a Configuration object to it, and extended that object with a trait that contains business rules which the case class uses to populate itself with. Here is the Main.scala file:

abstract class ConfigReader[A] {

  def apply(config: Configuration): A

  def map[B](f: A => B): ConfigReader[B] =

    new ConfigReader[B] {

      def apply(c: Configuration) =

        f(ConfigReader.this.apply(c))

    }

  def flatMap[B](f: A => ConfigReader[B]): ConfigReader[B] =

    new ConfigReader[B] {

      def apply(c: Configuration) =

        f(ConfigReader.this.apply(c))(c)

    }

}


object Main {


    def lift4ConfigReader[A, B, C, D, E](f: A => B => C => D => E):

        ConfigReader[A] =>

        ConfigReader[B] =>

        ConfigReader[C] =>

        ConfigReader[D] =>

        ConfigReader[E] =

        a => b => c => d=>

        for{

            aa <- a

            bb <- b

            cc <- c

            dd <- d

        } yield f(aa)(bb)(cc)(dd)


        def main(args: Array[String]) {


          // utility construction

          def configReader[A](k: Configuration => A): ConfigReader[A] =

            new ConfigReader[A] {

              def apply(c: Configuration) = k(c)

          }

          val hostname = configReader(_.hostname)

          val port = configReader(_.port)

          val outfile = configReader(_.outfile)

          val fullpath = configReader(_.fullPath)

          val r = for {

              h <- hostname

              p <- port

              o <- outfile

              f <- fullpath

            } yield "Hello there " + h + ":" + p +

                    "! Want to write to " + o + "?" + " Full path is: " + f

          val conf =

            Configuration()

          println(r(conf))

        }

}


Here is the Configuration.scala file:


trait BusinessRules {

def strToInt(s: String): Int = {

val i: Int = Integer.parseInt(s.toString())

i

}

def intToString(i: Int): String = {

val s: String = "" + i

s

}

}


object Configuration extends BusinessRules {

def getHostname: String = {

"localhost"

}

def port: Int = 80

def getOutfile: String = {

"/etc/hosts"

}


override def toString: String = {

"ftp://" + getHostname + ":" + port + getOutfile

}

}


case class Configuration(

        hostname: String = Configuration.getHostname,

        port: String = Configuration.intToString(Configuration.port),

        outfile: String = Configuration.getOutfile,

        fullPath: String = Configuration.toString()

        )





--
http://RussP.us
Ross Rose
Joined: 2011-09-10,
User offline. Last seen 7 hours 26 min ago.
Re: Dependency Injection

On Sep 18, 2011, at 10:00, Russ Paielli <russ [dot] paielli [at] gmail [dot] com> wrote:

Does that allow you to change configuration parameter values between runs without recompiling?

If not, then what does all that boilerplate buy for you that you can't get with

object Configuration {


    val Hostname: String = "localhost"

val port: Int = 80

  val getOutfile: String = "/etc/hosts"

    }


Sorry if that's a dumb question.

--Russ P.


--
http://RussP.us

It allows me to place the configuration into a .class file external from the functionality of the rest of the solution. The business rules are free to do whatever they need to in order to apply logic as needed. That notion includes potentially reading settings from a file I suppose (which said file could be updated between executions as you mention). My use case is different and would not need that ability (at least at the present), but the functionality should be completely possible. In fact, what the business rules do is completely open ended. The case class becomes the contract ("interface" if you will), and as long as it is adhered to the sky is the limit.
Russ P.
Joined: 2009-01-31,
User offline. Last seen 1 year 26 weeks ago.
Re: Dependency Injection
On Sun, Sep 18, 2011 at 8:52 AM, Ross Rose <rossrose69 [at] gmail [dot] com> wrote:

On Sep 18, 2011, at 10:00, Russ Paielli <russ [dot] paielli [at] gmail [dot] com> wrote:

Does that allow you to change configuration parameter values between runs without recompiling?

If not, then what does all that boilerplate buy for you that you can't get with

object Configuration {


    val Hostname: String = "localhost"

val port: Int = 80

  val getOutfile: String = "/etc/hosts"

    }


Sorry if that's a dumb question.

--Russ P.


--
http://RussP.us

It allows me to place the configuration into a .class file external from the functionality of the rest of the solution.

I can do that with my simple Configuration object too.
 
The business rules are free to do whatever they need to in order to apply logic as needed. That notion includes potentially reading settings from a file I suppose (which said file could be updated between executions as you mention). My use case is different and would not need that ability (at least at the present), but the functionality should be completely possible. In fact, what the business rules do is completely open ended. The case class becomes the contract ("interface" if you will), and as long as it is adhered to the sky is the limit.


I'm still wondering what all that boilerplate gets you.

--Russ P.

--
http://RussP.us
Alec Zorab
Joined: 2010-05-18,
User offline. Last seen 42 years 45 weeks ago.
Re: Dependency Injection

Ah, but with a touch of magic, we can elide the boilerplate!
Consider that the configreader class appears to be something that
takes a Configuration and gives a T...we might even call that a
Configuration => T !
Thus:

def main(args: Array[String]) {
//if only Function1 had a flatMap method...
import scalaz._
import Scalaz._
//and now it does!

val hostname = (_: Configuration).hostname
val port = (_: Configuration).port
val outfile = (_: Configuration).outfile
val fullpath = (_: Configuration).fullPath

val r: Configuration => String = for {
h <- hostname
p <- port
o <- outfile
f <- fullpath
} yield "Hello there " + h + ":" + p + "! Want to write to " + o +
"?" + " Full path is: " + f

val conf = Configuration()

println(r(conf))
}

On Sun, Sep 18, 2011 at 8:04 PM, Russ Paielli wrote:
> On Sun, Sep 18, 2011 at 8:52 AM, Ross Rose wrote:
>>
>> On Sep 18, 2011, at 10:00, Russ Paielli wrote:
>>
>> Does that allow you to change configuration parameter values between runs
>> without recompiling?
>>
>> If not, then what does all that boilerplate buy for you that you can't get
>> with
>>
>> object Configuration {
>>
>>     val Hostname: String = "localhost"
>>
>> val port: Int = 80
>>
>>   val getOutfile: String = "/etc/hosts"
>>
>>     }
>>
>> Sorry if that's a dumb question.
>>
>> --Russ P.
>>
>>
>> --
>> http://RussP.us
>>
>> It allows me to place the configuration into a .class file external from
>> the functionality of the rest of the solution.
>
> I can do that with my simple Configuration object too.
>
>>
>> The business rules are free to do whatever they need to in order to apply
>> logic as needed. That notion includes potentially reading settings from a
>> file I suppose (which said file could be updated between executions as you
>> mention). My use case is different and would not need that ability (at least
>> at the present), but the functionality should be completely possible. In
>> fact, what the business rules do is completely open ended. The case class
>> becomes the contract ("interface" if you will), and as long as it is adhered
>> to the sky is the limit.
>
> I'm still wondering what all that boilerplate gets you.
>
> --Russ P.
>
> --
> http://RussP.us
>

Chris Twiner
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
Re: Dependency Injection

Absolutely nothing in that particular case. However, using a reader monad would allow you to separate the source of the configuration from its use. I.e. test vs prod etc. That's possible as your configuration receiving code is separated via the reading functions.

The difference to DI is of course the immutable nature of it.  Also it could mimic scopes etc but still behave in a similar fashion (the for loop managing "scope").

That said, I am not so convinced of its practicablity - there sure is a lot of boiler plate. Accessing a singletons val is far simpler for most apps. DI imo is far worse at infecting your code.

Tony gave a presentation that covered exactly this. Whilst his emails can be difficult to process that presentation was wonderfuly easy to follow. No idea on the link right now though, but an email sent to the lists also gave the resulting code.

On Sep 18, 2011 9:04 PM, "Russ Paielli" <russ [dot] paielli [at] gmail [dot] com> wrote:
> On Sun, Sep 18, 2011 at 8:52 AM, Ross Rose <rossrose69 [at] gmail [dot] com> wrote:
>
>>
>> On Sep 18, 2011, at 10:00, Russ Paielli <russ [dot] paielli [at] gmail [dot] com> wrote:
>>
>> Does that allow you to change configuration parameter values between runs
>> without recompiling?
>>
>> If not, then what does all that boilerplate buy for you that you can't get
>> with
>>
>> object Configuration {
>>
>>
>> val Hostname: String = "localhost"
>>
>> val port: Int = 80
>>
>> val getOutfile: String = "/etc/hosts"
>>
>> }
>>
>> Sorry if that's a dumb question.
>>
>> --Russ P.
>>
>>
>> --
>> <http://RussP.us>http://RussP.us
>>
>>
>> It allows me to place the configuration into a .class file external from
>> the functionality of the rest of the solution.
>>
>
> I can do that with my simple Configuration object too.
>
>
>> The business rules are free to do whatever they need to in order to apply
>> logic as needed. That notion includes potentially reading settings from a
>> file I suppose (which said file could be updated between executions as you
>> mention). My use case is different and would not need that ability (at least
>> at the present), but the functionality should be completely possible. In
>> fact, what the business rules do is completely open ended. The case class
>> becomes the contract ("interface" if you will), and as long as it is adhered
>> to the sky is the limit.
>>
>
>
> I'm still wondering what all that boilerplate gets you.
>
> --Russ P.
>
> --
> http://RussP.us
Ross Rose
Joined: 2011-09-10,
User offline. Last seen 7 hours 26 min ago.
Re: Dependency Injection
Sorry for the long winded first answer...
1) the code presented is by no means optimized (nor optimal for most solutions), it is meant to show some of the depth one could take it to. A domain SME could work on the business rules in a trait separately, which could have also resided in its own file. An individual (or individuals) building the solution's framework common to all domains in the enterprise could maintain the case class and object as part of the framework. The object extends the trait to bring it into scope for the case class. The case class becomes the contract with the ConfigReader for the business rules. As long as the case class remains consistent, nothing else need change in the code being configured. The trait is also constrained to provide the functionality that the case class is needing, but how that is done is completely isolated. The object also offers the framework implementers the liberty to "massage" the functionality provided by the SME in the trait. What this shows is one way to achieve separation of concerns without DI and all the headache as Tony tried to enlighten me to (yet took a while). Note: in my use case, "configured" not only means bringing in strings and port numbers and such, it also means how the domain data is manipulated and other functionality, i.e. it could pull in functions proper. The core of the solution (the "framework") is common to all domains.
A real world example is in order to express the requirement for such a thing: think of something like tax laws across the globe. Each area/region/country has its own specifics about how tax matters are computed. The framework should not care about this, it only needs the results from the tax rules to perform its work, but in a consistent manor. Thus, the framework can work globally for all domains when the tax rules are put into different traits specific to each domain. When the framework is deployed into a new domain, that domain's specific trait (and potentially other objects which the trait may use) is deployed with it to tailor the framework to that domain. This results in a single release of the solution globally while domain specific rules are maintained separately by the SME(s) making it manageable. Should the tax rules change for a domain, only its trait would need updated and deployed.
2) the code I used for this was from Tony's (awesome) presentation. It's located on Vimeo, don't have the link handy. I believe you can get to it from his blog.

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland