Skip to content

Commit bb77409

Browse files
AlexKnauthlexi-lambda
authored andcommitted
Implement type aliases
1 parent 8e90c5a commit bb77409

File tree

5 files changed

+139
-2
lines changed

5 files changed

+139
-2
lines changed

hackett-doc/scribblings/hackett/reference.scrbl

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ following combination of @racket[def], @racket[lambda], and @racket[case*]:
107107
The @racket[defn] form is generally preferred when defining top-level functions.
108108

109109
@(hackett-examples
110-
(defn square : (t:-> t:Integer t:Integer)
110+
(defn square : {t:Integer t:-> t:Integer}
111111
[[x] {x * x}])
112112
(eval:check (square 5) 25))}
113113

@@ -308,6 +308,49 @@ specified controls the fixity used by the associated @racket[type-constructor-id
308308
{(Leaf 1) :&: (Leaf 2) :&: (Leaf 3)})}
309309
@(close-eval data-examples-eval)
310310

311+
@subsection[#:tag "reference-type-alias"]{Defining type aliases}
312+
313+
@(define alias-examples-eval (make-hackett-eval))
314+
@defform[#:literals [left right]
315+
(type type-clause type-expr)
316+
#:grammar
317+
([type-clause name-id
318+
(code:line (name-id param-id ...+))])]{
319+
320+
Defines a @deftech{type alias} named @racket[name-id]. Uses of @racket[name-id] are equivalent to
321+
uses of the type specified in @racket[type-expr]. If @racket[type-clause] is a bare @racket[name-id],
322+
then @racket[name-id] is bound directly to the type alias.
323+
324+
@(hackett-examples
325+
#:eval alias-examples-eval
326+
(type Num Double)
327+
(def n : Num 1.5)
328+
(#:type n))
329+
330+
@margin-note{
331+
Type aliases with @racket[param-id]s can only be used with prefix notation, using
332+
@racket[(name-id type-argument ...)] rather than
333+
@racket[{type-argument name-id type-argument}].}
334+
335+
If @racket[param-id]s are specified, then uses of the type alias must supply as many arguments as
336+
there are @racket[param-id]s. The arguments are supplied like those to a type constructor—i.e.
337+
@racket[(name-id type-argument ...)]—and the resulting type is @racket[type-expr] with each
338+
@racket[param-id] substituted with the corresponding @racket[type-argument].
339+
340+
Though the application of a type alias is syntactically similar to the application of a type
341+
constructor, type aliases are effectively type-level macros, and they may not be partially applied.
342+
All uses of a type alias must be fully saturated.
343+
344+
@(hackett-examples
345+
#:eval alias-examples-eval
346+
(type (Predicate a) {a t:-> t:Bool})
347+
(def zero? : (Predicate t:Integer) (== 0))
348+
(#:type zero?)
349+
(eval:check (zero? 0) True)
350+
(eval:check ((: zero? (Predicate t:Integer)) 0) True)
351+
(eval:error (: zero? Predicate)))
352+
@(close-eval alias-examples-eval)}
353+
311354
@subsection[#:tag "reference-numbers"]{Numbers}
312355

313356
@deftype[t:Integer]{

hackett-lib/hackett/base.rkt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
#lang racket/base
22

33
(require (only-in hackett/private/adt case* case λ λ* lambda lambda* defn _)
4+
(only-in hackett/private/type-alias type)
45
(only-in hackett/private/class instance derive-instance)
56
(except-in hackett/private/kernel λ lambda)
67
hackett/private/provide
78
(only-in hackett/private/toplevel @%top-interaction))
89
(provide (all-from-out hackett/private/adt)
10+
(all-from-out hackett/private/type-alias)
911
(all-from-out hackett/private/class)
1012
(all-from-out hackett/private/kernel)
1113
(all-from-out hackett/private/provide)

hackett-lib/hackett/private/infix.rkt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@
3535
(define (infix-operator? x)
3636
(and (has-prop:infix-operator? x)
3737
(operator-fixity? (infix-operator-fixity x))))
38-
38+
39+
; Infix transformer bindings; use the make-infix-variable-like-transformer constructor instead of
40+
; creating instances of this struct directly.
3941
(struct infix-variable-like-transformer (procedure fixity)
4042
#:property prop:procedure (struct-field-index procedure)
4143
#:property prop:infix-operator
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#lang curly-fn racket/base
2+
3+
(require (for-syntax racket/base
4+
racket/format
5+
6+
hackett/private/infix
7+
hackett/private/typecheck
8+
hackett/private/util/stx)
9+
syntax/parse/define
10+
11+
(only-in hackett/private/adt type-constructor-spec))
12+
13+
(provide type)
14+
15+
(begin-for-syntax
16+
; Alias transformer bindings; use the make-alias-transformer constructor instead of creating
17+
; instances of this struct directly.
18+
(struct alias-transformer (procedure)
19+
#:property prop:procedure (struct-field-index procedure))
20+
21+
(define (make-alias-transformer args type-template)
22+
(let ([arity (length args)])
23+
(alias-transformer
24+
(cond
25+
[(zero? arity)
26+
(make-variable-like-transformer type-template)]
27+
[else
28+
(syntax-parser
29+
[(_ t:type ...)
30+
#:fail-unless (= (length (attribute t)) arity)
31+
(~a "expected " arity " argument(s) to type alias, got "
32+
(length (attribute t)))
33+
(insts type-template (map cons args (attribute t)))]
34+
[:id
35+
#:fail-when #t (~a "expected " arity " argument(s) to type alias")
36+
(error "unreachable")])])))))
37+
38+
39+
(define-syntax-parser type
40+
[(_ ctor-spec:type-constructor-spec {~type type-template:expr})
41+
#:with [ctor-spec*:type-constructor-spec] (type-namespace-introduce #'ctor-spec)
42+
#:fail-when (attribute ctor-spec.fixity) "type aliases do not support infix notation"
43+
44+
; Create a dummy internal definition context with args.
45+
#:do [(define intdef-ctx (syntax-local-make-definition-context))
46+
(syntax-local-bind-syntaxes (attribute ctor-spec*.arg) #f intdef-ctx)]
47+
#:with [arg* ...] (map #{internal-definition-context-introduce intdef-ctx %}
48+
(attribute ctor-spec*.arg))
49+
50+
; Expanding the type in `ctx` checks immediately that it is a valid type,
51+
; rather than deferring that check to when the type alias is applied.
52+
#:with {~var type-template- (type intdef-ctx)} #'type-template
53+
#'(begin
54+
(define-values [] type-template-.residual)
55+
(define-syntax ctor-spec*.tag
56+
(make-alias-transformer
57+
(list (quote-syntax arg*) ...)
58+
(quote-syntax type-template-.expansion))))])
59+
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#lang hackett
2+
3+
(require hackett/private/test
4+
(only-in racket/base submod)
5+
(submod tests/hackett/typecheck assertions))
6+
7+
(type X Integer)
8+
(def x : X 5)
9+
(typecheck-fail (: "" X))
10+
11+
(type (Arr a b) {a -> b})
12+
(type (Pred a) (Arr a Bool))
13+
(type (BiRel a) {a -> a -> Bool})
14+
15+
(type Y (forall [a b] (Monoid b) => (Either a b)))
16+
17+
(typecheck-fail
18+
(λ ([x : (Arr Bool)]) ; not enough args to alias
19+
x))
20+
21+
(defn never : (forall [a] (Pred a))
22+
[[x] False])
23+
24+
(test {(never 5) ==! False})
25+
(test {(never "asdasaf") ==! False})
26+
27+
(def int= : (BiRel Integer)
28+
==)
29+
30+
(test {{4 int= 6} ==! False})
31+
(test {{4 int= 4} ==! True})

0 commit comments

Comments
 (0)