diff --git a/contracts-to-types.rkt b/contracts-to-types.rkt new file mode 100644 index 0000000..5c4b0fe --- /dev/null +++ b/contracts-to-types.rkt @@ -0,0 +1,44 @@ +#lang type-expander + +(provide :contract→type + (rename-out [c→t contract→type] + [c→t contract->type] + [:contract→type :contract->type])) +(require racket/contract/base + (for-syntax syntax/parse + type-expander/expander)) + +(define-type-expander c→t + (syntax-parser + [(_ ({~literal or/c} alt ...)) #'(U (c→t alt) ...)] + [(_ ({~literal and/c} alt ...)) #'(∩ (c→t alt) ...)] + [(_ ({~literal listof} c)) #'(Listof (c→t c))] + [(_ ({~literal list/c} c ...)) #'(List (c→t c) ...)] + [(_ ({~literal *list/c} prefix suffix ...)) #'(Rec R (U (Pairof prefix R) + (List suffix ...)))] + [(_ ({~literal vectorof} c)) #'(Vectorof (c→t c))] + [(_ ({~literal vector/c} c ...)) #'(Vector (c→t c) ...)] + [(_ ({~literal cons/c} a d)) #'(Pairof (c→t a) (c→t d))] + [(_ {~literal integer?}) #'Integer] + [(_ {~literal string?}) #'String] + [(_ {~literal symbol?}) #'Symbol] + [(_ {~literal exact-nonnegative-integer?}) #'Exact-Nonnegative-Integer] + [(_ {~literal exact-positive-integer?}) #'Exact-Positive-Integer] + [(_ {~and τ ({~literal quote} _)}) #'τ] + [(_ {~and τ {~or :number :str :id}}) #''τ] + [(_ {~and τ ({~literal quasiquote} _)}) #'τ] + [(_ ({~literal unquote} τ)) #'τ] + [(_ c) (raise-syntax-error + 'contract→type + (string-append + "I cannot convert this contract to a type automatically." + " Please fill in an issue at" + " https://github.com/jsmaniac/type-expander/issues if the translation" + " can easily be done automatically, or do the translation manually " + " otherwise. " + (format "~a" (syntax->datum #'c))) + #'c)])) + +(define-syntax (:contract→type stx) + (syntax-case stx () + [(_ c) #`(writeln '#,(expand-type #`(c→t c)))])) \ No newline at end of file diff --git a/scribblings/contracts-to-types.scrbl b/scribblings/contracts-to-types.scrbl new file mode 100644 index 0000000..b5660b0 --- /dev/null +++ b/scribblings/contracts-to-types.scrbl @@ -0,0 +1,37 @@ +#lang scribble/manual + +@require[(for-label racket/contract/base) + scribble/example] +@title{Using contract syntax to specify types} + +@defmodule[type-expander/contracts-to-types] + +@defform*[{(contract→type contract) + (contract->type contract)}]{ + + This is a simple type expander which translates common contracts to types. + Note that it only supports a limited number of contract constructors. The + following are supported: @racket[or/c], @racket[and/c] (the translation may + produce a type too complex for Typed/Racket to understand properly, though), + @racket[listof], @racket[list/c], @racket[*list/c], @racket[vectorof], + @racket[vector/c], @racket[cons/c], @racket[integer?], @racket[string?], + @racket[symbol?], @racket[exact-nonnegative-integer?], + @racket[exact-positive-integer?], @racket['quoted-datum], + @racket[`quasiquoted-datum-with-unquoted-types]. + + Furthermore, using @racket[,_τ] anywhere outside of a quoted datum will leave + the type @racket[_τ] unchaged, allowing the user to manually convert to types + only the parts which cannot be converted automatically.} + +@defform*[{(:contract→type contract) + (:contract->type contract)}]{ + + Prints a representation of the contract translated as a type. It is then + possible to copy-paste that result into the code. + + @examples[ + (require type-expander/lang + racket/contract/base + type-expander/contracts-to-types) + (:contract→type (list/c 1 2 "str" (or/c integer? string?)))] +} diff --git a/scribblings/type-expander.scrbl b/scribblings/type-expander.scrbl index c330fea..42d7722 100644 --- a/scribblings/type-expander.scrbl +++ b/scribblings/type-expander.scrbl @@ -842,3 +842,5 @@ arguments to a type expander. define-struct/exec:] @include-section{deprecated-colon.scrbl} + +@include-section{contracts-to-types.scrbl} \ No newline at end of file