Local-global variables, partial classes, cake pattern?

661 views
Skip to first unread message

Haoyi Li

unread,
May 16, 2012, 12:06:28 AM5/16/12
to scala-user
This is going to be a somewhat long post. Essentially, I've been trying out a new way of structuring my code, and was wondering if something like this has been done before, and what people think of it.

The problem I'm trying to solve is that of *local globals*. These are variables that you want to access from a whole bunch of different places in the code. They kind of belong to an environment your objects live in, but you do not want to make the global because globals are bad: doing so would preclude having more than one environment, and generally make everything (testing, etc.) difficult.

An example of a *local global* would be the language the application is using. Another example would be the acceleration due to gravity in a video game. Another, common in web frameworks, is the HttpRequest: almost everyone wants access to it for various things: picking the correct language, or getting the currently logged in User. Using the HttpRequest as an example I've seen this solved in a variety of ways:

- Pass it around everywhere. This is basically what Play! 2.0 does, as far as I could tell from:


This has the disadvantage of needing to thread the Request all the way through the call stack to the functions who needs it. Sometimes the function which needs the Request is pretty deep in the call stack, and threading the Request through all the function calls just to reach him at the bottom can be rather tedious. Implicits help reduce the pain (no longer need to pass it *in* to functions) but doesn't remove it (still need to declare it in function headers). 

- Fake a global. This is what Flask (the python web framework) does:


Essentially, before they run your code, they manually set the global variables to the values you want so you can access them throughout your code. After you're code is run, it is set back to whatever it was before. This clearly works (lots of people use Flask!) but it seems really magic and brittle: having to actively use mutable (!) global (!!) state to avoid having to thread variables through the call stack seems pretty extreme.

The solution I've come up with mirrors how objects contain methods: you often do not need to thread variables through all an objects internal method calls, because these variables are often put into the class scope. They are then accessible from any method within the class without needing to be passed in, but do not preclude having more than one instance of that class. 

Essentially, I place all the classes whom i want to have access to this environment in a single big class. Something like:

class Environment{
  val localglobal = ...

  class A(...){
    ...
  }
  class B(...){
    ...
  }
  class C(...){
    ...
  }
  class D(...){
    ...
  }
}

Doing this, when i create an Environment, all classes and methods within it have access to localglobal directly. Unlike simply making it global/static (or doing the global-mutable-magic that Flask does), it does not preclude creating multiple environments and running them at the same time, e.g. for unit testing. 

It still makes unit testing individual classes *inside* the Environment difficult (have to set up entire environment before doing stuff with classes), 
just as object-oriented programming makes unit testing individual methods difficult (have to create and initialize object before doing stuff with methods), 
but it seems a reasonable compromise short of using global variables (have to set up *everything* before doing stuff!). 

It seems like a perfect way of introducing controlled-globalness into the program, giving you some of the benefits (no need to thread variables through functions) and some of the costs (functions are less pure) in a controlled fashion/scope (within the Environment).

However, this only works so simply if everything fits into one file, which is unlikely for programs with many interacting A, B, Cs and Ds (e.g. those complex enough with deep enough call stacks that the benefit in not-having-to-thread-variables-through-methods is worthwhile). If I had C# style partial classes, I could just split it up:

class Environment{
  val localglobal = ...
}

// new file
partial class Environment{
  class A(...){
    ...
  }
  class B(...){
    ...
  }
}

//new file
partial class Environment{
  class C(...){
    ...
  }
  class D(...){
    ...
  }
}

but alas Scala doesn't have partial classes. However, I managed to get something similar using traits:

class Environment 
  extends File1
  with File2{

  val localglobal = ...
}

//new file
trait File1{ env: Environment =>
  class A(...){
    ...
  }
  class B(...){
    ...
  }
}

//new file
trait File2{ env: Environment =>
  class C(...){
    ...
  }
  class D(...){
    ...
  }
}

This is basically identical to the partial class technique using traits, except having to put the env:Environment=> thing on each trait as well as "registering" each trait with the Environment by making the Environment extend it. It seems like I finally have it! A set of "global" variables that are visible to all my classes, don't need to be passed around, but only within a certain scope (the Environment).

This turned out much longer than I thought it would. My questions are:

- Has this been done before? It's a problem that has been bugging me for a long time, ever since I made my first 3D game 8 years ago and first found myself needing to pass accelerationDueToGravity around (back then I copped out and just made it global), and it seems like I have found a solution. If I had this problem, other people must have had it too; the Play! and Flask people certainly seem to have bumped into it

