5. Domain Specific Languages (DSLs)
are limited forms of computer language
designed for a specific class of problems.
6. Martin Fowler
“(DSL) is a computer language that's
targeted to a particular kind of problem,
rather than a general purpose language
that's aimed at any kind of software
problem.”
7. Paul Graham
“*Lisp Programmers+ follow a principle
which could be called bottom-up design--
changing the language to suit the
problem.”
9. Types of DSL
Internal External
• Written in host • Separate to host
language language
• Conventional use of • Needs the
subset of host language compiler/interpreter to
syntax execute
10. Types of DSL
Internal External
• Tied to base language • Lack of Symbolic
• Awkward with Integration
mainstream • Complex
parser/generator
technologies
• Ignorant IDEs
• (language cacophony)
18. Why should we use DSL
• Domain-specific abstractions: a DSL provides pre-
defined abstractions to directly represent
concepts from the application domain.
Consequently, domain experts themselves can
understand, validate, modify, and often even
develop DSL program
• Domain-specific concrete syntax: a DSL offers a
natural notation for a given domain and avoids
syntactic clutter that often results when using a
GPL.
19. Why should we use DSL
• Domain-specific error checking: a DSL enables
building static analyzers that can find more
errors than similar analyzers for a GPL and
that can report the errors in language familiar
to the domain expert.
• Domain-specific optimizations: a DSL creates
opportunities for generating optimized code
base on domain-specific knowledge, which is
usually not available to a compiler for a GPL.
20. Why should we use DSL
• Domain-specific tool support: a DSL creates
opportunities to improve any tooling aspect of
a development environment, including,
editors, debuggers, version control, etc.; the
domain-specific knowledge that is explicitly
captured by a DSL can be used to provide
more intelligent tool support for developers.
21. Why is DSL good?
Business
Peoples and
“Rules”
Developers
and their
“Magic”
22. Read and Write
director of finance for the client bank to edit, test,
and deploy the business rules of the system without any assistance from IT
32. Extension methods
DateTime now =
VS DateTime res =
DateTime.Now; 3.Days()
TimeSpan span = .From(
TimeSpan.FromDays(3); DateTime.Now
DateTime res = )
now + span;
33. Extension methods
public static class MyExtensions {
public static TimeSpan Days(this int i) {
return TimeSpan.FromDays(i);
}
public static DateTime From(
this TimeSpan span, DateTime dt) {
return dt + span;
}
}
36. LINQ
string s1 = GetS1(); VS var res =
string s2 = GetS2(); from a in GetS1()
string res = from b in GetS2()
string.Empty; select a + b;
if(s1 != null && s2 != null){
res = s1 + s2;
}
37. LINQ
public class Maybe<T> {
public T Value { get; private set; }
public bool HasValue { get; private set; }
private Maybe() { HasValue = false; }
public Maybe(T value) {
Value = value;
HasValue = true;
}
public static readonly Maybe<T> Nothing = new Maybe<T>();
}
38. LINQ
public static Maybe<T> ToMaybe<T>(this T value) {
return new Maybe<T>(value);
}
public static Maybe<U> SelectMany<T, U>(
this Maybe<T> src, Func<T, Maybe<U>> f) {
if (!src.HasValue) {
return Maybe<U>.Nothing;
}
return f(src.Value);
}
39. Important
• LINQ is a Monad
• F# Workflows are monads
• What isn’t?!
"[W]hen the designers of F# talked with the designers of Haskell about this,
they agreed that the word monad is a bit obscure and sounds a little
daunting and that using other names might be wise.“
[F# Workflows and Haskell Monads, Expert F#, p232]
42. Workflows
type Чо<'a> =
| Ничо
| Чото of 'a
let bindMaybe x f =
match x with
| Ничо -> Ничо
| Чото y -> f y
type MaybeBuilder() =
member x.Return(what) = Чото what
member x.Bind(m, f) = bindMaybe m f
let чокак = new MaybeBuilder()
43. Workflows
let first =
чокак ,
let! x = Чото 6
let! y = Чото 5
return (x + y)
}
let second =
чокак ,
let! x = Ничо
let! y = Чото 5
return x+y
}
51. Validation DSL : IronPython
public class User : ValidationBase
{
[Validation("first_name_length")]
public string FirstName { get; set; }
}
52. Validation DSL : IronPython
<rule name="last_name_length"
message="Last name must be at least 2
characters">
<![CDATA[result = property_value != None
and len(property_value) >= 2]]>
</rule>
59. Validation DSL : Boo
Boo:
results.Add(fail("Username and Password are required"))
C#:
CompilerContext ctx =
_interpreter.EvalCompilerInput(new
FileInput(_filePath));
foreach (CompilerError error in ctx.Errors)
{
sb.AppendLine(error.Message);
}
60. Object2RSS DSL : Boo
Rules
(Boo)
Model
(C#)
ASP.NET
MVC
Application
61. Object2RSS DSL : Boo
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public double Price { get; set; }
public DateTime CreateDate { get; set; }
public Manufacturer Manufacturer { get; set; }
}
67. MiniBar specification tutorial
import Specter.Framework
import Bender
context "At Bender's bar":
_bar as duck
setup:
subject _bar = Bender.MiniBar()
specify { _bar.DrinkOneBeer() }.Must.Not.Throw()
specify "If I drink 5 beers then I owe 5 bucks":
for i in range(5):
_bar.DrinkOneBeer()
_bar.Balance.Must.Equal(-5)
specify "If I drink more than ten beers then I get drunk":
for i in range(10):
_bar.DrinkOneBeer()
{ _bar.DrinkOneBeer() }.Must.Throw()
68. MiniBar specification tutorial
import Specter.Framework
import Bender
context "At Bender's bar":
_bar as duck
setup: [NUnit.Framework.TestFixture]
subject _bar = Bender.MiniBar()class EmptyStack:
specify { _bar.DrinkOneBeer() }.Must.Not.Throw()
specify "If I drink 5 beers then I owe 5 bucks":
for i in range(5):
_bar.DrinkOneBeer()
_bar.Balance.Must.Equal(-5)
specify "If I drink more than ten beers then I get drunk":
for i in range(10):
_bar.DrinkOneBeer()
{ _bar.DrinkOneBeer() }.Must.Throw()
69. MiniBar specification tutorial
import Specter.Framework [NUnit.Framework.SetUp]
import Bender def SetUp():
context "At Bender's bar":
_bar as duck subject _bar =
setup:
subject _bar = Bender.MiniBar() Bender.MiniBar()
specify { _bar.DrinkOneBeer() }.Must.Not.Throw()
specify "If I drink 5 beers then I owe 5 bucks":
for i in range(5):
_bar.DrinkOneBeer()
_bar.Balance.Must.Equal(-5)
specify "If I drink more than ten beers then I get drunk":
for i in range(10):
_bar.DrinkOneBeer()
{ _bar.DrinkOneBeer() }.Must.Throw()
70. MiniBar specification tutorial
import Specter.Framework
import Bender
context "At Bender's bar":
_bar as duck
setup:
subject _bar = Bender.MiniBar()
specify { _bar.DrinkOneBeer() }.Must.Not.Throw()
specify "If I drink 5 beers then I owe 5 bucks":
for i in range(5):
_bar.DrinkOneBeer()
_bar.Balance.Must.Equal(-5)
[NUnit.Framework.Test] then I get drunk":
specify "If I drink more than ten beers
for i in range(10):
def BarDrinkOneBeerMustNotThrow():
_bar.DrinkOneBeer()
Assert.DoesNotThrow(_bar. DrinkOneBeer())
{ _bar.DrinkOneBeer() }.Must.Throw()
71. MiniBar specification tutorial
[NUnit.Framework.Test]
import Specter.Framework
import Bender def IfIDrink5BeersThenIOwe5Bucks():
context "At Bender's bar": for i in range(5):
_bar as duck _bar.DrinkOneBeer()
setup: Int32MustModule.Must(_bar.Balance, “Bar
subject _bar = Bender.MiniBar() -5").Equal(-5)
balance must equal
specify { _bar.DrinkOneBeer() }.Must.Not.Throw()
specify "If I drink 5 beers then I owe 5 bucks":
for i in range(5):
_bar.DrinkOneBeer()
_bar.Balance.Must.Equal(-5)
specify "If I drink more than ten beers then I get drunk":
for i in range(10):
_bar.DrinkOneBeer()
{ _bar.DrinkOneBeer() }.Must.Throw()
72. MiniBar specification tutorial
[NUnit.Framework.Test]
import Specter.Framework
import Bender
contextdef IfiDrinkMoreThanTenBeersThenIGetDrunk():
"At Bender's bar":
_bar as duck
setup:
for i in range(10):
_bar.DrinkOneBeer()
subject _bar = Bender.MiniBar()
specify { _bar.DrinkOneBeer() }.Must.Not.Throw()
Assert.Throws((typeof(InvalidOperationExc
specify "If I drink 5 beers then I owe 5 bucks":
eption), _bar.DrinkOneBeer())
for i in range(5):
_bar.DrinkOneBeer()
_bar.Balance.Must.Equal(-5)
specify "If I drink more than ten beers then I get drunk":
for i in range(10):
_bar.DrinkOneBeer()
{ _bar.DrinkOneBeer() }.Must.Throw()