macrotypes/tapl/stlc+overloading.rkt
2015-10-22 15:20:16 -04:00

165 lines
6.0 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")
(reuse #:from "stlc+rec-iso.rkt") ; to load current-type=?
(extends "stlc+sub.rkt" #:except #%datum)
;; Revision of overloading, using identifier macros instead of overriding #%app
;; =============================================================================
(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
) #:constructor-name make-
#:transparent
#:property prop:procedure
(lambda (self τ-or-e #:exact? [exact? #f])
(define r
(if (syntax? τ-or-e) ;; Can I ask "type?"
(-resolve-syntax self τ-or-e #:exact? exact?)
(-resolve-value self τ-or-e #:exact? exact?)))
(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)
(make- name (box '()) τ-cod))
(define (->type #:subst [τ-dom (assign-type #''α #'#%type)])
((current-type-eval) #`( #,τ-dom #,(-cod ))))
(define (-find τ #:=? =?)
(define (τ=? τ2)
(=? τ τ2))
(assf τ=? (unbox (-dom* ))))
(define (-resolve-syntax τ #:exact? [exact? #f])
;; First try exact matches, then fall back to subtyping (unless 'exact?' is set).
;; When subtyping, the __order instances were declared__ resolves ties.
(define result
(or (-find τ #:=? (current-type=?))
(and (not exact?)
(-find τ #:=? (current-typecheck-relation)))))
(and (pair? result)
(cdr result)))
(define (-resolve-value e #:exact? [exact? #f])
(error ' (format "Runtime resolution not implemented. Anyway your value was ~a" e)))
(define (-unbound? τ)
(not (-resolve-syntax τ #:exact? #t)))
(define (syntax-> id)
;; Don't care about the type
(define stx+τ (infer+erase id))
;; Boy, I wish I had a monad
(define (fail)
(error 'resolve (format "Identifier '~a' is not overloaded" (syntax->datum id))))
(unless (pair? stx+τ) (fail))
(define stx (car stx+τ))
(unless (syntax? stx) (fail))
(define -stx (syntax->datum (car stx+τ)))
(unless (and (list? -stx)
(not (null? -stx))
(not (null? (cdr -stx))))
(fail))
(define (cadr -stx))
(unless (? ) (fail))
)
(define-syntax-rule (error-template sym id τ reason)
(error sym (format "Failure for '~a' at type '~a'. ~a"
(syntax->datum id)
(syntax->datum τ)
reason)))
(define-syntax-rule (instance-error id τ reason)
(error-template 'instance id τ reason))
(define-syntax-rule (resolve-error id τ reason)
(error-template 'resolve id τ reason))
)
;; =============================================================================
;; === Overloaded signature environment
(define-typed-syntax signature
[(_ (name:id α:id) τ)
#:with ((α+) (~→ τ_α:id τ-cod) _) (infer/tyctx+erase #'([α : #%type]) #'τ)
(define (-init #'name #'τ-cod))
( (define-syntax name
(syntax-parser
[_:id
#'(quote #,)] ;; Is there a way to transmit directly?
[(n e)
#:with [e+ τ+] (infer+erase #'e)
#:with n+ (#, #'τ+)
( (#%app n+ e+)
: τ-cod)]
[(_ e* (... ...))
#'(raise-arity-error (syntax->datum name) 1 e* (... ...))]))
: Bot)]
[(_ e* ...)
(error 'signature (format "Expected (signature (NAME VAR) (→ VAR τ)), got ~a" (xerox #'(e* ...))))])
(define-typed-syntax resolve/tc #:export-as resolve
[(_ name:id τ)
#:with τ+ ((current-type-eval) #'τ)
;; Extract a resolver from the syntax object
(define (syntax-> #'name))
;; Apply the resolver to the argument type. woo-wee!
( #,( #'τ+ #:exact? #t) : #,(->type #:subst #'τ+))])
(define-typed-syntax instance
[(_ (name:id τ-stx) e)
#:with τ ((current-type-eval) #'τ-stx)
#:with [e+ τ+] (infer+erase #'e)
(define (syntax-> #'name))
(unless (-unbound? #'τ) (instance-error #'name #'τ "Overlaps with existing instance."))
(define _unify ;; Should be a helper function
(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))
)]))
;; Should we use syntax instead of e+ ?
(-add! #'τ #'e+)
( (void) : Bot)]
[_
(error 'instance "Expected (instance (id τ) e).")])