Building Dsls On Clr And Dlr (.net)

  • Uploaded by: Vitaly Baum
  • 0
  • 0
  • May 2020
  • PDF

This document was uploaded by user and they confirmed that they have the permission to share it. If you are author or own the copyright of this book, please report to us by using this DMCA report form. Report DMCA


Overview

Download & View Building Dsls On Clr And Dlr (.net) as PDF for free.

More details

  • Words: 1,744
  • Pages: 81
Building

DSL

s

on the CLR and DLR

VitalyBaum [email protected] butaji

MaximMoiseev [email protected] moiseev

http://altdotnet.org

http://spbalt.net/

The short annotation

DOMAIN SPECIFIC LANGUAGES

Domain Specific Languages (DSLs) are limited forms of computer language designed for a specific class of problems.

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

Paul Graham “*Lisp Programmers+ follow a principle which could be called bottom-up design-changing the language to suit the problem.”

DSLS CLASSIFICATION

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

Types of DSL Internal • Tied to base language • Awkward with mainstream





• •

External Lack of Symbolic Integration Complex parser/generator technologies Ignorant IDEs (language cacophony)

Types of DSL • • • • •

Internal LINQ (C#) Monads (Haskell) Workflows (F#) Sequence expressions (F#) List comprehensions (Haskell, Python)

• • • • •

External Regexp SQL CSS Ant XSLT

Types of DSL

Workflow

Graphical DSL Log Activity Clear Activity

Shutdown Activity

DSL Oriented .NET Languages

LANGUAGES

F# Lisp

Ruby

Python Boo

C#

Smalltalk

F# Lisp

CLR Boo

C#

Ruby

Python

DLR Smalltalk

I <3

The important advantages over a GPL

WHY SHOULD WE USE DSL?

Why should we use DSL • Domain-specific abstractions: a DSL provides predefined 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.

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.

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.

Why is DSL good? Business Peoples and “Rules”

Developers and their “Magic”

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

Read

Write

DSL Building Patterns

PATTERNS

Better code readability • • • • • •

Fluent interfaces Extension methods LINQ Workflows Syntactic sugar AdHoc LINQ

Fluent interfaces Expect .Once .On(…) .Method(…) .WithAnyArguments() .Will( Return.Value(…) ) NMock mocking library usage

Fluent interfaces Expect .Once .On(…) .Method(…) .WithAnyArguments() .Will( Return.Value(…) )

public static Expect {…} IRecieverSyntax IMethodSyntax IArgumentSyntax IActionSyntax IAction

Fluent interfaces public class ExpectationBuilder: IReceiverSyntax, IMethodSyntax, IArgumentSyntax, IActionSyntax {…} public IMethodSyntax On(…){ // … return this; } public IArgumentSyntax Method(…){ // … return this; }

Combinations

today.at 3.pm 3.days.from_today at(3.pm)

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}")

String interpolation

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

Extension methods

VS

DateTime now = DateTime.Now; TimeSpan span = TimeSpan.FromDays(3); DateTime res = now + span;

DateTime res = 3.Days() .From( DateTime.Now )

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

Man, there’s LINQ already Can’t we use that?

We do

LINQ string s1 = GetS1(); string s2 = GetS2(); string res = string.Empty; if(s1 != null && s2 != null){ res = s1 + s2; }

VS var res = from a in GetS1() from b in GetS2() select a + b;

LINQ public class Maybe { 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 Nothing = new Maybe();

}

LINQ public static Maybe ToMaybe(this T value) { return new Maybe(value); } public static Maybe SelectMany( this Maybe src, Func> f) { if (!src.HasValue) { return Maybe.Nothing; } return f(src.Value); }

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]

C# is no fun. Want more!

Yeah?! F# you!

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

Workflows let first = чокак , let! x = Чото 6 let! y = Чото 5 return (x + y) } let second = чокак , let! x = Ничо let! y = Чото 5 return x+y }

Syntactic sugar

Sweeties • • • • • • •

Partial application Pipelining Composition List comprehension Operators overriding AST modifications Measurement units

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

AdHoc LINQ Calculation calculation = Time.Between( Select.First(thisYear), Select.Last(thisYear) );

