We want to create a DSL in Scala where you can declaratively list the ingredients that a product consist of. These ingredients can consist of for example "Create product a", "Create product b", "Send mail". The ingredients can depend on each other (e.g., "mail" can only be send once both product a and product b are created). The goal is that the definition of the ingredients does not include imperative information. The parser of the DSL needs to determine the correct execution order based on for example types.

So for example:

"CompletedProduct" consists of { Mail, ProductA, ProductB }

should result in ProductA and ProductB being executed first (preferably sequentially, but this is not necessary in a first step), and once this is finished Mail should be executed and a mail should be send.

What are possible ways to accomplish such a DSL that determined an execution plan?

  • 2
    Sounds like you’re looking for topological sorting. – Jon Purdy Apr 11 '16 at 9:06
  • 1
    This is sometimes called dependency-oriented programming. The stereotypical example pf this type of programming is make, other examples are Ant, Rake, and SBT (the Scala Build Tool). – Jörg W Mittag Apr 12 '16 at 0:25

This isn't really specific to Scala DSLs. You have the same problem with the traditional Builder pattern, when you want a builder that needs to be given certain things. Instead of execution order it is then a dependency.

For example, you want a builder that can build either ProductA or ProductB, but when you choose ProductA you must specify a delivery mechanism, and for ProductB you need a customer id (basically just two different things).

What you can do to solve this is the same as for Scala DSLs: You create classes corresponding to the individual steps and instead of returning this (or self), you return a new object with the next possible options.

So Builder.createProductA returns a ProductABuilder with a "setDeliveryMechanism" method, whereas Builder.createProductB returns a ProductBBuilder.

In your case, you probably don't want your DSL users to start declaratively define a product via "Mail". Instead, you have a DSL main class which allows creation of ProductA or ProductB, but returns a "MissingProductB", or "MissingProductA" class, on which the corresponding other product can be specified. Once you have both, the next class may be MissingMail.

Note that this class structure statically corresponds to the possible sentences of your DSL grammar.

Something that's hard to realize via this approach is when you want to be able to arbitrarily mix and match those three things. You can then either add some sort of validation in the end (but that's runtime not compile time validation), or you would have to create specific classes for all the different variations of possible calls at which time you will probably call no-joy.

  • Thanks! This way to look at it helps. But the question: how would you handle the following case: ProductA and ProductB both have as next step Mail, but in our definition the mail only must be send if BOTH ProductA and ProductB are created. And how would you handle long-running steps (e.g., wait two hours for ProductA created event)? – Erwin Rooijakkers Apr 11 '16 at 9:11
  • I was just looking at the statical language. Execution can be considered entirely independent of your declarative specification language. (i.e.what a builder builds is entirely up to you and can have any execution semantics you want) – Frank Apr 11 '16 at 9:47
  • I think the suggestion of Jon Purdy to use topological sorting (defining a directional non-cyclic graph whose nodes consist of the ingredients which each contain possible input and output and then ordering them) is the way to accomplish the execution order. This can then generate a builder. I don't think the builder pattern you clearly describe can be used to determine the execution order. – Erwin Rooijakkers Apr 11 '16 at 12:53
  • I may have misunderstood your question then. I'm not following your comment here either, but if you're happy with a top.sort it's fine with me. – Frank Apr 11 '16 at 16:54
  • No problem. :) I am very happy you helped out. – Erwin Rooijakkers Apr 11 '16 at 17:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.