Redex Miscellanea
(require redex-etc) | package: redex-etc |
This library is under development; compatibility may not be maintained.
This package implements miscellaneous metafunctions, macros, and functions for Redex. For the purposes of illustration, we will use the following language in our examples.
(define-language Λ [e ::= x v (e e ...)] [v ::= (λ (x ...) e)] [x ::= variable-not-otherwise-mentioned] [E ::= hole (v ... E e ...)] #:binding-forms (λ (x ...) e #:refers-to (shadow x ...))) (default-language Λ)
1 Metafunctions
metafunction
(substitute-env target ([key value] ...))
> (term (substitute-env x ([x 1] [y 2]))) 1
metafunction
(substitute* target [key value] ...)
> (term (substitute* x [x 1] [y 2])) 1
metafunction
(lookup ([key value] ...) needle)
metafunction
(ext ([key value] ...) [new-key new-value] ...)
metafunction
(unique (any ...))
metafunction
(rem ([key value] ...) old-key ...)
2 Macros
syntax
(match-term lang expression [pattern expression] ...)
syntax
(not-match? lang pattern term)
> (not-match? Λ x (λ (y) y)) #t
> (not-match? Λ x y) #f
syntax
(require-typed-primitives lang reduce-id type-id maybe-convert-type require-spec ...)
maybe-convert-type =
| #:convert-type convert-type-id
Thanks to Andrew Wagner for suggesting this feature.
Uses the given Typed Racket modules to define the reduction and typing of primitive operations. When specified, the type conversion procedure is given a datum representing the Typed Racket type, and is expected to return an equivalent type as a Redex term. Note, that you must define the type conversion function with define-syntax.
In the following, we import even?, odd?, and add1 from Typed Racket. Since add1 takes and returns arbitrary numbers, not just integers, we provide a type conversion function to handle the number type. This is because the object language only has integers.
(define-language Ω [e ::= v (e e ...)] [v ::= integer boolean o] [o ::= even? odd? add1] [E ::= hole (v ... E e ...)] [τ ::= Integer Boolean (-> τ τ ...)] [Γ ::= ([x τ] ...)]) (define-syntax (convert-type type) (let go ([type type]) (match type ['Boolean (term Boolean)] ['Integer (term Integer)] ['Number (term Integer)] [`(-> ,x ...) (term (-> ,@(map go x)))]))) (require-typed-primitives Ω δ Δ #:convert-type convert-type (only-in typed/racket/base add1 even? odd?)) (define-judgment-form Ω #:mode (⊢ I I I O) #:contract (⊢ Γ e : τ) [(⊢ Γ x : τ) (where τ (lookup Γ x))] [(⊢ Γ integer : Integer)] [(⊢ Γ boolean : Boolean)] [(⊢ Γ o : (Δ o))] [(⊢ Γ e_f : (-> τ_a ... τ_r)) (⊢ Γ e_v : τ_a) ... ---------------------------- (⊢ Γ (e_f e_v ...) : τ_r)]) (define ↦ (reduction-relation Ω [--> (in-hole E (o v ...)) (in-hole E (δ (o v ...))) δ]))
Given those definitions, we can now use those primitive operations.
> (judgment-holds (⊢ () (even? 42) : Boolean)) #t
> (judgment-holds (⊢ () add1 : (-> Integer Integer))) #t
> (apply-reduction-relation ↦ (term (even? 42))) '(#t)
> (apply-reduction-relation ↦ (term (odd? 42))) '(#f)
> (apply-reduction-relation ↦ (term (add1 42))) '(43)
3 Functions
procedure
(make-eval rr [ #:inject inject #:project project #:program? program? #:answer? answer?]) → (-> any/c any) rr : reduction-relation? inject : (-> any/c any) = values project : (-> any/c any) = values program? : predicate/c = (const #t) answer? : predicate/c = (const #t)
> (define v (reduction-relation Λ [--> (in-hole E ((λ (x ..._a) e) v ..._a)) (in-hole E (substitute* e [x v] ...)) βv])) > (define ⇓ (make-eval v)) > (⇓ (term ((λ (x) x) (λ (y) y)))) '(λ (y) y)
parameter
(current-max-steps max-steps) → void? max-steps : natural?
= 50