Logical Student Language
#lang lsl | package: lsl-lib |
The Logical Student Language (LSL) is a teaching language that extends the Intermediate Student Language (ISL) from How to Design Programs with features that support formally reasoning about programs. In particular, LSL tightly integrates contracts and property-based randomized testing.
1 Inherited from ISL
2 Extended from ISL
syntax
(define-struct structure-name (field-name ...))
make-structure-name : takes a number of arguments equal to the number of fields in the structure, and creates a new instance of that structure.
structure-name-field-name : takes an instance of the structure and returns the value in the field named by field-name.
set-structure-name-field-name! : takes an instance of the structure and a value, and sets the field named by field-name in the given instance of the structure to the given value.
structure-name? : takes any value, and returns #t if the value is an instance of the structure.
The name of the new functions introduced by define-struct must not be the same as that of other functions or variables, otherwise define-struct reports an error.
(define-struct posn [x y]) (define a-posn (make-posn 0 0)) (set-posn-x! a-posn 1) (posn-x a-posn) 1
syntax
(begin expr0 ... expr)
syntax
(set! id expr)
3 Testing
syntax
(test-suite "name" ...)
4 Contracts
syntax
(define-contract id contract)
(define-contract IntToInt (-> Integer Integer))
syntax
(: id contract)
(: on-two-and-three (-> (-> Integer Integer) Integer))
(define (on-two-and-three g) (+ (g 2) (g 3)))
Uses of on-two-and-three are protected:
> (on-two-and-three (lambda (x) (* x x))) 13
> (on-two-and-three (lambda (x) (/ x 2))) on-two-and-three: contract violation
expected: Integer
given: 3/2
blaming: program (as client)
4.1 Primitives
syntax
(Immediate clause ...)
(define-contract Even (Immediate (check (lambda (x) (and (integer? x) (even? x)))) (generate (lambda (fuel) (* 2 (random (add1 fuel))))) (shrink (lambda (fuel x) (let ([y (/ x 2)]) (if (even? y) y (sub1 y)))))))
syntax
(check predicate-expr)
The predicate-expr is expected to produce a predicate that determines if a value satisfies the contract.
syntax
(generate gen-expr)
The gen-expr should evaluate to a function that takes a single natural number, the fuel, and produces a value that satisfies the contract. Fuel provides a rough measure of how hard the generator is willing to work to produce a value.
syntax
(shrink shrink-expr)
The shrink-expr function takes two arguments, fuel and a value to shrink. The value to shrink is guaranteed to satisfy the contract.
syntax
(Function clause ...)
(: double (Function (arguments [x Integer]) (result (lambda (y) (= x y)))))
(define (double x) (* 2 x))
> (double 0) 0
> (double 1) double: contract violation
expected: (lambda (y) (= x y))
given: 2
blaming: program (as server)
syntax
(arguments [id contract] ...)
Describes the contracts on arguments. Any id can be used in any contract and will be bound to the concrete argument when the function is applied, so long as there is no cyclic dependency.
syntax
(result contract)
Describes the result contract. All id values are available in contract.
syntax
(raises exn-id)
Permits the exn-id struct to be raised in the function. Such exceptions are not considered failures during property-based testing.
syntax
(List contract)
> (contract-generate (List Integer)) '(-17 26)
syntax
(Tuple contract ...)
> (contract-generate (Tuple Boolean Integer)) '(#t 11)
syntax
(OneOf contract ...)
> (contract-generate (List (OneOf Boolean Integer)))
'(8
#t
-11
#f
16
2
1
#t
28
-27
-20
#t
#f
#t
-10
#t
#f
#f
#t
#t
#t
-26
22
22
#f
-26
4
25
#f
#t
29
#f
#f
#f
#t
#f
#t
16
#t
#t
#f
#f
48
40
#t)
syntax
(AllOf contract ...)
> (define-contract UnitInterval (AllOf Real (lambda (x) (< 0 x)) (lambda (x) (< x 1)))) > (: reciprocal (-> UnitInterval Real)) > (define (reciprocal x) (/ 1 x)) > (reciprocal 1/2) 2
> (reciprocal -2) reciprocal: contract violation
expected: (lambda (x) (< 0 x))
given: -2
blaming: program (as client)
> (reciprocal 2) reciprocal: contract violation
expected: (lambda (x) (< x 1))
given: 2
blaming: program (as client)
syntax
(Struct struct-id (field-contract ...))
> (define-struct my-point (x y)) > (contract-generate (Struct my-point [Integer Integer])) (make-my-point -37 -25)
syntax
(All (id ...) contract)
syntax
(Exists (id ...) contract)
> (: counter-pkg (Exists (A) (Tuple (-> A) (-> A A) (-> A Integer))))
> (define counter-pkg (list (λ () 0) (λ (x) (+ x 1)) (λ (x) x))) > (define make-counter (first counter-pkg)) > (define counter-incr (second counter-pkg)) > (define counter-get (third counter-pkg)) > (counter-get (counter-incr (make-counter))) 1
4.2 Derived
syntax
syntax
syntax
syntax
syntax
syntax
syntax
syntax
syntax
syntax
(Constant expr)
syntax
(Maybe contract)
syntax
(-> arg-contract ... result-contract)
syntax
(Record maybe-folding trace-id)
maybe-folder =
| folding-expr
The name of this contract rhymes with sword since the contract records values in a trace.
> (: down-prev (Maybe Natural)) > (define down-prev #f) > (: down-folder (-> (Maybe Natural) Natural Natural))
> (define (down-folder prev cur) (cond [(equal? prev cur) #f] [else cur])) > (: down (-> (AllOf Natural (Record down-folder down-prev))))
> (define down (let ([x 3]) (lambda () (begin (set! x (if (< x 1) 0 (sub1 x))) x)))) > (down) 2
> (down) 1
> (down) 0
> (down) down-folder: contract violation
expected: Natural
given: #f
blaming: program (as server)
> (define-contract Increasing (AllOf (List Real) (lambda (xs) (equal? (sort xs <) xs)))) > (: up-results Increasing) > (define up-results '()) > (: up (-> (AllOf Natural (Record up-results))))
> (define up (let ([x 0]) (lambda () (begin (set! x (modulo (add1 x) 4)) x)))) > (up) 1
> (up) 2
> (up) 3
> (up) contract violation
expected: (lambda (xs) (equal? (sort xs <) xs))
given: '(1 2 3 0)
5 Property-Based Randomized Testing
syntax
(check-contract id maybe-attempts)
maybe-attempts =
| nat-expr
> (: bigger-even (-> Integer Even))
> (define (bigger-even x) (* 3 x)) > (check-contract bigger-even 20)
--------------------
FAILURE
params: '(#<procedure:.../syntax/compile.rkt:183:7> 20)
name: check-contract
location: eval:56:0
discovered a counterexample
counterexample: (bigger-even -1)
error:
bigger-even: contract violation
expected: Even
given: -3
blaming: program (as server)
--------------------
syntax
(contract-generate contract maybe-fuel)
maybe-attempts =
| nat-expr
> (contract-generate Even) 88
6 State Machines
procedure
(machine-accepting? x) → boolean?
x : any/c
procedure
(machine-accepts? m l) → boolean?
m : machine? l : list?
procedure
(machine-next m x) → machine?
m : machine? x : any/c
6.1 DFAs
syntax
(dfa start (end ...) [state ([in next-state] ...)] ...)
> (define M (dfa s1 (s1) [s1 ([0 s2] [2 s1])] [s2 ([0 s1] [2 s2])])) > (machine-accepts? M (list 2 0 2 0 2)) #t
> (machine-accepts? M (list 0 2 0 2 0)) #f
6.2 Regular Expressions
syntax
(re re-form)
> (define R (re (star (seq 'a 'b)))) > (machine-accepts? R (list 'a 'b 'a 'b)) #t
> (machine-accepts? R (list 'a 'b 'a)) #f
A literal value recognizes itself.
syntax
(complement re)
Recognizes anything that is not recognized not re.
syntax
(seq re ...)
Recognizes a sequence of regular expressions.
syntax
(seq-prefix re ...)
Recognizes prefixes of the given regular expression. In other words, (seq-prefix x y z) is the same as (union (epsilon) x (seq x y) (seq x y z)).
syntax
(union re ...)
Recognizes any of the given re.
syntax
(star re)
Recognizes zero or more repetitions of re.
syntax
(epsilon)
Recognizes the empty list.
7 Resources
Outputs a visualization graphing how much time f takes for each of the given args.
8 Concurrency
Concurrency is not the same as parallelism. In parallelism, computations occur simultaneously, not just in an arbitrary order.
An actor system consists of a number of actors (or processes) that are identified by a unique name. Actors send immutable values (or messages) to one another. These messages are then processed by the receiving actor. Messages are guaranteed to be delivered in FIFO (first-in-first-out) between two actors. However, the order in which messages are delivered among all actors is unspecified. When starting an actor system, a scheduler can be provided to fix some delivery policy.
syntax
(Packet contract)
procedure
(packet-from p) → String
p : (Packet Any)
procedure
(packet-msg sp) → Any
sp : (Packet Any)
syntax
(SendPacket contract)
(: sp (SendPacket Natural)) (define sp (send-packet "process-1" 42))
procedure
(send-packet to msg) → (SendPacket Any)
to : string? msg : any/c
procedure
(send-packet-to sp) → String
sp : (SendPacket Any)
procedure
(send-packet-msg sp) → Any
sp : (SendPacket Any)
procedure
(send-packet? x) → Boolean
x : Any
syntax
(ReceivePacket contract)
(: rp (ReceivePacket Natural)) (define rp (receive-packet "process-1" 42))
procedure
(receive-packet from msg) → (ReceivePacket Any)
from : string? msg : any/c
procedure
(receive-packet-from rp) → String
rp : (ReceivePacket Any)
procedure
(receive-packet-msg rp) → Any
rp : (ReceivePacket Any)
procedure
(receive-packet? x) → Boolean
x : Any
syntax
(Action contract)
(: a (Action Natural))
(define a (action 5 (list (send-packet "process-1" 42) (send-packet "process-1" 43) (send-packet "process-2" "Hello!"))))
syntax
syntax
(process clause ...)
clause = (name s) | (on-start handler) | (on-receive handler)
syntax
s : String Names a process. This needs to be unique across all processes in a system.
The handler function is applied to the list of all the other processes (as strings) when the process starts. The result of the call is an action, containing the new state of the process and a list of packets to send to other processes.
syntax
handler : (-> contract ReceivePacket (Action contract)) The handler function is applied to the process’s state and a packet when the process receives a new packet. The result of the call is an action, containing the new state of the process and a list of packets to send to other processes.
procedure
(start-debug scheduler processes) → (List (Tuple String Any))
scheduler : (-> (List (Packet Any)) (Packet Any)) processes : (List Process)
procedure
(start-gui scheduler processes summarize-state) → (List (Tuple String Any)) scheduler : (-> (List (Packet Any)) (Packet Any)) processes : (List Process) summarize-state : (-> Any Any)
The extra argument, summarize-state, is a function that is applied to the process state before it is displayed, since large process states can make the interface harder to use. The identity function can be provided if you want the entire process state visible.