81
Building DSLs on the CLR and DLR

Building DSLs On CLR and DLR (Microsoft.NET)

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: Building DSLs On CLR and DLR (Microsoft.NET)

Building

DSLs

on the CLR and DLR

Page 3: Building DSLs On CLR and DLR (Microsoft.NET)

http://spbalt.net/

http://altdotnet.org

Page 4: Building DSLs On CLR and DLR (Microsoft.NET)

DOMAIN SPECIFIC LANGUAGES

The short annotation

Page 5: Building DSLs On CLR and DLR (Microsoft.NET)

Domain Specific Languages (DSLs)

are limited forms of computer language designed for a specific class of problems.

Page 6: Building DSLs On CLR and DLR (Microsoft.NET)

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.”

Page 7: Building DSLs On CLR and DLR (Microsoft.NET)

Paul Graham

“*Lisp Programmers+ follow a principle

which could be called bottom-up design--

changing the language to suit the

problem.”

Page 8: Building DSLs On CLR and DLR (Microsoft.NET)

DSLS CLASSIFICATION

Page 9: Building DSLs On CLR and DLR (Microsoft.NET)

Types of DSL

Internal

• Written in host language

• Conventional use of subset of host language syntax

External

• Separate to host language

• Needs the compiler/interpreter to execute

Page 10: Building DSLs On CLR and DLR (Microsoft.NET)

Types of DSL

Internal

• Tied to base language

• Awkward with mainstream

External

• Lack of Symbolic Integration

• Complex parser/generator technologies

• Ignorant IDEs

• (language cacophony)

Page 11: Building DSLs On CLR and DLR (Microsoft.NET)

Types of DSL

Internal

• LINQ (C#)

• Monads (Haskell)

• Workflows (F#)

• Sequence expressions (F#)

• List comprehensions (Haskell, Python)

External

• Regexp

• SQL

• CSS

• Ant

• XSLT

Page 12: Building DSLs On CLR and DLR (Microsoft.NET)

Types of DSL

Log Activity

Clear Activity

Shutdown ActivityW

ork

flo

w

Graphical DSL

Page 13: Building DSLs On CLR and DLR (Microsoft.NET)

LANGUAGES

DSL Oriented .NET Languages

Page 14: Building DSLs On CLR and DLR (Microsoft.NET)

Lisp

Smalltalk

Ruby

Python

Boo

C#

F#

Page 15: Building DSLs On CLR and DLR (Microsoft.NET)

Lisp

Smalltalk

Ruby

Python

Boo DLRCLR

C#

F#

Page 16: Building DSLs On CLR and DLR (Microsoft.NET)

I <3

Page 17: Building DSLs On CLR and DLR (Microsoft.NET)

WHY SHOULD WE USE DSL?

The important advantages over a GPL

Page 18: Building DSLs On CLR and DLR (Microsoft.NET)

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.

Page 19: Building DSLs On CLR and DLR (Microsoft.NET)

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.

Page 20: Building DSLs On CLR and DLR (Microsoft.NET)

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.

Page 21: Building DSLs On CLR and DLR (Microsoft.NET)

Why is DSL good?

Business Peoples and

“Rules”

Developers and their “Magic”

Page 22: Building DSLs On CLR and DLR (Microsoft.NET)

Read Writeand

director of finance for the client bank to edit, test,

and deploy the business rules of the system without any assistance from IT

Page 23: Building DSLs On CLR and DLR (Microsoft.NET)

Read Write

Page 24: Building DSLs On CLR and DLR (Microsoft.NET)

PATTERNS

DSL Building Patterns

Page 25: Building DSLs On CLR and DLR (Microsoft.NET)

Better code readability

• Fluent interfaces

• Extension methods

• LINQ

• Workflows

• Syntactic sugar

• AdHoc LINQ

Page 26: Building DSLs On CLR and DLR (Microsoft.NET)

Fluent interfaces

Expect

.Once

.On(…)

.Method(…)

.WithAnyArguments()

.Will(

Return.Value(…)

)

NMock mocking library usage

Page 27: Building DSLs On CLR and DLR (Microsoft.NET)

Fluent interfaces

Expect

.Once

.On(…)

.Method(…)

.WithAnyArguments()

.Will(

Return.Value(…)

)

public static Expect {…}

IRecieverSyntax

IMethodSyntax

IArgumentSyntax

IActionSyntax

IAction

Page 28: Building DSLs On CLR and DLR (Microsoft.NET)

Fluent interfaces

public class ExpectationBuilder:IReceiverSyntax,IMethodSyntax,IArgumentSyntax,IActionSyntax {…}

public IMethodSyntax On(…){// …return this;

}public IArgumentSyntax Method(…){

// …return this;

}

Page 29: Building DSLs On CLR and DLR (Microsoft.NET)

Combinations

today.at 3.pm

3.days.from_today at(3.pm)

Page 30: Building DSLs On CLR and DLR (Microsoft.NET)

Timespan Literals

print(50s) // 50 seconds

print(1d) // 1 day

print(2m) // 2 minutes

print(42ms) // 42 miliseconds

print("Tomorrow this time will be: ${date.Now + 1d}")

Page 31: Building DSLs On CLR and DLR (Microsoft.NET)

String interpolation

print("Now is ${date.Now}.")

print("Tomorrow will be ${date.Now + 1d}")

Page 32: Building DSLs On CLR and DLR (Microsoft.NET)

Extension methods

DateTime now = DateTime.Now;

TimeSpan span =

TimeSpan.FromDays(3);

DateTime res =

now + span;

DateTime res =

3.Days()

.From(

DateTime.Now

)

VS

Page 33: Building DSLs On CLR and DLR (Microsoft.NET)

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;

}

}

Page 34: Building DSLs On CLR and DLR (Microsoft.NET)

Man, there’s LINQ alreadyCan’t we use that?

Page 35: Building DSLs On CLR and DLR (Microsoft.NET)

We do

Page 36: Building DSLs On CLR and DLR (Microsoft.NET)

LINQ

string s1 = GetS1();

string s2 = GetS2();

string res =

string.Empty;

if(s1 != null && s2 != null){

res = s1 + s2;

}

var res =

from a in GetS1()

from b in GetS2()

select a + b;

VS

Page 37: Building DSLs On CLR and DLR (Microsoft.NET)

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>();}

