Racket: Modules, Contracts, Languages

Similar documents
Racket: Macros. Advanced Functional Programming. Jean-Noël Monette. November 2013

Syntax Color: Utilities

Parsing Scheme (+ (* 2 3) 1) * 1

Syntax: Meta-Programming Helpers

Scheme in Scheme: The Metacircular Evaluator Eval and Apply

Programming Languages: Application and Interpretation

Typed Racket: Racket with Static Types

Summer 2017 Discussion 10: July 25, Introduction. 2 Primitives and Define

Typed Scheme: Scheme with Static Types

Essentials of Programming Languages Language

Essentials of Programming Languages Language

Functional Programming. Pure Functional Languages

Vectors in Scheme. Data Structures in Scheme

Programming Languages: Application and Interpretation

Functional Programming. Pure Functional Languages

Streams, Delayed Evaluation and a Normal Order Interpreter. CS 550 Programming Languages Jeremy Johnson

How to Design Programs

Redex: Practical Semantics Engineering

6.184 Lecture 4. Interpretation. Tweaked by Ben Vandiver Compiled by Mike Phillips Original material by Eric Grimson

CS 360 Programming Languages Interpreters

An Introduction to Scheme

LECTURE 16. Functional Programming

Why do we need an interpreter? SICP Interpretation part 1. Role of each part of the interpreter. 1. Arithmetic calculator.

The Typed Racket Guide

regsim.scm ~/umb/cs450/ch5.base/ 1 11/11/13

Computer Science 21b (Spring Term, 2015) Structure and Interpretation of Computer Programs. Lexical addressing

6.037 Lecture 4. Interpretation. What is an interpreter? Why do we need an interpreter? Stages of an interpreter. Role of each part of the interpreter

CS 314 Principles of Programming Languages

The Typed Racket Reference

Redex: Practical Semantics Engineering

Functional Programming. Pure Functional Programming

Mid-Term 2 Grades

The Typed Racket Guide

2D Syntax. Version October 30, 2017

Parser Tools: lex and yacc-style Parsing

Parser Tools: lex and yacc-style Parsing

Distributed Places. Version 6.3. Kevin Tew. November 20, (require racket/place/distributed)

Scheme Quick Reference

Introduction to Typed Racket. The plan: Racket Crash Course Typed Racket and PL Racket Differences with the text Some PL Racket Examples

Lecture 09: Data Abstraction ++ Parsing is the process of translating a sequence of characters (a string) into an abstract syntax tree.

Macro Debugger: Inspecting Macro Expansion

Scheme Quick Reference

CS61A Discussion Notes: Week 11: The Metacircular Evaluator By Greg Krimer, with slight modifications by Phoebus Chen (using notes from Todd Segal)

Programming Languages. Function-Closure Idioms. Adapted from Dan Grossman's PL class, U. of Washington

Administrative Stuff

Fall 2018 Discussion 8: October 24, 2018 Solutions. 1 Introduction. 2 Primitives

Project 2: Scheme Interpreter

Fall Semester, The Metacircular Evaluator. Today we shift perspective from that of a user of computer langugaes to that of a designer of

CSE 413 Midterm, May 6, 2011 Sample Solution Page 1 of 8

Data: Data Structures

SCHEME 8. 1 Introduction. 2 Primitives COMPUTER SCIENCE 61A. March 23, 2017

Documentation for LISP in BASIC

COP4020 Programming Languages. Functional Programming Prof. Robert van Engelen

Graphics: Legacy Library

State. Substitution relies on an identifer having a fxed value

FrTime: A Language for Reactive Programs

Principles of Programming Languages

Fall 2017 Discussion 7: October 25, 2017 Solutions. 1 Introduction. 2 Primitives

User-defined Functions. Conditional Expressions in Scheme

RSL Reference Manual

XML: Parsing and Writing

CS 342 Lecture 7 Syntax Abstraction By: Hridesh Rajan

Unstable. Version 5.1. February 14, This manual documents some of the libraries available in the unstable collection.

Introduction to Scheme

Introduction to Functional Programming in Racket. CS 550 Programming Languages Jeremy Johnson

Scheme: Expressions & Procedures

Data: Data Structures

UNIVERSITY OF TORONTO Faculty of Arts and Science. Midterm Sample Solutions CSC324H1 Duration: 50 minutes Instructor(s): David Liu.

Principles of Programming Languages Topic: Functional Programming Professor L. Thorne McCarty Spring 2003

Functional Data Structures for Typed Racket. Hari Prashanth and Sam Tobin-Hochstadt Northeastern University

FrTime: A Language for Reactive Programs

SRFIs: Libraries. Version August 10, 2015

File: Racket File Format Libraries

CSE413: Programming Languages and Implementation Racket structs Implementing languages with interpreters Implementing closures

