(defpackage :infer-type (:use :cl :serapeum) (:export )) (in-package :infer-type) (defclass type-env () ((%typedefs :initform (make-hash-table) :initarg :typedefs :reader typedefs) (%typebindings :initform (make-hash-table) :initarg :type-bindings :reader typebindings) (%values :initform (make-hash-table) :reader values-))) (defparameter *type-env* (make-instance 'type-env)) (defmacro with-types ((&rest types) (&rest defs) &body body) `(let ((*type-env* (make-instance 'type-env :typedefs (alexandria:alist-hash-table ',(loop for (type def) in types collect (cons type def))) :type-bindings (alexandria:alist-hash-table ',defs)))) ,@body)) (defun lookup-type (type) (gethash type (typedefs *type-env*))) (defun get-function-type (expr) (gethash (car expr) (typebindings *type-env*))) (defun is-type-variable (tyvar) (and (symbolp tyvar) (char= (elt (symbol-name tyvar) 0) #\?))) (defun is-bound-type-variable (tyvar) (nth-value 1 (gethash tyvar (typebindings *type-env*)))) (defun lookup-type-variable (tyvar) (gethash tyvar (typebindings *type-env*))) (defun infer-simple-type (expr) (loop for k being the hash-keys of (typebindings *type-env*) using (hash-value v) when (subtypep (type-of expr) v) do (return k))) (defun infer-complex-type (expr) (destructuring-bind (ret-type arg-types) (get-function-type expr) )) (defun infer-type (expr) (if (is-simple-expression expr) (infer-simple-type expr) (infer-complex-type expr))) (defun bind-type-variable (expr tyvar) (setf (gethash tyvar (typebindings *type-env*)) (infer-type expr))) (defun check-type- (expr type) (if (is-type-variable type) (if (is-bound-type-variable type) (check-type- expr (lookup-type-variable type)) (bind-type-variable expr type)) (if (is-simple-expression expr) (check-simple-type expr type) (check-complex-type expr type)))) (defun is-simple-expression (expr) (or (not (listp expr)) (eql (car expr) 'quote))) (defun check-simple-type (expr type) (values (subtypep (type-of expr) (lookup-type type)) type)) (defun check-complex-type (expr type) (destructuring-bind (return-type arg-types) (get-function-type expr) (values (and (lookup-type return-type) (subtypep (lookup-type return-type) (lookup-type type)) (= (1- (length expr)) (length arg-types)) (every (lambda (arg-expr arg-type) (and (lookup-type arg-type) (check-type- arg-expr arg-type))) (cdr expr) arg-types)) return-type))) (defun foo (a b) (check-type a number) (check-type b number) (+ a b)) #+null (defmacro with-implicit-conversions ((&rest conversions) &body body) `(let ((num-coercions 0) (coercion-limit 10)) (handler-bind ((type-error (lambda (c) (incf num-coercions) (when (< num-coercions coercion-limit) (let ((datum (type-error-datum c)) (expected-type (type-error-expected-type c))) (store-value (cond ,@(loop for (type handler) in conversions collect `((subtypep ',type expected-type) (,handler datum)))))))))) ,@body)))