Okay, let’s go over the basic DSL argument:
- You need to configure something
- It can behave in very different ways in different environments
- The data model is subject to change and in any event not easily represented by a relational database
These are pretty much exactly the use cases for Composite and Chain of Responsibility patterns. But that’s one of those “raises as many questions as it answers” things. How are you going to configure it? Well, until the advent of embeddable scripting languages, the answer was pretty much always you hacked something together. That, or you hacked together a configuration language together, often using XML.
Embeddable scripting languages change this equation. Now you can do anything you could do in code. If you’re lucky, you can make it look DSL-y. Now, you could write a book on this. Oren already has, but I’m not talking about all-conquering DSLs that solve a domain problem, I’m taking about hacking something together. So, what are you going to need for that?
- Embed Python in your program
- Inject some variables into the python script
- Allow the Python script to call back to the original program
With this you can do anything. You can even read an XML configuration file, if you must.
Hosting IronPython
I’m aware I’m something like the 100th person to blog this, but most of the information is horribly out of date or misses out an important detail. So, here’s some really trivial code that runs a (hardwired) python file.
static void Main(string[] rawArgs) { var engine = Python.CreateEngine(); // Set the search path to include the calling program, so that you can import it with the python code engine.SetSearchPaths(new[] { Path.GetDirectoryName(Application.ExecutablePath) }.ToList()); var scriptScope = new ScriptScope(engine, new Scope()); // Inject in a variable engine.SetVariable(scriptScope, "c", new CurveDownload()); engine.ExecuteFile("curve.py", scriptScope); }
Note that we’ve set the search path, to allow the python file to import the original assembly. You might not need that, but in general you will. Equally, we’ve slapped a variable in there to demonstrate the ability to pass in context. We could make all of this a lot prettier, but we’re going for braindead here.
Okay, next you need to actually reference back to the calling program
import clr clr.AddReferenceByPartialName("Bloomberg.YieldCurves") from Bloomberg.YieldCurves import * curves = [ ShortLongCurveSpecification("USD", "CMTUSD__", "A_A", "US000__", "A_360"), ShortLongCurveSpecification("GBP", "COMGBP__", "A_A", "BP000__", "A_365"), ShortLongCurveSpecification("EUR", "CMTEUR__", "A_A", "EE000__", "A_360") ] c.Export("""c:tryme.xml""", curves)
So, the first three lines do that. I’d like to get that whole bit slicker, preferably so that you could move it into the C# code, but for now it’ll do (obviously you could just hack it by modifying the file). For those of you who know practically no Python, like me, the difference between “import Bloomberg.YieldCurves” and “from Bloomberg.YieldCurves import *” is that in the former case you’d have to refer to Bloomberg.YieldCurves.ShortLongCurveSpecification.