a shallow embedded, type safe extendable dsl for the …people.inf.elte.hu/cefp/cefp15pieter.pdf ·...
TRANSCRIPT
A Shallow Embedded, Type Safe Extendable DSL �for the Arduino
Pieter Koopman
what is an Arduino?
§ open source microprocessor boardØ 8-bit ATMega328Ø 16 MHzØ 32 KB flash memoryØ 2 KB RAMØ big input / output �
connectors (14D, 6A)Ø USB portØ boot loaderØ 1 on-board LEDØ cheap
CEFP15: DSL for Arduino
2
why an Arduino
§ very suited to handle sensors and actuatorsØ sensors: buttons, temperature, heart rate, GPS, ..Ø actuators: lights, motors, robots, LCD, relay, .. Ø many shields available• LCD, relay, GPS, keypad, ..• shields can be stacked• easy experimentation
§ hence very suited for�simple control tasksØ integrate this with iTasksØ IoT Internet of Things
CEFP15: DSL for Arduino
3
software on the Arduino
§ no multitasking / no threads§ no operating system, just a tiny boot-loader§ your program has to do everythingØ setup: initialisationØ loop: repeated ever after the setup§ programming language(s) for ArduinoØ C for all basic thingsØ C++ like objects to control shields
CEFP15: DSL for Arduino
4
'hello world' for Arduino
§ blink the LEDboolean ledOn = false;
void setup() { pinMode(13, OUTPUT); digitalWrite(13, LOW); }
void loop() { ledOn = not ledOn; digitalWrite(13, ledOn); delay(500); }
CEFP15: DSL for Arduino
5
busy-wait loop
L L entire Arduino is sleeping
better 'hello world' for Arduino
§ blink the LEDboolean ledOn = false; long lastTime = 0;
void setup() { pinMode(13, OUTPUT); digitalWrite(13, LOW); }
void loop() { if (millis() / 500 > lastTime) { ledOn = not ledOn; digitalWrite(13, ledOn); lastTime += 1; } }
CEFP15: DSL for Arduino
6
J no delay
servomotor
§ rotary actuator with control of angular positionØ duration of input pulse controls position �
PWM: pulse width modulationØ Arduino ports can generate PWM signals
CEFP15: DSL for Arduino
7
servo sweep program
#include <Servo.h> // include library
Servo s; // create servo object
int pos = 10; // servo position int step = 1; // angle change long time = 0; // time of last change
void setup() { s.attach( A5 ); // attach servo on pin A5 to object } void loop() { if (millis() / 25 > time) { // 40 step per second time += 1; pos += step; if (pos > 170 || pos < 10) { // outside preferred angle region? step = -‐step; // turn the direction of the servo pos += step; } s.write( pos ); // set servo angle in degrees } }
CEFP15: DSL for Arduino
8 easy to combine with blink program
why a Domain Specific Language ?
§ the Arduino is very suited for low level I/OØ we need this kind of control in iTask, e.g. IoT
§ running Clean on the Arduino is not feasible§ programming the Arduino in C and interfacing it with iTask is tedious, error-prone detention work§ Embedded Domain Specific Language bridge the gapØ single source of programØ strong typingØ extendable DSL new shields without recompilingØ complete DSL control the parts of Clean inheritedØ multiple views compile, simulate, analyse, ..
CEFP15: DSL for Arduino
9
implementing a DSL 1
§ a library with function and data typesØ shallow embedding of DSLØ like the iTask system: a DSL for task oriented programs§ Clean with EDSL program must run on Arduino§ this requires Clean on the ArduinoØ this cannot workØ 32KB flash, 2KB ram, 8 bit, 16 MHz
Ø ✖
CEFP15: DSL for Arduino
10
implementing a DSL 2
§ a library with function and data typesØ like the iTask system: a DSL for task oriented programs
§ running Clean with EDSL program on PC, �remote control of ArduinoØ e.g. Firmata protocol, like hArduinoØ can work
§ requires much interaction between PC and ArduinoØ we want independent tasks at the Arduino
Ø ✖
CEFP15: DSL for Arduino
11
implementing a DSL 3
§ EDSL should have clear borderØ DSL does not inherit everything from Clean
§ DSL is an algebraic data type, deep embedding:: Expr = Num Int | Bool Bool | Add Expr Expr | ..
§ this does not prevent runtime type errors in DSLp = Add (Int 7) (Bool False)
Ø we want a strongly typed DSL
Ø ✖
CEFP15: DSL for Arduino
12
implementing a DSL 4
§ EDSL should have clear borderØ DSL does not inherit everything from Clean
§ DSL is an generalized algebraic data type, GADT:: Expr a = Lit a | Add (BM a Int) (Expr Int) (Expr Int) |..
§ this prevents runtime type errors in DSLp = Add bm (Lit 7) (Lit False) // this is rejected by the compilerq = Add bm (Lit 7) (Lit 36)
Ø this is strongly typed DSL,Ø but the DSL cannot be extended without recompilation
Ø ✖
CEFP15: DSL for Arduino
13
implementing a DSL 5
§ EDSL should have clear borderØ DSL does not inherit everything from Clean
§ DSL is set of type constructor classesØ strongly typed, have a border, have multiple views
class expr v where lit :: t -‐> v t add :: (v Int) (v Int) -‐> v Int p :: a Int | expr x p = add (lit 7) (lit 36)
§ this solves our problems
Ø ✔
CEFP15: DSL for Arduino
14
Show view of this DSL
§ the DSLclass expr v where lit :: t -‐> v t | toString t add :: (v Int) (v Int) -‐> v Int
§ each view is an instance of this classØ e.g. show = conversion to list of strings
:: Show a = S [String]
instance expr Show where lit a = S [toString a] add (S x) (S y) = S (["("] ++ x ++ ["+"] ++ y ++ [")"])
CEFP15: DSL for Arduino
15
we do not use argument a,but it must be there
Show view of this DSL 2
class expr v where lit :: t -‐> v t | toString t add :: (v Int) (v Int) -‐> v Int
:: Show a = S [String]
instance expr Show where lit a = S [toString a] add (S x) (S y) = S (["("] ++ x ++ ["+"] ++ y ++ [")"])
Ø a DSL programp :: (v Int) | expr v p = add (lit 7) (lit 36)
Ø evaluating this program in the Show viewStart = strings where (S strings) = p
CEFP15: DSL for Arduino
16
this yields���["(","7","+","36",")"]
Evaluation view of this DSL
§ the DSLclass expr v where lit :: t -‐> v t | toString t add :: (v Int) (v Int) -‐> v Int
§ each view is an instance of this classØ e.g. eval = conversion to single value
:: Eval a = E a
instance expr Eval where lit a = E a add (E x) (E y) = E (x + y)
CEFP15: DSL for Arduino
17
here we use argument a
Eval view of this DSL 2
class expr v where lit :: t -‐> v t | toString t add :: (v Int) (v Int) -‐> v Int
:: Eval a = E a
instance expr Eval where lit a = E a add (E x) (E y) = E (x + y)
Ø a DSL programp :: (v Int) | expr v p = add (lit 7) (lit 36)
Ø evaluating this program in the both viewsStart = (val, strings) where (E val) = p (S strings) = p
CEFP15: DSL for Arduino
18
this yields���(43,["(","7","+","36",")"])
extending the DSL
§ leave class expr untouched, define a new classØ we do not break existing code and views
class expr v where lit :: t -‐> v t | toString t add :: (v Int) (v Int) -‐> v Int
class expr2 v where And :: (v Bool) (v Bool) -‐> v Bool Not :: (v Bool) -‐> v Bool equ :: (v a) (v a) -‐> v Bool | ==, toString a
CEFP15: DSL for Arduino
19
unchanged
extending the DSL 2: views
§ make instances of Show and Eval for expr2 :: Show a = S [String] // unchangedinstance expr2 Show where And (S x) (S y) = S (["("] ++ x ++ ["&&"] ++ y ++ [")"]) Not (S x) = S (["(Not"] ++ x ++ [")"]) equ (S x) (S y) = S (["("] ++ x ++ ["=="] ++ y ++ [")"])
:: Eval a = E a // unchanged instance expr2 Eval where And (E x) (E y) = E (x && y) Not (E x) = E (not x) equ (E x) (E y) = E (x == y)
q :: (v Bool) | expr, expr2 v q = Not (equ p (lit 42))
CEFP15: DSL for Arduino
20
execution yields������(True, ["(Not","(","(","7","+", "36", ")","==","42",")",")"])
borders of DSL
§ the classes expr and expr2 are our DSLclass dsl t | expr, expr2 t
§ there is a clear difference betweenØ 7, it has type Int Ø lit 7, it has type v Int | expr v § this is exactly what we wantØ we can make a view for dsl, �
without having to implement it for everything in Clean
CEFP15: DSL for Arduino
21
strong typing
§ the Clean type system detects statically any type error in our DSL
Ø for instance adding an integer and a Boolean �err = add (lit 7) (lit True) Ø Type error :"argument lit" cannot unify types: Int Bool
Ø comparing an integer and a Boolean err2 = equ (lit 7) (lit True) Ø Type error :"argument lit" cannot unify types: Bool Int
§ this prevents runtime type errors in our DSL
CEFP15: DSL for Arduino
22
show in combinators
§ hide the implementation of show a little:: Show a = S [String]
(+.+) infixr 5 :: (Show a) (Show b) -‐> Show c (+.+) (S a) (S b) = S (a ++ b)
brac :: (Show a) -‐> Show b brac s = S ["("] +.+ s +.+ S [")"]
§ the view for our DSL becomesinstance expr Show where lit a = S [toString a] add x y = brac (x +.+ S ["+"] +.+ y)
instance expr2 Show where And x y = brac (x +.+ S ["&&"] +.+ y) Not x = brac (S ["Not"] +.+ x) equ x y = brac (x +.+ S ["=="] +.+ y)
CEFP15: DSL for Arduino
23
why are these type arguments
different?
variables in our DSL
§ simple approachØ state = list of valuesØ dynamic to put different type of variables in one list
:: State :== [Dyn] :: Eval a = E (State -‐> (a, State))
§ monad(>>==) infixl 1 :: (Eval a) (a -‐> Eval b) -‐> Eval b (>>==) (E f) g = E \s. let (a,s2) = f s; (E h) = g a in h s2
rtrn :: a -‐> Eval a rtrn a = E \s -‐> (a,s)
CEFP15: DSL for Arduino
24
variables in our DSL 2
§ update eval view of our DSL
instance expr Eval where lit a = rtrn a add x y = x >>== \a. y >>== \b. rtrn (a + b)
instance expr2 Eval where And x y = x >>== \a. y >>== \b. rtrn (a && b) Not x = x >>== \a. rtrn (not a) equ x y = x >>== \a. y >>== \b. rtrn (a == b)
CEFP15: DSL for Arduino
25
variables in our DSL 3
§ add variables to our DSLØ use index in list as identification
class var v where var :: Int -‐> v a | dyn a
instance var Show where var v = S ["v" + toString v]
instance var Eval where var v = E \s.(fromJust (fromDyn (s !! v)), s)
§ examplep2 :: (v Int) | expr, var v p2 = add (lit 7) (var 1)
CEFP15: DSL for Arduino
26
limitations of these variables
§ type system cannot check indicesØ index can be wrong
p3 :: (v Int) | expr, var v p3 = add (lit 7) (var -‐1)
Ø types of variable can be inconsistent
p4 :: (v Bool) | expr, var v p4 = And (equ (lit 7) (var 1)) (var 1)
§ it is dangerous when the user handles indicesØ let the system generate them
CEFP15: DSL for Arduino
27
Bool Int
better variables
§ DSL implementation assigns numbersØ expression is function that accepts a variable
class var v where var :: t ((v t)-‐>v t) -‐> v t | dyn, toString t
Ø applicationp2 :: (v Int) | expr, var v p2 = var 3 \x.add (lit 7) x
Ø implementing viewsinstance var Show where var v f = name +.+ S [" = " + toString v + " in "] +.+ f name where name = S ["v"]
instance var Eval where var v f = E (\s.(length s, s ++ [toDyn v])) >>==
\n-‐>f (E \s.(fromJust (fromDyn (s !! n)), s))
CEFP15: DSL for Arduino
28
fresh name required
this yields������(10, "v = 3 in (7 + v)")
variables are special
§ suppose we add an assignmentassign x (add x (lit 1)) or x = x + 1 Ø variables are allowed on the left-hand side�
array selection might be addedØ lit 1 is not allowed on the left-hand side, �
our type system should prevent that§ add an additional type to control accessØ Read: read-only expressionsØ Write: read and write access
:: Read = Read :: Write = Write
§ add these types to every class in the DSL
CEFP15: DSL for Arduino
29
new type classes
class expr v where lit :: t -‐> v t Read | toString t add :: (v Int p) (v Int q) -‐> v Int Read
class expr2 v where And :: (v Bool p) (v Bool q) -‐> v Bool Read Not :: (v Bool p) -‐> v Bool Read equ :: (v a p) (v a q) -‐> v Bool Read | ==, toString a
§ new variable classclass var v where var :: t ((v t Write)-‐>v t p) -‐> v t p | dyn, toString t
§ views get also an additional argument:: Show a p = S [String]
CEFP15: DSL for Arduino
30
sequencing expressions
§ once we have a global state it make sense to sequence expressionsØ first expression can change stateØ second expression is executed with that state
class sequ v where sequ :: (v a p) (v b q) -‐> v b q
instance sequ Show where sequ x y = S ["sequ"] +.+ x +.+ y
instance sequ Eval where sequ x y = x >>== \_. y
CEFP15: DSL for Arduino
31
assignment: change the state
assign x (add x (lit 1)) or x = x + 1
§ how to distinguish x and x?§ the assign function knows the contextØ tell it to the Eval view
:: RW a = R | W a :: Eval a p = E ((RW a) State -‐> (a, State))
CEFP15: DSL for Arduino
32
read write
the assignment class
readVar :: Var (RW a) [Dyn] -‐> (a, [Dyn]) | dyn a readVar n R s = (fromJust (fromDyn (s !! n)), s) readVar n (W a) s = (a, updateAt n (toDyn a) s)
class var v where var :: t ((v t Write)-‐>v t p) -‐> v t p | dyn, toString t
instance var Eval where var v f = E (\r s.(length s, s ++ [toDyn v])) >>== \n. f (E (readVar n))
class assign v where assign :: (v t Write) (v t p) -‐> v t q | dyn, toString t
instance assign Eval where assign (E v) e = e >>== \a. E \r s.v (W a) s
CEFP15: DSL for Arduino
33
using this
class dsl t | expr, expr2, var, assign, sequ t
p3 :: (v Int Read) | dsl v p3 = var 15 \n. sequ (assign n (add n (lit 6))) (add n n)
Ø showing p3 yields v = 15 in sequ (v = (v + 6)) (v + v)
Ø evaluating p3 yields 42
§ compiler rejects any improper assignment§ we cannot use undefined or ill typed variables
Ø ✔
CEFP15: DSL for Arduino
34
how to write C++ code in Clean back to a DSL for the Arduino
CEFP15: DSL for Arduino
35
design of the DSL
§ we need a state to store informationØ next iteration of loop needs to know what we are doingØ hence we add an assignment to fields in the state§ we need to control heap usage, no recursive types§ use shallow embedding for easy extension of DSLØ language is based on functions
§ use classes to allow multiple viewsØ one instance for each viewØ currently two views: compile + simulate in iTask
CEFP15: DSL for Arduino
36
DSL constants and operators
§ each DSL element is a type constructor classØ argument v: the view
§ this view v has two argumentsØ t: the type of this constructØ p: Read / Write behaviour of this construct
class lit v where lit :: t -‐> v t Read | toString, type, tocode t class arith v where (+.) infixl 6 :: (v t p) (v t q) -‐> v t Read | type, + t (-‐.) infixl 6 :: (v t p) (v t q) -‐> v t Read | type, -‐ t class eq v where (=.=) infix 4 :: (v t p) (v t q) -‐> v Bool Read | type, Eq t class logical v where ~. :: (v Bool p) -‐> v Bool p
CEFP15: DSL for Arduino
37
assignment and variables
class assign v where (=.) infixr 1 :: (v t Write) (v t q) -‐> v t Read | type t
§ variable has same type as value written§ variable must be writable (no assignment to a lit )class bind v where (>==) infixr 0 :: (v t p) ((v t Read)-‐>(v u q)) -‐> (v u q) | type, type2code t & type u
class varDef v where int :: ((v Int Write) -‐> ARDSL (v t p) (v u q)) -‐> ARDSL (v t p) (v u q) :: ArDSL a b = {setup :: a, loop :: b}
CEFP15: DSL for Arduino
38
'Hello world' in ARDSL
helloworld = boolean \ledOn. int \lastTime. ardsl { setup = pinmode D13 OUTPUT :. digitalWrite D13 (lit False) , loop = If (millis /. lit 500 >. lastTime) ( ledOn =. ~. ledOn >== \b. digitalWrite D13 b :. lastTime =. lastTime +. lit 1 ) }
CEFP15: DSL for Arduino
39
define state variables
enumeration i.s.o. integer
operators have extra .
servo sweep in ARDSLservosweep =
servo \s. int \pos. int \step. long \time. ardsl { setup = pos =. lit 10 :. step =. lit 1 :. attach s A5 , loop = If (millis /. lit 25 >. time) ( time =. time +. lit 1 :. pos =. pos +. step :. If (pos >. lit 170 |. pos <. lit 10) ( step =. lit 0 -‐. step ) :. writeS s pos ) }
CEFP15: DSL for Arduino
40
"Hello world" and ticks on LCD
�testLCD = int \n. liquidCrystal [] \lcd. // ARDSL knows default pins ardsl { setup = begin lcd (lit 16) (lit 2) :. print lcd (lit "hello world") ,loop = setCursor lcd (lit 1) (lit 1) :. n =. n +. lit 1 :.
setCursor lcd (lit 0) (lit 1) :. print lcd n :. delay (lit 1000) // better check the time }
CEFP15: DSL for Arduino
41
adding a LCD shield
:: LCD
class lcd v where begin :: (v LCD r) (v Int p) (v Int q) -‐> v Int Read print :: (v LCD r) (v t p) -‐> v Int Read | printCode t setCursor :: (v LCD r) (v Int p) (v Int q) -‐> v Void Read
liquidCrystal :: [DigitalPin] ((v LCD Read) -‐> ARDSL (v t p) (v u q)) -‐> ARDSL (v t p) (v u q)
CEFP15: DSL for Arduino
42
abstract type
methods of C++ class LCD
no assignment to this variable
compiling ARDSL
§ an instance of the classes that emits C-code§ use Arduino IDE to compile and load this code :: Code t p = Code (CompState -‐> CompState) :: *CompState = { gcode :: *File // code to this file , idNum :: Int // fresh identifiers , indnt :: Int // indentation }
CEFP15: DSL for Arduino
43
type arguments are not used here
compiling ARDSL to Arduino
§ C++ as intermediate language
CEFP15: DSL for Arduino
44
ARDSL
C++
compiling: basic cases
instance lit Code where lit x = toCode x
toCode :: t -‐> Code b p | tocode t toCode a = addCode (tocode a)
class tocode t :: t -‐> String instance tocode Int where tocode x = toString x instance tocode Bool where tocode b | b = "true"; = "false" instance arith Code where (+.) x y = codeOp2 x " + " y (-‐.) x y = codeOp2 x " -‐ " y
codeOp2 :: (Code t p) String (Code u q) -‐> Code c r codeOp2 x n y = brackets (x +.+ addCode n +.+ y)
CEFP15: DSL for Arduino
45
compiling: bind
§ make a new variable and let Clean substitute itclass bind v where (>==) infixr 0 :: (v t p) ((v t Read)-‐>(v u q)) -‐> (v u q) | type, type2code t & type u
instance bind Code where (>==) x f = genName \var. type2code x +.+ addCode var +.+ addCode " = " +.+ x +.+ addCode ";" +.+ nl +.+ f (addCode var)
CEFP15: DSL for Arduino
46
compiling: variable definition
§ make a new variable and let Clean substitute itclass varDef v where int :: ((v Int Write) -‐> ARDSL (v t p) (v u q)) -‐> ARDSL (v t p) (v u q) instance varDef Code where int f = {ardsl & defs = [def: ardsl.defs]} where ardsl = f (addCode name) name = newName ardsl
def = {name = name, type = "long", args = ""}
CEFP15: DSL for Arduino
47
next view: simulate DSL as iTask
§ evaluation in iTask simulatorØ executes loop in each step
:: Eval a p = Eval ((RW a) State -‐> (a, State)) :: State = { vars :: [(String, Dyn)] , dpins :: [(DigitalPin, Bool)] , apins :: [(AnalogPin, Int)] , time :: Int } simulate :: (ARDSL (Eval a p) (Eval b q)) -‐> Task Void
CEFP15: DSL for Arduino
48
to do / future work
§ more complex types in DSL, e.g. array§ user defined functions§ more shields§ fancy simulation of shields§ step-by-step simulation§ serial communication§ integration with iTaskØ serial communication: message basedØ iTask: task communicate by changes in shared state§ ..
CEFP15: DSL for Arduino
49
achieved
§ type safe extendable DSL for the ArduinoØ Clean checks types for the DSLØ DSL uses only type-safe and properly defined variables Ø DSL does not inherit Clean as a side-effect,• Clean is just the macro layer for ARDSL
Ø we can add new constructs one-by-oneØ we can add new views one-by-one• occasionally a minor change: add a class restriction
§ (almost) solves Wadler's expression problem ['98]Ø The goal is to define a datatype by cases, where one can add new cases to the datatype and new functions over the datatype, without recompiling existing code, and while retaining static type safety
CEFP15: DSL for Arduino
50
exercises
1. "Hello CEFP" on lcd (8,9,4,5,6,7) from Arduino IDEØ connect Arduino, start IDE, select port and UNO, �
write program, check program, upload it2. "Hello CEFP" on lcd from ARDSLØ generate C++ from ARDSL, copy this to Arduino IDE
3. print the key pressed on the lcdØ key represented by voltage on A0, web: arduino.ccØ use ���keySwitch from lcd.dcl
4. make a clock on the lcd, keys to adjust time5. add a += operator to ARDSL6. add read and write to serial port to ARDSL
CEFP15: DSL for Arduino
51
hello CEFP
#include <LiquidCrystal.h>
LiquidCrystal lcd(8,9,4,5,6,7); int ticks;
void setup() { lcd.begin(16, 2); lcd.print("Hello CEFP"); }
void loop() { if (millis() / 500 > ticks) { lcd.setCursor(0,1); lcd.print(ticks); ticks += 1; } }
CEFP15: DSL for Arduino
52
on your own computer
§ download Arduino IDE: arduino.cc§ special driver for this ArduinoØ see
http://miscky.com/product/cheapest-micro-b-usb-arduino-uno/
Ø CH340 micro-USB driver at (in Chinese): http://www.wch.cn/downloads.php?name=pro&proid=5
CEFP15: DSL for Arduino
53