LAINTYPES: Good enough descriptive types for Common Lisp
I've created a repo for the package which implements an MVP of this system, using algorithms from the paper The Simple Essence of Algebraic Subtyping, specifically implementing Simple-Sub.
I also asked around on reddit for opinions and interest, seems that it would be a nice to have for many people.
Currently the main entrypoint is #'deftun which admits a function and
immediately typechecks it, returning both it and a reified type specifier:
CL-USER>
#<PACKAGE "LAINTYPE">
LAINTYPE>
FOO
And it already checks for some things which the built-in SBCL inference won't
handle. Take the type of identity for example. It is quite nondescript:
LAINTYPE>
ID
(function (t) t) tells us nothing, yet even when id shadows the usage of a
type, laintype still manages to figure out that a number is required:
LAINTYPE>
IDADD
While the SBCL description only knows about T:
LAINTYPE>
#<FUNCTION IDADD>
[compiled function]
Lambda-list:
Derived type:
Ideally I think we'd have a system vaguely similar to Unison's lovely code manager:
LAINTYPE>
UNION
; Types:
; union :: (α) -> (or string integer)
LAINTYPE>
FOO
; Types:
; foo :: (α) -> α
LAINTYPE>
BAR
; Types:
; bar :: () -> number
LAINTYPE>
FOO
; Types:
; foo :: (α) -> string
; bar :: () -> string
LAINTYPE>
BAR
; Types:
; bar :: !
; Culprits:
; in: DEFUN BAR
;
; type mismatch:
; The second argument to #'+ in:
;
; (+ 1 (foo 20))
;
; Requires a type with upper bound `number`,
; however `(foo 20)` has type `string`.
!stands forFaili.e. "this does not typecheck"Similar to Rust's usage such a function will never return in standard cases.
The type of bar is printed even when admitting foo because its type changes.
A function failing its typecheck is still admitted (perhaps unless an option is set), to preserve as much of standard CL behaviour as possible, we aim to be descriptive not prescriptive. The error will persist on screen until it is fixed by the programmer or hidden using some mechanism.
List
of special forms that need to be handled, other symbols are either functions
or macros. Thse can be input into the type database, and don't need their own
cases in the code-walker.