www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit ed7566534e8db408e80f1925c5dda634e9551629
parent 93f39cea0ce7e58504b88be29181a48badaffe8f
Author: Georges Dupéron <georges.duperon@gmail.com>
Date:   Wed, 21 Jun 2017 17:13:09 +0200

Just build phc-adt, to initialize the cache without a timeout

Diffstat:
M.travis.yml | 16+++++++++-------
Mscribblings/introduction.scrbl | 2+-
Mscribblings/state-of-the-art.scrbl | 4+++-
Mscribblings/tr.scrbl | 237+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
4 files changed, 214 insertions(+), 45 deletions(-)

diff --git a/.travis.yml b/.travis.yml @@ -66,7 +66,8 @@ before_install: install: - raco pkg update --all --scope user - - raco pkg install -j 2 --deps search-auto --no-launcher + - raco pkg install -j 2 phc-adt +# - raco pkg install -j 2 --deps search-auto # --no-setup # - (while ! test -e ~/done.txt; do sleep 10; echo -n '.'; done) & raco setup -v -j 2 --pkgs phc-thesis && touch ~/done.txt @@ -76,16 +77,17 @@ before_script: # `raco pkg install --deps search-auto` to install any required # packages without it getting stuck on a confirmation prompt. script: + echo "." #- raco test -x -p phc-thesis #- raco setup --check-pkg-deps --no-zo --no-launcher --no-install --no-post-install --no-docs --pkgs phc-thesis - - rm -fr doc/ - - raco scribble --dest doc/phc-thesis --html --dest-name index ++main-xref-in --redirect-main https://docs.racket-lang.org/ --redirect https://docs.racket-lang.org/ scribblings/phc-thesis.scrbl - - make - - if test "x${DEPLOY:-}" = "xtrue"; then sh ./auto-push-gh-pages.sh; fi +# - rm -fr doc/ +# - raco scribble --dest doc/phc-thesis --html --dest-name index ++main-xref-in --redirect-main https://docs.racket-lang.org/ --redirect https://docs.racket-lang.org/ scribblings/phc-thesis.scrbl +# - make +# - if test "x${DEPLOY:-}" = "xtrue"; then sh ./auto-push-gh-pages.sh; fi #after_success: # - raco pkg install --deps search-auto cover cover-coveralls # - raco cover -b -f coveralls -d $TRAVIS_BUILD_DIR/coverage . -after_success: - - sh ./auto-push-master.sh +#after_success: +# - sh ./auto-push-master.sh diff --git a/scribblings/introduction.scrbl b/scribblings/introduction.scrbl @@ -332,7 +332,7 @@ } @asection{ - @atitle{Expressing the data dependencies of a path via row types} + @atitle{Expressing the data dependencies of a pass via row types} It is easy enough to test a compiler by feeding it sample programs and checking that the compiled output behaves as expected. However, @htodo{ diff --git a/scribblings/state-of-the-art.scrbl b/scribblings/state-of-the-art.scrbl @@ -155,7 +155,9 @@ @item{Subtyping (also called inclusion polymorphism, subtype polymorphism, or nominal subtyping ?): Subclasses and interfaces in @csharp and Java, sub-structs and union types in @|typedracket|, - polymorphic variants in @CAML @~cite[#:precision @list{chap. 6, sec. Polymorphic Variants}]{minsky2013real}} + polymorphic variants in @CAML + @~cite[#:precision @list{chap. 6, sec. Polymorphic Variants} + ]{minsky2013real}} @; https://realworldocaml.org/v1/en/html/variants.html @; See also: use of exceptions as dynamically extensible sum types: diff --git a/scribblings/tr.scrbl b/scribblings/tr.scrbl @@ -200,12 +200,21 @@ therefore keep our overview succinct and gloss over most details. using the ``type-safe enum'' design pattern, two otherwise identical declarations of an enum will yield objects of different types. In contrast, two uses of an interned symbols in Racket and @|typedracket| will produce - identical values and types. A way of seeing this is that symbols act as uses - of constructors which are implicitly declared globally. + identical values and types. A way of seeing this is that symbols are similar + to constructors (in the functional programming sense) or enums which are + implicitly declared globally. @examples[#:label #f #:eval (tr-eval) - ] - } + (module m1 typed/racket + (define sym1 'foo) + (provide sym1)) + (module m2 typed/racket + (define sym2 'foo) + (provide sym2)) + (require 'm1 'm2) + (code:comment "The tow independent uses of 'foo are identical:") + (eq? sym1 sym2)] + } @asection{ @atitle[#:tag "tr-presentation-unions"]{Unions} @@ -232,7 +241,7 @@ therefore keep our overview succinct and gloss over most details. v (set! v 'bar) v - ;; This throws an error at compile-time: + (code:comment "This throws an error at compile-time:") (eval:error (set! v 'oops))] A union such as @racket[(U 'ca (List 'cb Number) (List 'cc String Symbol))] @@ -248,14 +257,18 @@ therefore keep our overview succinct and gloss over most details. variants@~cite["minskyRealWorldOCaml"] than to regular variants. @examples[#:label #f #:eval (tr-eval) - ;; 'foo is used in a first union - (define v : (U 'foo 'bar) 'bar) - ;; The same 'foo is reused in another union - (define w : (U 'foo 'quux) 'quux)] + (define-type my-variant (U 'ca + (List 'cb Number) + (List 'cc String Symbol))) + (define v₁ : my-variant 'ca) + (define v₂ : my-variant (list 'cb 2187)) + (define v3 : my-variant (list 'cc "Hello" 'world))] Finally, it is possible to mix different sorts of types within the same union: the type @racket[(U 0 #true 'other)] is inhabited by the number @racket[0], the boolean @racket[#true], and the symbol @racket['other]. + Translating such an union to a language like @|CAML| could be done by + explicitly tagging each case of the union with a distinct constructor. Implementation-wise, all values in the so-called ``untyped'' version of Racket are tagged: a few bits within the value's representation are reserved @@ -278,20 +291,27 @@ therefore keep our overview succinct and gloss over most details. the singleton type @racket[0]. The intersection of @racket[(U 'a 'b 'c)] and @racket[(U 'b 'c 'd)] will be @racket[(U 'b 'c)], as @racket['b] and @racket['c] belong to both unions. + + @examples[ + #:label #f #:eval (tr-eval) + (code:comment ":type shows the given type, or a simplified version of it") + (:type (∩ (U 'a 'b 'c) (U 'b 'c 'd)))] @|Typedracket| is able to reduce some intersections such as those given above at compile-time. However, in some cases, it is forced to keep the intersection type as-is. For example, structs (@seclink["tr-presentation-structs"]{ - describled below} can, using special properties, impersonate functions. - @|Typedracket| does not handle these properties (yet), and therefore cannot - determine whether a given struct type also impersonates a function or not. - This means that the intersection @racket[(∩ s (→ Number String))], where - @racket[s] is a struct type, cannot be reduced to @racket[Nothing], because - @|typedracket| cannot determine whether the struct @racket[s] can act as a - function or not. - - Another situation where @|typedracket| cannot reduce the intersection of two - function types (@seclink["tr-presentation-functions"]{presented below}). + describled below} can, using special properties, impersonate functions. This + mechanism is similar to PHP's @tt["__invoke"], the ability to overload + @tt["operator()"] in @|CPP|. @|Typedracket| does not handle these properties + (yet), and therefore cannot determine whether a given struct type also + impersonates a function or not. This means that the intersection + @racket[(∩ s (→ Number String))], where @racket[s] is a struct type, cannot be + reduced to @racket[Nothing], because @|typedracket| cannot determine whether + the struct @racket[s] can act as a function or not. + + Another situation where @|typedracket| cannot reduce the intersection is when + intersecting two function types (@seclink["tr-presentation-functions"]{ + presented below}). @racketblock[ (∩ (→ Number String) (→ Number Symbol)) @@ -330,7 +350,32 @@ therefore keep our overview succinct and gloss over most details. @racket[Nothing] propagates outwards). However, if the user directly specifies the type @racket[(Pairof (∩ 'a 'b) Integer)], it is simplified to @racket[(Pairof Nothing Integer)], but the @racket[Nothing] does not propagate - outwards beyond the initial use of @racket[∩].} + outwards beyond the initial use of @racket[∩]. + + @examples[#:label #f #:eval (tr-eval) + (:type (∩ 'a 'b)) + (:type (∩ (Pairof 'a 'x) (Pairof 'b 'x))) + (:type (Pairof (∩ 'a 'b) Integer))] + + A simple workaround exists: the outer type, which could be collapsed to + @racket[Nothing], can be intersected again with a type of the same shape. The + outer intersection will traverse both types (the desired one and the + ``shape''), and propagate the leftover @racket[Nothing] further out. + + @examples[#:label #f #:eval (tr-eval) + (:type (Pairof (∩ 'a 'b) Integer)) + (:type (∩ (Pairof (∩ 'a 'b) Integer) + (Pairof Any Any)))] + + These intersections are not very interesting on their own, as in most cases + it is possible to express the resulting simplified type without using the + intersection operator. They become more useful when mixed with polymorphic + types: intersecting a polymorphic type variable with another type can be used + to restrict the actual values that may be used. The type @racket[(∩ A T)], + where @racket[A] is a polymorphic type variable and @racket[T] is a type + defined elsewhere, is equivalent to the use of bounded type parameters in + @|java| or @|csharp|. In @|csharp|, for example, the type @racket[(∩ A T)] + would be written using an @tt["where A : T"] clause.} @asection{ @atitle[#:tag "tr-presentation-structs"]{Structs} @@ -350,14 +395,15 @@ therefore keep our overview succinct and gloss over most details. corresponding type. @examples[#:label #f #:eval (tr-eval) - (struct s ([field₁ : (Pairof String Symbol)] - [field₂ : Integer] - [field₃ : Symbol]) + (struct parent ([field₁ : (Pairof String Symbol)]) + #:transparent) + (struct s parent ([field₂ : Integer] + [field₃ : Symbol]) #:transparent) (s (cons "x" 'y) 123 'z)] In @|typedracket|, structs can have polymorphic type arguments, which can be - used within the types of the struct's fields. + used inside the types of the struct's fields. @examples[#:label #f #:eval (tr-eval) (struct (A B) poly-s ([field₁ : (Pairof A B)] @@ -367,7 +413,7 @@ therefore keep our overview succinct and gloss over most details. (poly-s (cons "x" 'y) 123 'z)] Racket further supports - @tech[#:doc '(lib "scribblings/reference/reference.scrbl")]{ struct type + @tech[#:doc '(lib "scribblings/reference/reference.scrbl")]{struct type properties}, which can be seen as a limited form of method definitions for a struct, thereby making them closer to real objects. The same struct type property can be implemented by many structs, and the declaration of a struct @@ -409,23 +455,142 @@ therefore keep our overview succinct and gloss over most details. @asection{ @atitle[#:tag "tr-presentation-functions"]{Functions} - @itemlist[ - @item{Simple function types} - @item{Rest arguments} - @item{Case functions (i.e. lightweight dependent function types)} - @item{Polymorphic functions} - @item{Keyword and optional arguments} - @item{Filters (information gained on the input when a predicate returns true - or false)}] - } + + @|Typedracket| supports rich function types, to support some of the flexible + use patterns allowed by Racket. + + The simple function type below indicates that the function expects two + arguments (an integer and a string), and returns a boolean: + + @racketblock[(→ Integer String Boolean)] + + We note that unlike @|haskell| and @|CAML| functions, Racket functions are + not implicitly curried. To express the corresponding curried function type, + one would write: + + @racketblock[(→ Integer (→ String Boolean))] + + A function may additionally accept optional positional arguments, and keyword + (i.e. named) arguments, both mandatory and optional: + + @racketblock[ + (code:comment "Mandatory string, optional integer and boolean arguments:") + (->* (String) (Integer Boolean) Boolean) + (code:comment "Mandatory keyword arguments:") + (→ #:size Integer #:str String Boolean) + (code:comment "Mandatory #:str, optional #:size and #:opt:") + (->* (#:str String) (#:size Integer #:opt Boolean) Boolean)] + + Furthermore, functions in Racket accept a catch-all ``rest'' argument, which + allows for the definition of variadic functions. Typed racket also allows + expressing this at the type level, as long as the arguments covered by the + ``rest'' clause all have the same type: + + @racketblock[ + (code:comment "The function accepts one integer and any number of strings:") + (-> Integer String * Boolean) + (code:comment "Same thing with an optional symbol inbetween: ") + (->* (Integer) (Symbol) #:rest String Boolean)] + + One of @|typedracket|'s main goals is to be able to typecheck idiomatic + Racket programs. Such programs may include functions whose return type depends + on the values of the input arguments. Similarly, @racket[case-lambda] can be + used to create lambda functions which dispatch to multiple behaviours based on + the number of arguments passed to the function. + + @|Typedracket| provides the @racket[case→] type operator, which can be used to + describe the type of these functions: + + @racketblock[ + (code:comment "Allows 1 or 3 arguments, with the same return type.") + (case→ (→ Integer Boolean) + (→ Integer String Symbol Boolean)) + (code:comment "A similar type based on optional arguments allows 1, 2 or 3") + (code:comment " arguments in contrast:") + (->* (Integer) (String Symbol) Boolean) + (code:comment "The output type can depend on the input type:") + (case→ (→ Integer Boolean) + (→ String Symbol)) + (code:comment "Both features (arity and dependent output type) can be mixed") + (case→ (→ Integer Boolean) + (→ Integer String (Listof Boolean)))] + + Another important feature, which can be found in the type system of most + functional programming languages, and most object-oriented languages, is + parametric polymorphism. @|Typedracket| allows the definition of polymorphic + structs, as detailed above, as well as polymorphic functions. For example, the + function @racket[cons] can be considered as a polymorphic function with two + polymorphic type arguments @racket[A] and @racket[B], which takes an argument + of type @racket[A], an argument of type @racket[B], and returns a pair of + @racket[A] and @racket[B]. + + @racketblock[(∀ (A B) (→ A B (Pairof A B)))] + + @htodo{Something on which types can be inferred and which can't (for now).} + + Finally, predicates (functions whose results can be interpreted as booleans) + can be used to gain information about the type of their argument, depending on + the result. The type of a predicate can include positive and negative filters, + indicated with @racket[#:+] and @racket[#:-], respectively. The type of the + @racket[string?] predicate is: + + @racketblock[(→ Any Boolean : #:+ String #:- (! String))] + + In this notation, the positive filter @racket[#:+ String] indicates that when + the predicate returns @racket[#true], the argument is known to be a + @racket[String]. Conversely, when the predicate exits with @racket[#false], + the negative filter @racket[#:- (! String)] indicates that the input could not + (@racket[!]) possibly have been a string. The information gained this way + allows regular conditionals based on arbitrary predicates to work like + pattern-matching: + + @examples[#:label #f #:eval (tr-eval) + (define (f [x : (U String Number Symbol)]) + (if (string? x) + (code:comment "x is known to be a String here:") + (ann x String) + (code:comment "x is known to be a Number or a Symbol here:") + (ann x (U Number Symbol))))] + + The propositions do not necessarily need to refer to the value as a whole, + and can instead give information about a sub-part of the value. Right now, the + user interface for specifying paths can only target the left and right members + of @racket[cons] pairs, recursively. Internally, @|typedracket| supports + richer paths, and the type inference can produce filters which give + information about individual structure fields, or about the result of forced + promises, for example.} @asection{ @atitle[#:tag "tr-presentation-recursive-types"]{Recursive types} - + recursion via named types - } + + @|Typedracket| allows recursive types, both via (possibly mutually-recursive) + named declarations, and via the @racket[Rec] type operator. + + In the following examples, the types @racket[Foo] and @racket[Bar] are + mutually recursive. The type @racket[Foo] matches lists with an even number of + alternating @racket[Integer] and @racket[String] elements, starting with an + @racket[Integer], + + @racketblock[ + (define-type Foo (Pairof Integer Bar)) + (define-type Bar (Pairof String (U Foo Null)))] + + This same type could alternatively be defined using the @racket[Rec] + operator. The notation @racket[(Rec R T)] builds the type @racket[T], where + occurrences of @racket[R] are interpreted as recursive occurrences of + @racket[T] itself. + + @racketblock[ + (Rec R + (Pairof Integer + (Pairof String + (U R Null))))]} @asection{ @atitle{Classes} + + The @racketmodname[racket/class] module provides an object-oriented system for + Racket. } @asection{