Simples DSLs on .NET

SAMPLES

Validation DSL : IronPython

discussion

demo

discussion

Validation DSL : IronPython public class User : ValidationBase { [Validation("first_name_length")] public string FirstName { get; set; } }

Validation DSL : IronPython = 2]]>

Validation DSL : IronPython

PythonEngine::Execute(“python code here”)

Validation DSL : Boo

Rules (Boo) Model (C#) ASP.NET MVC Application

Validation DSL : Boo public class User { public string Username { get; set; } public string Password { get; set; } }

Validation DSL : Boo rule_for "User": validate def(x): results = create_results() if string.IsNullOrEmpty(x.Username) return results

Validation DSL : Boo Boo.Lang.Interpreter. InteractiveInterpreter _interpreter; Func> createResults = (() => new List()); _interpreter.SetValue("create_results", createResults);

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

Object2RSS DSL : Boo

Rules (Boo) Model (C#) ASP.NET MVC Application

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

}

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

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

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

A BDD framework for .NET and Mono

SPECTER FRAMEWORK

MiniBar specification tutorial

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

MiniBar specification tutorial import Specter.Framework import Bender context "At Bender's bar": _bar as duck [NUnit.Framework.TestFixture] setup: 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()

MiniBar specification tutorial [NUnit.Framework.SetUp] import Specter.Framework import Bender def SetUp(): context "At Bender's bar": _bar as duck subject _bar = setup: Bender.MiniBar() 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()

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] specify "If I drink more than ten beers then I get drunk": for i in range(10): def BarDrinkOneBeerMustNotThrow(): _bar.DrinkOneBeer() { _bar.DrinkOneBeer() }.Must.Throw() Assert.DoesNotThrow(_bar. DrinkOneBeer())

MiniBar specification tutorial [NUnit.Framework.Test]

import Specter.Framework def IfIDrink5BeersThenIOwe5Bucks(): import Bender for i in range(5): context "At Bender's bar": _bar.DrinkOneBeer() _bar as duck Int32MustModule.Must(_bar.Balance, “Bar setup: subject _bar = Bender.MiniBar() balance must equal -5").Equal(-5) 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()

MiniBar specification tutorial import Specter.Framework [NUnit.Framework.Test] import Bender IfiDrinkMoreThanTenBeersThenIGetDrunk() : contextdef "At Bender's bar": _bar as duck for i in range(10): setup: subject _bar = Bender.MiniBar() _bar.DrinkOneBeer() specify { _bar.DrinkOneBeer() }.Must.Not.Throw() Assert.Throws((typeof(InvalidOperationExc specify "If I drink 5 beers then I owe 5 bucks": for i in range(5): eption), _bar.DrinkOneBeer()) _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()

MiniBar specification tutorial

MiniBar specification tutorial namespace Bender class MiniBar: def DrinkOneBeer(): pass [getter(“Balance”)+ _balance = 0

MiniBar specification tutorial

MiniBar specification tutorial namespace Bender class MiniBar: def DrinkOneBeer(): _balance-if _balance < -10: raise System.Exception("i'm drunk") [getter(“Balance”)+ _balance = 0

MiniBar specification tutorial

thx.

Need more? • http://www.microsoft.com/soa/products/oslo.aspx • http://www.jetbrains.com/mps/index.html • http://blogs.msdn.com/wesdyer/archive/2008/01/11/themarvels-of-monads.aspx • http://www.google.com/

Resources • http://www.codemagazine.com/article.aspx?quickid=0902041 &page=1 (Building Domain Specific Languages in C#) • http://bradfordcross.blogspot.com/2009/01/e xternal-dsl-vs-internal-dsl-smackdown_24.html (External DSL vs. Internal DSL Smack Down)

Resources • http://www.paulgraham.com/ • http://martinfowler.com/ • http://en.wikipedia.org/wiki/Domainspecific_programming_language • http://msdn.microsoft.com/enus/library/bb126235.aspx

Related Documents

Clr
November 2019 44
Colamate Dsls
April 2020 16
Clr Integration
June 2020 14

More Documents from ""