Syntactic Sugar: Using the Metacircular Evaluator to Implement the Language You Want

Spring 2018 Discussion 7: March 21, Introduction. 2 Primitives

COP4020 Programming Assignment 1 - Spring 2011

CSE 341 Section Handout #6 Cheat Sheet

A brief tour of history

SCHEME 7. 1 Introduction. 2 Primitives COMPUTER SCIENCE 61A. October 29, 2015

SCHEME 10 COMPUTER SCIENCE 61A. July 26, Warm Up: Conditional Expressions. 1. What does Scheme print? scm> (if (or #t (/ 1 0)) 1 (/ 1 0))

Programming Languages

How to Design Programs Languages

CSE 413 Languages & Implementation. Hal Perkins Winter 2019 Structs, Implementing Languages (credits: Dan Grossman, CSE 341)

Name EID. (calc (parse '{+ {with {x {+ 5 5}} {with {y {- x 3}} {+ y y} } } z } ) )

Random Testing in 321

Why Macros? Scheme-Style Macros: Patterns and Lexical Scope. Macros versus Arbitrary Program Generators. Macros versus Arbitrary Program Generators

C311 Lab #3 Representation Independence: Representation Independent Interpreters

Scheme as implemented by Racket

CSE341 Autumn 2017, Final Examination December 12, 2017

CS115 - Module 10 - General Trees

Type Checking Binary Operators

Evaluating Scheme Expressions

An Explicit-Continuation Metacircular Evaluator

Racket. CSE341: Programming Languages Lecture 14 Introduction to Racket. Getting started. Racket vs. Scheme. Example.

CSE 341, Spring 2011, Final Examination 9 June Please do not turn the page until everyone is ready.

Local definitions and lexical scope

Local definitions and lexical scope. Local definitions. Motivating local definitions. s(s a)(s b)(s c), where s = (a + b + c)/2.

;;; Determines if e is a primitive by looking it up in the primitive environment. ;;; Define indentation and output routines for the output for

Implementing HtDP Teachpacks, Libraries, and Customized Teaching Languages

Transcription:

Racket: Modules, Contracts, Languages Advanced Functional Programming Jean-Noël Monette November 2013 1

Today Modules are the way to structure larger programs in smaller pieces. Modules can import and export bindings. Contracts define conditions on the exported bindings. Modules can be used to define new languages. 2

Modules Each file defines a separate module. A module is usually defined in its own file. In that case the name of the module is the name of the file. Bindings are exported with (provide funcname). Bindings are imported with (require "filename"). 3-5

Example #lang racket ; In file fact.rkt (provide fact) (define (fact x) (fact-help x 1)) (define (fact-help x acc) (case x [(0) acc] [else (fact-help (- x 1) (* x acc))])) (define (fact2 x) (for/product ([i (in-range x)]) (+ i 1))) Non-exported functions are private. #lang racket ; In file use-fact.rkt (require "fact.rkt") (fact 10) (fact2 10) ; Error 6

Provide provide takes a list of elements to provide. Those elements can take various forms. all-defined-out exports all bindings defined in the module. (rename-out [orig-name export-name]...) changes the name of some exported elements. (prefix-out prefix spec) adds a prefix to all bindings exported by spec. (except-out spec id...) exports all bindings from spec except id. (all-from-out module-path) reexports all the bindings from another module.... 7

Provide: Example. (provide (except-out (all-from-out racket) lambda) (rename-out [lambda function])) This exports all bindings provided by racket but lambda. lambda is replaced by function. 8

Require require takes a list of elements to import. Again, they can be of various forms. A module name will simply import all from the module. (except-in spec id...) imports all bindings from spec except id. (rename-in spec [orig-id new-id]...) renames some of the bindings. (only-in spec id [o-id n-id]...) imports only some bindings, optionally renaming them. (prefix-in prefix spec) adds a prefix to all bindings. (for-syntax spec) imports at the syntax level (see macros).... 9

Require: Example. (require (prefix-in racket: (except-in racket lambda)) (only-in racket [lambda function])) This import all bindings from racket by prefixing them with "racket:", except lambda that is renamed to function 10

Submodules Submodules can be defined using (module mname language decls...), where mname is the name of the module, language defines the initially available bindings (it is usually racket), decls... is the body of the module. Submodules are not automatically evaluated along their parent. They need to be imported as any module. Such a submodule can be imported by (require 'mname) (in the parent module) or by (require (submod "fname" mname)) (in another module). Submodules can be nested arbitrarily. 11

Submodule: Example (module my-sum racket (provide plus) (define (plus x y) (+ x y))) (require 'my-sum) (plus 3 4) (module test-sum racket (require (submod ".." my-sum)) (equal? (plus 3 4) (plus 4 3))) (require 'test-sum) Note the order of evaluation: require s, then define s, then the rest. 12-13

Submodules (2) Submodules cannot access the bindings of their parent module. It is possible to define "inverted" submodules that can access the parent s bindings, but cannot be required inside it. (module* mname language-or-#f decls) declares such a submodule. The language can also be #f, in which case the submodule accesses all of the parent module bindings. (module+ mname decls) is a shortcut when the language is #f. Using this form, the same submodule can also be declared in several parts. 14

Submodules: Example #lang racket ; In file fact.rkt (provide fact) ;... (module* extra #f (provide fact fact2)) Importing the module extra allows to have access to fact2 as well. #lang racket ; In file use-fact.rkt (require (submod "fact.rkt" extra)) (fact2 10) ; Now it works 15

Special Submodules (module+ main (display "In the main")) (module+ test (when (not (= (fact 10) (fact2 10))) (raise "Problem"))) Those module names have a special meaning. main modules are run when the module is directly executed (not required from another module). test modules are run by the raco test executable. 16

Modules Modules are useful to structure larger programs. We will see that they provide the basis for two interesting concepts Contracts define the boundary between modules. New languages can be created transparently. 17

Side Note: Include (include "filename") inlines the content of "filename" in the current file. This is not to be confused with require! 18

Contracts Contracts are used to define the pre- and post-conditions on provided procedures. They are defined as part of the provide instruction. The contract system ensures that contracts are always respected. 19

A first contract (provide (contract-out [fact (-> natural-number/c natural-number/c)])) The contract stipulates that fact is a function taking a natural and returning a natural. natural-number/c is provided by the system but we can write our own test: (define (nat? x) (and (number? x) (integer? x) (exact? x) (>= x 0))) (provide (contract-out [fact (-> nat? nat?)])) A basic contract just need to be a procedure taking one argument. Its return value is interpreted as a "boolean". 20-21

Contract language (examples) (and/c number? integer?) ensures the two conditions. (or/c number? string?) ensures one of the conditions. any accepts any argument(s). any/c accepts one argument of any type. (listof string?) accepts a list of strings. Literals (e.g. symbols, numbers) are evaluated as a contract accepting only themselves. Example: (or/c 'bold 'italic #f) Side Question: how would you implement e.g. or/c? (define ((or/c. tests) x) (ormap (lambda (test) (cond [(procedure? test) (test x)] [else (equal? test x)])) tests)) 22-24

Functions with optional and rest arguments To have a variable number of arguments, use the ->* combinator that takes three arguments (mandatory, optional, return). (define (silly x y [z 1]) (* (+ x y) z)) (provide (contract-out [silly (->* (integer? integer?) (integer?) integer?)])) One can also put a contract on the rest. (provide (contract-out [max (->* () () #:rest (listof real?) real?)])) 25-26

Dependencies (struct counter (cnt)) (define (count) (counter 0)) (define (inc cnt) (counter (add1 (counter-cnt cnt)))) (define (val cnt) (counter-cnt cnt)) How to express that the value stored in the result of inc is the value of the argument plus 1? With what we have seen so far, it is not possible. We need to use yet another combinator: ->i 27-28

Specifying dependencies ->i looks like ->* but all arguments are given name so that they can be reused. (define (minus a b) (- a b)) (provide (contract-out [minus (->i ([a natural-number/c] [b (a) (and/c natural-number/c (<=/c a))]) [result (a) (and/c natural-number/c (<=/c a))])])) 29

Example: counter (module counter racket (struct counter (cnt)) (define (count) (counter 0)) (define (inc cnt) (counter (add1 (counter-cnt cnt)))) (define (val cnt) (counter-cnt cnt)) (provide (contract-out [count (-> (and/c counter? (lambda (res) (= 0 (val res)))))] [val (-> counter? natural-number/c)] [inc (->i ([cnt counter?]) [res (cnt) (and/c counter? (lambda (res) (= (val res) (add1 (val cnt)))))])]))) (require 'counter) (define x (count)) (val x) (define y (inc (inc x))) (val y) 30

Example: Stack (provide make-stack ; empty stack list->stack ; already filled stack stack->list ; The content of the stack in a list empty-stack? ; Is the stack empty? size ; The number of elements in the stack top ; The first element of the stack pop ; The stack without its top element push ; The stack with a new element on top stack?) ; Is this object a stack? 31

Stack: Implementation (struct stack (list)) (define (make-stack) (stack '())) (define list->stack stack) (define stack->list stack-list) (define empty-stack? (compose null? stack-list)) (define size (compose length stack-list)) (define top (compose car stack-list)) (define pop (compose stack cdr stack-list)) (define (push s el) (stack (cons el (stack-list s)))) ; stack? is provided by the struct. top and pop need a non-empty stack. This is not handled here but in the contracts. 32

Stack: Simple Contracts First, we put contracts on types and pre-conditions. (provide (contract-out [make-stack (-> stack?)] [list->stack (-> (listof any/c) stack?)] [stack->list (-> stack? (listof any/c))] [empty-stack? (-> stack? boolean?)] [size (-> stack? natural-number/c)] [top (-> (and/c stack? (not/c empty-stack?)) any/c)] [pop (-> (and/c stack? (not/c empty-stack?)) stack?)] [push (-> stack? any/c stack?)] [stack? (-> any/c boolean?)])) 33-34

Stack: What is a stack? The previous contracts would still be valid if our implementation was actually a queue. What is the speciality of a stack? LIFO: Last-In-First-Out The elements are popped in the opposite order than they were pushed. Condition on push: the pushed element is now on top. 35-36

Contract on push (... [push (->i ([st stack?] [elem any/c]) [res (elem) (and/c stack? (lambda (res) (equal? (top res) el)))])]...) Exercise: What about the property that the rest of the stack is preserved? 37-38

Contracts for more complex cases. Many more complex contracts are available. See the guide and reference. 39

New Languages Macros can only extend the language, and this inside the syntactic convention of the language. If one wants to create a new language, it may be necessary to restrict or alter the language, or to change the syntax. We will see how to do that (but not the syntax part). 40

New Languages (2) The "language" of a module can be defined arbitrarily. In (module name language body...), the argument language can be any module. The bindings provided by the language module define what is available to the new module. One can define what is available in a language in the provide instruction. 41

A first language (module racketf racket ; Like racket but "lambda" is "function" (provide (except-out (all-from-out racket) lambda) (rename-out [lambda function]))) (module mymodule 'racketf ; Use the new language (define sqr (function (x) (* x x))) ; (define sqr (lambda (x) (* x x))) ; would be an error (sqr 5)) (require 'mymodule) 42

Minimal export (module minimal racket (provide #%app ; Implicit form for procedure application #%module-begin ; Implicit for module declaration #%datum ; Implicit for literals and data #%top ; Implicit for unbound identifiers lambda)) ; Just because we want to do something (module test 'minimal ; ok ((lambda (x) x) 10) ; not ok ((lambda (x) (+ x 1)) 10)) 43

Redefining implicit forms #lang racket (module verbose racket (provide (except-out (all-from-out racket) #%module-begin #%app #%top #%datum) (rename-out [module-begin #%module-begin] [app #%app] [top #%top] [datum #%datum])) (define-syntax-rule (module-begin expr...) (#%module-begin (displayln "Entering Module Verbose") expr... (displayln "Leaving Module Verbose"))) [...]) 44

Redefining implicit forms (define-syntax-rule (app f arg...) (begin (display "Applying: ") (displayln '(f arg...)) (let ([res (#%app f arg...)]) (display " res: ") (displayln res) res))) (define-syntax-rule (top. arg) (begin (display "Not found ") (displayln 'arg) 'arg)) (define-syntax-rule (datum. arg) (begin (display "Value: ") (displayln 'arg) (#%datum. arg))) 45

Redefining implicit forms (module client (submod ".." verbose) (define x 5) (+ x 10 x) (display y)) (require 'client) Result: Entering Module Verbose Value: 5 Applying: (+ x 10 x) Value: 10 res: 20 20 Applying: (display y) Not found y y res: #<void> Leaving Module Verbose 46

A new #lang #lang s-exp "fname.rkt"... is equivalent to (module name "fname.rkt"...) To change the syntax, or to get rid of the "s-exp", see the guide. 47-48

Example: Half-Life. Everytime an identifier is used, its value is divided by two. Produces... #lang s-exp "Language-half-life.rkt" (define x 10) (define y 10) (+ (* x y) (* x y)) (define z (* 50 y (- x x))) (+ (if (= z 100) z 100) y) z 125 51 25 49-50

Half-Life: Implementation #lang racket (provide + - / * = > < >= <= if #%app #%datum #%module-begin #%top-interaction (rename-out [my-define define] [lookup #%top])) (define env (make-hash)) (define-syntax-rule (my-define id expr) (let ([val expr]) (hash-set! env 'id val) (void))) 51

Half-Life: Implementation (define-syntax-rule (lookup. id) (if (hash-has-key? env 'id) (let* ([val (hash-ref env 'id)] [new-val (floor (/ val 2))]) (if (not (= new-val 0)) (hash-set! env 'id new-val) (hash-remove! env 'id)) val) (error "This id does not exist (anymore)"))) 52

Summary Modules are used to structure larges program in smaller pieces. It is possible to define contracts on the provided procedures. Creating a new language amounts to using macros and the module system. 53

General summary Racket is a modern functional programming language. {if {you get sick of '()} {use {'[] or '{}}}}:-) It is more than one language: Several existing languages. The infrastructure to create your own. 54