Hacker News new | ask | show | jobs
by zapov 3960 days ago
I actually find compilers for transformation of DSL AST to target languages much more costly then designing the DSL syntax.

But that's probably because I don't think using templates for code generation is good enough. At least if you want to do something interesting with it.

Language workbenches cut down the cost of DSL design to minimum, but more interesting problem is providing valuable output from it.

1 comments

> I actually find compilers for transformation of DSL AST to target languages much more costly then designing the DSL syntax.

Mind explaining why? What can be costly and complex in a chain of trivial transforms, each being very simple, flat and comprehensible?

When I say DSL I mean external DSL, not a fluent interface.

So an example of the problem I deal with is a database migration. Let's say we have an entity with a value

entity SqlTable { List<TableColumn> nestedTuple; }

value TableColumn { int i; }

and if I change column type in value object to long I want my compilers to prepare a DB migration with the appropriate SQL statements for the specific DB I use. Of course, there is nothing too complex about it, but it's not trivial either (in this case you have to prepare a second field, unnest the whole hierarchy to get to the nested field, copy it to new type and compact the hierarchy back again).

I find it costly since there are gazillion of such features. And when they start interacting with each other things gets messy.

> When I say DSL I mean external DSL, not a fluent interface

If you're using a meta-language, there is no difference between external and embedded DSLs. External (or standalone) DSLs also should be built exactly the same way, on top of a hierarchy of existing DSL components.

In your case, you're likely not compartmentalising your DSLs properly, trying to stuff too much functionality into a single DSL while it must be a chain of different DSLs. E.g., one DSL for describing a schema of the DB you want to migrate (with a tool for inferring it from the existing DB schema), another for representing the intermediate uniform data format and transforms over it, and a third for mapping the uniform intermediate representation on your target DB schema. Of course I do not know any details of your particular problem, just describing how I solved a DB migration problem before. Your specifics could add more to the DSL chain design.

But I find it highly valuable to have a single model representation as a source of truth. You can see how it works in practice here: https://github.com/ngs-doo/revenj

Also, there are no tools for describing DB schemas that way (except if you consider DB DDL such a schema). So my DSL is used as uniform data format. And it's not a problem of mapping between formats, but within the logic required to do such a mapping. It can't be expressed as a simple transformation, compiler is required to analyze and transform it appropriately in various scenarios. And if you want optimizations, good luck with "simple mapping".

So yeah, it's complicated, but it needs to be complicated to support simple modeling DSL. Otherwise you are better off with having several DB schemas, various POOs, Protobuf/Flatbuffer IDL etc...

In this case you need four DSLs - your core model and three DSLs for mapping the model to the specific storage backends.
> When I say DSL I mean external DSL, not a fluent interface.

That's the problem here. 'DSL' is a vague and ambiguous notion. An ad-hoc 'fluent interface' usually isn't considered a DSL.

I'm talking about efficiently compiled embedded DSLs built on top of meta-language hosts (with potentially more than one backend).