Domain separate into problem domain and solution domain. The problem domain is closed to business, while the solution domain map the implementation to problem domain. These 2 domain communicate with "domain vocabulary", which explain domain concepts to the outside world. DSL qualities: 1. specific problem area, 2. higher level of abstraction. DSL classify: + Internal DSL. Use the infrastructure...
Domain separate into problem domain and solution domain.
The problem domain is closed to business, while the solution domain map the implementation to problem domain.
These 2 domain communicate with "domain vocabulary", which explain domain concepts to the outside world.
DSL qualities: 1. specific problem area, 2. higher level of abstraction.
+ Internal DSL. Use the infrastructure of an existing programming language.
+ External DSL. Has separate infrastructure for lexical analysis, parsing techniques, interpretation, compilation and code generation.
+ Nontextual DSL. Some representation like graphic, etc.
DSL Pros: expressive, concise, higher level of abstraction, higher payoff, scalable
DSL Cons: hard design, upfront cost, performance(may be), lack adequate tool support(may be), Yet-Another-Language-To-Learn Syndrome(-_-#), language cacophony(using multiple DSLs).
First, Create a domain vocabulary, Then, Implement the program domain.
Builder design pattern help make published api more expressive.Fluent Interfaces provide an easy-to-read representation of remain problem.(See more design abstraction in Appendix A)
Problems: Verbosity in syntax ( language basic syntax and design pattern details ) and Extra non-domain complexity.
+ Externalizing the domain with XML: It's more a documentary than an expressed-elegantly control structures.
+ Groovy. A dynamic language for JVM, that supports method_missing, meta programming, closure.
Integrating DSL with core application using `new GroovyShell().evaluate(script)`
+ Smart API is based on chaining method, also called fluent interface. Groovy or Ruby offer `named arguments` to help build Smart API.
+ Syntax Tree Manipulation. We can generate code by manipulating the AST.
+ Some Statically typed language( like Haskell, Scala) offer `types` to abstract domain semantics and make syntax more concisely, without using technique of generating codes. Pros: Have automatically type-checked by the host language compiler.
+ MetaProgramming! Runtime metaprogramming makes DSL dynamic by generating code during runtime. With compile-time metaprogramming, most common implemented with `Macros`, we can add custom syntax to DSL.
consists of 2 phases: `Parse` and `Process`.
+ Context-driven string manipulation: just a string that doesn't make sense in any programming language, but it can be coerced to valid Ruby or Groovy code.
+ XML configuration spec to a consumable resource: DI
+ DSL WorkBench.
+ Mix-in DSL with Embedded foreign code: let language parse the extension syntax using EBNF
+ DSL Design Based on Parser Combinator. Another way to define grammar .
Choosing DSL Implementation:
+ Reusing Existing Infrastructure: Meta-Programming is often better than embedded variant.
+ Leveraging Existing knowledge: Your team members.
+ Learning curve with external DSLs. Hard, but you can customize almost everything.
+ The right level of expressivity. Internal DSL may cause abstraction leaky.
+ Composability. Composing the host language and internal DSL is easy. External DSLs is harder to make composaility.
Internal DSL integration patterns
+ DSL wrapper: Implementation using java, and wrapped with Scala
+ Language-specific integration features: (GroovyClassLoader#run).delegate(SolutionDomainInstance)
+ Spring-Based integration: Under using Spring DI as interface, we can write ruby dsl.
External DSL integration patterns: XML
+ Context-driven string manipulation: string convert to host language through tokenize process(re, dynamic eval)
+ Transforming XML to a consumable resource: XML parsers are the most natural form of integration point.
+ Non-textual representation: -> AST
+ Mixing DSL with embedded foreign code: transforms the DSL into appropriate data structures in the language of the embedded code and plugs in the embedded code snippets as callbacks.
+ DSL design-based on parser combinators: Parser combinators are implemented as a library in languages.
Handle Errors and Exceptions:
+ Naming an exception: use domain vocabulary
+ Handling incorrect typing errors: use `method_missing` for internal DSL, report error occur line when parse error for external DSL.
+ Handling exceptional business conditions
Managing performance: It's not so important.Even so, we need to consider performance factors when designing. Benchmark extensively before going for optimization.
Internal DSLs fall into two categories: Embedded(Smart API, Reflective, Typed embedded) & Generative(Compile-time , runtime). Embedded DSLs: patterns in metaprogramming： + Implicit context and Smart APIs: - define the implicit context: Ruby(instance_eval) and Groovy(with) can also build the implicit context declaration, making DSL more readable. - use smart api, Manage side effect be out o...
Internal DSLs fall into two categories: Embedded(Smart API, Reflective, Typed embedded) & Generative(Compile-time , runtime).
Embedded DSLs: patterns in metaprogramming：
+ Implicit context and Smart APIs:
- define the implicit context: Ruby(instance_eval) and Groovy(with) can also build the implicit context declaration, making DSL more readable.
- use smart api, Manage side effect be out of abstraction, using block to make side effect.
+ Reflective metaprogramming with dynamic decorators as Mix-in: The Decorator design pattern is used to add functionalities to objects dynamically during runtime. Ruby `extend` module would make DSL more expressive. (May be the technique will cause type safety and performance problem.)
+ Reflective metaprogramming with builders.
Embedded DSLs: patterns with typed abstractions: It's using commonly in static typed language. Use Higher-order functions as generic abstractions. Remember set enough type constraint in model domain logic!
Generative DSLs: boilerplates for runtime generation:
+ Ruby metaprogramming for concise DSL design (like Rails active record validates_*)
+ using class methods to abstract validation logic
+ mixin for dynamic method generation
Generative DSLs: macros for compile-time code generation
Making DSLs concise with dynamic typing:
non-programmer can at least **understand** domain semantics. Ruby/Groovy/Clojure is good at this.
+ enhance readability: without typing annotation! more succinct syntax.
+ duck typing
+ write an interpreter to generate from string to code.
Anatomy of an external DSL:
DSL script -> Parser -> Semantic model
The role of a parser in designing an external DSL:
+ YACC + Lex -> C
+ Bison + Flex -> C++
+ ANTLR -> Java, C, C++, Python, and Ruby.
Design the lexical analyzer: specify lexer rules either inline with the grammar specification or as a separate file
Design the grammar rules: also defined in a separate file followed the EBNF notation.ANTLRWorks is a gui-based interpreter env to help run sample DSL scripts.
Embeding foreign code as a custom actions, populate the semantic model.
Building the parser module: wrap it up.
The mature evolution of a DSL:
+ Versioning your DSL. write a simple note when fix bug or introduce new features. Make sure handling backward compatibility, Catering to specific user needs
+ Smoother evolution of DSL:
+ Implicit context for better version evolution
+ Automatic transformation for backward compatibility
+ A DSL facade can address a lot of versioning problems: decouple dsl wrapper and base abstractions
+ Follow the principles of **well-designed abstractions**.
External DSL ，其实就是写编译器/解释器。 不过 DSL 的语法比一般的编程语言少很多，满足基本需求就可以，甚至不必是图灵完备的。 External DSL design follows the same lifecycle and principles of general-purpose language design. Although the statement is true in theory, it’s not all that grim when you consider that your DSL isn’t necessarily as complex in syntax and semantics as a general-purpose pro...
Internal DSL 主要分为两种类型： Generative—Domain-specific constructs are transformed to generate code through compile-time macros, preprocessors, or some form of runtime meta-object protocol (MOP). Embedded—Domain-specific types are embedded within the type system of the host language. 这两种类型并不是互斥的，比如一个 Lisp 宏写的 Internal DSL 就是两种兼有。 实现 Internal DSL 时经常用到的一些...
Internal DSL 的定义： An internal DSL is one that uses the infrastructure of an existing programming language (also called the host language of the DSL) to build domain-specific semantics on top of it. In most cases, an internal DSL is implemented as a library on top of the existing host language. As you see in figure 1.5, the internal DSL script is a thin veneer over the abstrac- tions of an u...
DSL 执行的几种模式： 1. 作为 script 直接运行（比如 AWK） 2. 作为 bytecodes 在 VM 之上运行（比如 JAVA 系的DSL） 3. 作为编译器时的元语言运行（比如 Lisp） The script can directly execute the underlying model without any more code generation or manipulation. There might be an interpreter that directly interprets the script and runs it. The UNIX little programming languages awk and sed are examples ...
DSL 的本质，创建元语言抽象，或者更直接地说，创建一种新语言。 In most cases, a DSL is nothing but a layer of abstraction over the host language that presents a domain-friendly interface to the business users. (It’s not always the host language. See section 1.5 for details about DSL classification.) You’re kind of extending the host language to implement another language on top of it. This conc...
怎样写一个好的 DSL ？ 恰当的抽象层次，面向用户的API，隐藏实现细节。 A DSL provides a direct mapping to the artifacts of the problem domain. If the problem domain has an entity named Trade, the DSL script must contain the same abstraction that plays the same role. The DSL script must use the common vocabulary of the problem domain. The vocabulary becomes the catalyst for better communication bet...
DSL 和 GENERAL-PURPOSE PROGRAMMING LANGUAGE 不同的地方。 最重要的两点： A DSL is targeted at a specific problem area A DSL contains syntax and semantics that model concepts at the same level of abstraction as the problem domain does 使用 DSL 的好处： When you program using a DSL, you deal only with the complexity of the problem domain. You don’t have to worry about the implementation details o...
DSL 和 GENERAL-PURPOSE PROGRAMMING LANGUAGE 不同的地方。