macrotypes/tapl/stlc+overloading-param.rkt

164 lines
5.8 KiB
Racket
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#lang s-exp "typecheck.rkt"
(reuse List cons nil #:from "stlc+cons.rkt")
(extends "stlc+sub.rkt" #:except #%datum #:rename [#%app stlc:#%app])
(reuse #:from "stlc+rec-iso.rkt") ; load current-type=?
;; Apparently cannot propogate type information across statements.
;; Until the first-class ones work, let's do the big dumb parameter
;; So here's what going to happen
;; - current-Σ will be a map from identifiers to resolvers
;; - resolvers will map overloaded signatures and types to concrete instances
;; - extending a resolver (via instance) will add a new (τ, e) pair to a mutable list
;; =============================================================================
(define-base-type Bot)
(define-base-type Str)
(define-typed-syntax #%datum
[(_ . n:str) ( (#%datum . n) : Str)]
[(_ . x) #'(stlc+sub:#%datum . x)])
(define-for-syntax xerox syntax->datum)
;; =============================================================================
;; === Resolvers
(begin-for-syntax
(struct (
name ;; Symbol
dom* ;; (Box (Listof (Pairof Type Expr)))
cod ;; Type
) #:transparent
#:property prop:procedure
(lambda (self τ-or-e)
(define r
(if (syntax? τ-or-e) ;; Can I ask "type?"
(-resolve-syntax self τ-or-e)
(-resolve-value self τ-or-e)))
(or r
(error ' (format "Resolution for '~a' failed at type ~a"
(syntax->datum (-name self))
τ-or-e))))
)
;; Rad!
(define (-add! τ e)
(define dom* (-dom* ))
(set-box! dom* (cons (cons τ e) (unbox dom*))))
(define (-init name τ-cod)
( name (box '()) τ-cod))
(define (->type )
((current-type-eval) #`( #,(assign-type #''α #'#%type) #,(-cod ))))
(define (-find τ)
(define τ=?
(let ([type=? (current-type=?)])
(lambda (τ2)
(type=? τ τ2))))
(assf τ=? (unbox (-dom* ))))
(define (-resolve-syntax τ)
(define result (-find τ))
(and (pair? result)
(cdr result)))
(define (-resolve-value e)
(error ' (format "Runtime resolution not implemented. Anyway your value was ~a" e)))
(define (-unbound? τ)
(not (-find τ)))
)
;; =============================================================================
;; === Overloaded signature environment
(begin-for-syntax
(define current-Σ (make-parameter (lambda (id) #f)))
)
(define-typed-syntax signature
[(_ (name:id α:id) τ)
#:with ((α+) (~→ τ_α:id τ-cod) _) (infer/tyctx+erase #'([α : #%type]) #'τ)
(let ([-old ((current-Σ) #'name)])
(when -old
(error 'signature (format "Identifier '~a' already bound to a type ~a"
(syntax->datum #'name) (syntax->datum (->type -old))))))
(define (-init #'name #'τ-cod))
(current-Σ
(let ([old-Σ (current-Σ)])
(lambda (id)
(if (free-identifier=? id #'name)
(old-Σ id)))))
( (define name #,)
: Bot)]
[(_ e* ...)
(error 'signature (format "Expected (signature (NAME VAR) (→ VAR τ)), got ~a" (xerox #'(e* ...))))])
;; TODO make this available to users
(define-for-syntax (resolve stx)
(syntax-parse stx
[(name:id τ)
#:with [name+ τ_fn+] (infer+erase #'name)
#:with τ+ ((current-type-eval) #'τ)
(define ((current-Σ) #'name+))
(unless
(error 'resolve (format "Identifier '~a' is not overloaded" (syntax->datum #'name+))))
( #'τ)]))
(define-typed-syntax #%app
[(_ e_fn:id e_arg)
#:with [e_fn+ τ_fn+] (infer+erase #'e_fn)
;; Beware 3D syntax
#:when ((current-Σ) #'e_fn+)
#:with [e_arg+ τ_arg+] (infer+erase #'e_arg)
(unless (syntax-e #'τ_arg+)
(error '#%app "No type for expression ~a\n" (syntax->datum #'e_arg)))
(define fn (resolve #'(e_fn+ τ_arg+)))
#`(#%app #,fn e_arg+)]
[(_ e* ...)
#'(stlc:#%app e* ...)])
(begin-for-syntax
(define-syntax-rule (instance-error id τ r)
(error 'instance (format "Cannot make instance for '~a' at type '~a'. ~a"
(syntax->datum id) (syntax->datum τ) r)))
)
(define-typed-syntax instance
[(_ (name:id τ-stx) e)
#:with τ ((current-type-eval) #'τ-stx)
#:with [e+ τ+] (infer+erase #'e)
(define ((current-Σ) #'name))
(unless (instance-error #'name #'τ "Not an overloaded identifier."))
(unless (-unbound? #'τ) (instance-error #'name #'τ "Overlaps with existing instance."))
(define does-this-id-matter?
(syntax-parse #`(τ+ #,(->type ))
[((~→ τ_dom1 τ_cod1)
(~→ _ τ_cod2))
;; Really, need to unify this type with the template
;; (unless ((current-type=?) τ_dom1 τ_dom2)
;; (instance-error #'name #'τ (format "Domain '~a' must unify with template domain '~a'."
;; (syntax->datum #'τ_dom1) (syntax->datum #'τ_dom2))))
(unless ((current-type=?) ((current-type-eval) #'τ) #'τ_dom1)
(instance-error #'name #'τ (format "Domain '~a' must be the instance type, for now (2015-10-20)." (syntax->datum #'τ_dom1))))
(unless ((current-type=?) #'τ_cod1 #'τ_cod2)
(instance-error #'name #'τ (format "Codomain '~a' must match template codomain '~a'"
(syntax->datum #'τ_cod1) (syntax->datum #'τ_cod2))))
(void)]
[(a b)
(instance-error #'name #'τ (format "May only overload single-argument functions. (Got ~a and ~a)"
(syntax->datum #'a) (syntax->datum #'b))
)]))
(-add! #'τ #'e+) ;; Should we use syntax instead of e+ ?
( (void) : Bot)]
[_
(error 'instance "Expected (instance (id τ) e).")])