Page 38: Building DSLs On CLR and DLR (Microsoft.NET)

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);

}

Page 39: Building DSLs On CLR and DLR (Microsoft.NET)

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 littledaunting and that using other names might be wise.“

[F# Workflows and Haskell Monads, Expert F#, p232]

Page 40: Building DSLs On CLR and DLR (Microsoft.NET)

C# is no fun.

Want more!

Page 41: Building DSLs On CLR and DLR (Microsoft.NET)

Yeah?! F# you!

Page 42: Building DSLs On CLR and DLR (Microsoft.NET)

Workflows

type Чо<'a> =| Ничо| Чото of 'a

let bindMaybe x f = match x with| Ничо -> Ничо| Чото y -> f y

type MaybeBuilder() =member x.Return(what) = Чото whatmember x.Bind(m, f) = bindMaybe m f

let чокак = new MaybeBuilder()

Page 43: Building DSLs On CLR and DLR (Microsoft.NET)

Workflows

let first = чокак ,

let! x = Чото 6let! y = Чото 5return (x + y)

}

let second =чокак ,

let! x = Ничоlet! y = Чото 5return x+y

}

Page 44: Building DSLs On CLR and DLR (Microsoft.NET)

Syntactic sugar

Page 45: Building DSLs On CLR and DLR (Microsoft.NET)

Sweeties

• Partial application

• Pipelining

• Composition

• List comprehension

• Operators overriding

• AST modifications

• Measurement units

Page 46: Building DSLs On CLR and DLR (Microsoft.NET)

Million years ago there wasn’t LINQAnd I still program C# 2. Can I have some DSL?

Page 47: Building DSLs On CLR and DLR (Microsoft.NET)

AdHoc LINQ

Calculation<Item> calculation =

Time.Between(

Select.First(thisYear),

Select.Last(thisYear)

);

Page 48: Building DSLs On CLR and DLR (Microsoft.NET)

SAMPLES

Simples DSLs on .NET

Page 49: Building DSLs On CLR and DLR (Microsoft.NET)
Page 50: Building DSLs On CLR and DLR (Microsoft.NET)

Validation DSL : IronPython

discussion demo discussion

Page 51: Building DSLs On CLR and DLR (Microsoft.NET)

Validation DSL : IronPython

public class User : ValidationBase

{

[Validation("first_name_length")]

public string FirstName { get; set; }

}

Page 52: Building DSLs On CLR and DLR (Microsoft.NET)

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>

Page 53: Building DSLs On CLR and DLR (Microsoft.NET)

Validation DSL : IronPython

PythonEngine::Execute(“python code here”)

Page 54: Building DSLs On CLR and DLR (Microsoft.NET)
Page 55: Building DSLs On CLR and DLR (Microsoft.NET)

Validation DSL : Boo

ASP.NET MVC

Application

Model (C#)

Rules (Boo)

Page 56: Building DSLs On CLR and DLR (Microsoft.NET)

Validation DSL : Boo

public class User

{

public string Username { get; set; }

public string Password { get; set; }

}

Page 57: Building DSLs On CLR and DLR (Microsoft.NET)

Validation DSL : Boo

rule_for "User":

validate def(x):

results = create_results()

if string.IsNullOrEmpty(x.Username)

return results

Page 58: Building DSLs On CLR and DLR (Microsoft.NET)

Validation DSL : Boo

Boo.Lang.Interpreter. InteractiveInterpreter_interpreter;

Func<List<ValidationResult>> createResults = (() => new List<ValidationResult>());

_interpreter.SetValue("create_results", createResults);

Page 59: Building DSLs On CLR and DLR (Microsoft.NET)

Validation DSL : Boo

Boo:

results.Add(fail("Username and Password are required"))

C#:

CompilerContext ctx = _interpreter.EvalCompilerInput(newFileInput(_filePath));

foreach (CompilerError error in ctx.Errors)

{

sb.AppendLine(error.Message);}

Page 60: Building DSLs On CLR and DLR (Microsoft.NET)

Object2RSS DSL : Boo

ASP.NET MVC

Application

Model (C#)

Rules (Boo)

Page 61: Building DSLs On CLR and DLR (Microsoft.NET)

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; }}