- Is there anything I should watch out for, doing these sort of stunts? It seems really elegant (the environment-class relationship mirrors the whole object-method thing from OO programming) but there are probably some hidden pitfalls I don't see. 

- Does this have anything to do with the Cake pattern? I have heard about it a lot, have gone through every link on the front page results from Googling "scala cake pattern", and I still haven't figured it out. I've never done any DI before, which probably is part of the reason I have failed to understand it, but it seems to have a lot of the class-within-trait trait-extended-by-container thing that I am doing here.

Thanks!
-Haoyi

Vlad Patryshev

unread,
May 16, 2012, 3:33:46 AM5/16/12
to Haoyi Li, scala-user
I wholeheartedly support this idea. There are rites and habits that kind of prevent systematically doing things this way, but I believe this is the right way.

E.g. define a class "chess figure", or, rather, say, "pawn". It has no meaning outside its universe, which is the chess game. Etc.

Thanks,
-Vlad

Haoyi Li

unread,
May 16, 2012, 10:24:57 AM5/16/12
to Vlad Patryshev, scala-user
The way I see it, it seems like a natural extension to the hierarchy of scopes that everyone's already used to:

- closures can access anything in the methods they are defined
- methods can access anything in the object they are defined
- objects can access anything in the environment they are defined <- new
- environments can access anything in the global scope

It's by no means *neccesary*, but neither are closures (you can define the closure as a method and pass the relevant variables in, Java style) or objects (you can define the methods as standalone functions and pass the object-data in as a struct F# style), or global scope.

In fact, you can put stuff like the Database endpoint, external services, etc. as Environment variables when you create the environment, allowing everyone inside easy-access-without-passing-stuff-around still but allowing you to create different configurations for whatever reason (e.g. replacing them with mock services for testing), or having multiple environments running in the same process with different configuration.

This all seems very familiar to something I've read before. Have I just re-invented the Cake Pattern for dependency injection?

-Haoyi

√iktor Ҡlang

unread,
May 16, 2012, 10:33:38 AM5/16/12
to Haoyi Li, Vlad Patryshev, scala-user
On Wed, May 16, 2012 at 4:24 PM, Haoyi Li <haoy...@gmail.com> wrote:
The way I see it, it seems like a natural extension to the hierarchy of scopes that everyone's already used to:

- closures can access anything in the methods they are defined
- methods can access anything in the object they are defined
- objects can access anything in the environment they are defined <- new
- environments can access anything in the global scope

It's by no means *neccesary*, but neither are closures (you can define the closure as a method and pass the relevant variables in, Java style) or objects (you can define the methods as standalone functions and pass the object-data in as a struct F# style), or global scope.

In fact, you can put stuff like the Database endpoint, external services, etc. as Environment variables when you create the environment, allowing everyone inside easy-access-without-passing-stuff-around still but allowing you to create different configurations for whatever reason (e.g. replacing them with mock services for testing), or having multiple environments running in the same process with different configuration.

This all seems very familiar to something I've read before. Have I just re-invented the Cake Pattern for dependency injection?




--
Viktor Klang

Akka Tech Lead
Typesafe - The software stack for applications that scale

Twitter: @viktorklang

Vlad Patryshev

unread,
May 16, 2012, 10:33:46 AM5/16/12
to Haoyi Li, scala-user
I believe Cake is different; in Cake we have an abstract dependency that can be filled this or that way; here we have embedded contexts where an external context cannot be replaced so we kind of lose abstraction. In practice, I think, it makes sense to combine these two.

Thanks,
-Vlad

Haoyi Li

unread,
May 16, 2012, 9:18:09 PM5/16/12
to Vlad Patryshev, scala-user
I went through the video; after that, and more reading, it seems that I have indeed rediscovered the Cake pattern, just in a slightly less generic form. If i replace the concrete members of the embedded contexts with abstract members, there we have it, almost precisely the thing i see on the various blogs that come up for googling "scala cake pattern"!

Who knew it would take re-inventing the wheel to finally understand it. 

Miles Sabin

unread,
May 17, 2012, 4:30:23 AM5/17/12
to Haoyi Li, Vlad Patryshev, scala-user
On Thu, May 17, 2012 at 2:18 AM, Haoyi Li <haoy...@gmail.com> wrote:
> Who knew it would take re-inventing the wheel to finally understand it.

I find that's usually the best way ;-)

Cheers,


Miles

--
Miles Sabin
tel: +44 7813 944 528
gtalk: mi...@milessabin.com
skype: milessabin
g+: http://www.milessabin.com
http://twitter.com/milessabin
http://underscoreconsulting.com
http://www.chuusai.com
Reply all
Reply to author
Forward
0 new messages