So I decided to switch to instead hardcoding the configuration into the code itself. For a one-off program, this seems entirely reasonable; moreoever, it's increasingly idiomatic Scala. (Scala's native build system essentially uses a little DSL program to define your build, so you have access to full Scala. Scala tries to be script-friendly, and that sometimes means that code is better than config.)
I've spent the morning fiddling with that, and gradually improving it, and the results are just lovely. For example, here is the listing for the Kingdom of Artemisia (the bits that are relevant to our OP so far, anyway):
That's compiled code, that builds the data structures representing the Kingdom. While it isn't *quite* the absolute minimum number of keystrokes to express the concepts, it's pretty damned close. And it's about as readable as I could hope for.Kingdom("Artemisia", awards = Seq( "Golden Feather of Artemisia", "Grace of Artemisia", "Griffin's Heart", "Griffin's Talon", "Lady of the Papillon", "LUST", "Maple Leaf of Artemisia", "Pillar of Artemisia" ), children = Seq( Barony("Arn Hold", awards = Seq( "Moose Combatant of Arn Hold" )), Barony("Loch Salaan", awards = Seq( "Bannthegn", "Crystal of the Salt Wastes", Award("Devoted and Unique Company of Keepers", "Devoted & Unique Company of Keepers"), "Falcon of Loch Salaan" )) ) )
What makes it really spiffy-keen, though, is that it's much more strongly-typed than it looks. (Scala is strongly typed to its core, with type inference that most languages can only envy.) So those lists of strings above, that give the names of the awards? Those aren't actually strings as far as the calling code is concerned. Instead, the compiler knows that it expects a Seq[AwardInfo] for the awards parameter. And I have the following implicit converter in scope:
That is to say, "When you are looking for an AwardInfo object, and what you find is a String, this is how to convert it into an AwardInfo". So each of those lines actually turns into an AwardInfo to get passed around. (And yes, the Eclipse plugin is smart enough to highlight these and provide tooltips about the conversion.) This way, I can just write a string for the "simple" cases where I just have a name, but give an explicit Award() listing (which is itself a special sort of function call, with lots of optional parameters) when I have more information to convey such as alternate spellings. (As in the DUCKs above.) That level of strong typing means that I get code as concise as most dynamic languages, but errors still usually get caught in the editor within a second of my typing them.
implicit def string2AwardInfo(name:String) = AwardInfo(AwardName(name, Gender.Unknown))
I don't think I can explain to a non-programmer the sheer delight of code like this, and the way it hits me on an aesthetic level. Think of it like sculpture, though. Most languages hand me basically a big hammer, and the result is that my programs come out rough and chunky -- you can sort of see what it's supposed to look like, but have to squint a bit. Scala, by contrast, gives me a whole assortment of chisels to work with, so that I can produce programs like this: finely detailed, with nothing extra to get in the way of its proper Platonic form. I spent the morning sculpting, and it is just *delightful* to get something pretty as a result...