Page 62: Building DSLs On CLR and DLR (Microsoft.NET)

Object2RSS DSL : Boo

rule_for "Product":

title def(x):

return "${x.Manufacturer.Name.ToUpper()} -${x.Name}"

description def(x):

return x.Description

link def(x):

return "http://codevoyeur.com/products/${x.Id}"

pubDate def(x):

return x.CreateDate.ToString("s")

Page 63: Building DSLs On CLR and DLR (Microsoft.NET)

Object2RSS DSL : Boo

var engine = new RssDslEngine();foreach (T item in items)

{writer.WriteStartElement("item");List<string> fields = RssFieldFactory.Create();fields.ForEach(x =>

writer.WriteElementString(x, engine.Execute(item, x)));writer.WriteEndElement();

}

Page 64: Building DSLs On CLR and DLR (Microsoft.NET)

Object2RSS DSL : Boo

public string Execute(object o, string field){

string ruleName = o.GetType().Name;if (_rules.ContainsKey(ruleName)){

return _rules[ruleName][field].Invoke(o);}else{

throw new ApplicationException("Invalid rule name");

}}

Page 65: Building DSLs On CLR and DLR (Microsoft.NET)

SPECTER FRAMEWORK

A BDD framework for .NET and Mono

Page 66: Building DSLs On CLR and DLR (Microsoft.NET)

MiniBar specification tutorial

Page 67: Building DSLs On CLR and DLR (Microsoft.NET)

MiniBar specification tutorial

import Specter.Frameworkimport 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()

Page 68: Building DSLs On CLR and DLR (Microsoft.NET)

MiniBar specification tutorial

import Specter.Frameworkimport 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()

[NUnit.Framework.TestFixture]class EmptyStack:

Page 69: Building DSLs On CLR and DLR (Microsoft.NET)

MiniBar specification tutorial

import Specter.Frameworkimport 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()

[NUnit.Framework.SetUp] def SetUp():

subject _bar = Bender.MiniBar()

Page 70: Building DSLs On CLR and DLR (Microsoft.NET)

MiniBar specification tutorial

import Specter.Frameworkimport 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()

[NUnit.Framework.Test]def BarDrinkOneBeerMustNotThrow():

Assert.DoesNotThrow(_bar. DrinkOneBeer())

Page 71: Building DSLs On CLR and DLR (Microsoft.NET)

MiniBar specification tutorial

import Specter.Frameworkimport 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()

[NUnit.Framework.Test]def IfIDrink5BeersThenIOwe5Bucks():

for i in range(5):_bar.DrinkOneBeer()

Int32MustModule.Must(_bar.Balance, “Bar balance must equal -5").Equal(-5)

Page 72: Building DSLs On CLR and DLR (Microsoft.NET)

MiniBar specification tutorial

import Specter.Frameworkimport 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()

[NUnit.Framework.Test]def IfiDrinkMoreThanTenBeersThenIGetDrunk():

for i in range(10): _bar.DrinkOneBeer()

Assert.Throws((typeof(InvalidOperationException), _bar.DrinkOneBeer())

Page 73: Building DSLs On CLR and DLR (Microsoft.NET)

MiniBar specification tutorial

Page 74: Building DSLs On CLR and DLR (Microsoft.NET)

MiniBar specification tutorial

namespace Bender

class MiniBar:

def DrinkOneBeer():

pass

[getter(“Balance”)+

_balance = 0

Page 75: Building DSLs On CLR and DLR (Microsoft.NET)

MiniBar specification tutorial

Page 76: Building DSLs On CLR and DLR (Microsoft.NET)

MiniBar specification tutorial

namespace Bender

class MiniBar:

def DrinkOneBeer():

_balance--

if _balance < -10:

raise System.Exception("i'mdrunk")

[getter(“Balance”)+

_balance = 0

Page 77: Building DSLs On CLR and DLR (Microsoft.NET)

MiniBar specification tutorial

Page 78: Building DSLs On CLR and DLR (Microsoft.NET)

thx.

Page 80: Building DSLs On CLR and DLR (Microsoft.NET)

Resources

• http://www.code-magazine.com/article.aspx?quickid=0902041&page=1 (Building Domain Specific Languages in C#)

• http://bradfordcross.blogspot.com/2009/01/external-dsl-vs-internal-dsl-smack-down_24.html (External DSL vs. Internal DSL Smack Down)