Programming languages should be designed not by piling feature on top of feature, but by removing the weaknesses and restrictions that make additional features appear necessary.
data Maybe a = Just a | Nothing
Just a | Nothing
Data types have four parts:
(define $data-type
(make-rtd 'data-type
'#((immutable type); procedure
(immutable constructor) ; procedure
(immutable predicate) ; procedure
(immutable destructor)) ; procedure
#f 'sealed 'opaque))
(define Just (make-data-type 'Just '(value)))
(define Nothing (make-data-type 'Nothing '()))
(define-data-type Just (value))
(define-data-type Nothing)
Everything is built on this:
(define (call-with-data-type data-type proc)
(call-with-values (lambda () (data-type-destructor data-type)) proc))
(define-syntax new
(syntax-rules ()
((_ type values ...)
(call-with-data-type type (lambda ($ @ ? *) (@ values ...))))))
(define (instance-of? type obj)
(call-with-data-type type (lambda ($ @ ? *) (? obj))))
(define (data-ref obj type fields)
(cond
((list? fields)
(call-with-data-type type
(lambda ($ @ ? *)
(if (? obj)
(call-with-values (lambda () (apply * obj fields)) (lambda result result))
(assertion-violation 'data-ref "Invalid type!" type obj)))))))
(type-lambda x
(Just (a) a)
(Nothing () 'nothing)
(else 'not supported))
(lambda (x)
(call-with-data-type Just
(lambda ($ @ ? *)
(if (? x)
(call-with-values (lambda () (apply * x 'a)) (lambda (a) a))
(call-with-data-type Nothing
(lambda ($ @ ? *)
(if (? x)
(call-with-values
(lambda (apply * x '()))
(lambda () 'nothing)))
'not supported)))))))
examples are type-lambda clauses
(Point ((a y) (b x)) (+ a b))
(hashtable ((x 'hello) (y 'world))) (+ x y))
(vector ((x 1) (y 3)) (+ x y))
(list (x y z) (list z y x))
(\#t "this is true")
(((type-lambda (Point (x y) (and (> x 0) (> y 0))) (else \#f)) . (destructor Point)) (x y) (list x y))
(MuPoint (x y) (x 5) (y))
(define maybe? (type-lambda (Just (a) \#t) (Nothing () \#t) (else #f)))
(when (maybe? x)
(type-case x (Just (a) a) (Nothing () 'nothing) (else \#f)))
(define-type Maybe (Just a) (Nothing))
expands to:
(define-data-type Just a)
(define-data-type Nothing)
(define-syntax Maybe
(syntax-rules (Just Nothing)
((Maybe x (Just clauses (... ...)) (Nothing clauses (... ...)))
(type-case x
(Just clauses ...)
(Nothing clauses ...)
(else \#f)))))
(define-data-type Nil)
(define-data-type Node x l r)
(define tree? (type-lambda (Nil () \#t) (Node (x l r) (and (tree? l) (tree? r)))))
(define-type-class (Eq type) (equals)
(define $data-type
(make-rtd 'data-type
'#((immutable type); procedure
(immutable constructor) ; procedure
(immutable predicate) ; procedure
(immutable destructor)) ; procedure
#f 'sealed 'opaque))
(define (make-type-class name type-parms fields)
(let ((library '())
($funcs (make-rtd name (list->vector (map (lambda (f) `(immutable ,f)) fields)))))
(let ((funcs@ (rtd-constructor $funcs))
(funcs* (rtd-destructor $funcs)))
(define ($ proc) (proc 'type-class type-parms fields library))
(define (@ . rest)
(let ((parms-fields (fold-left
(lambda (a e)
(cond
((< (length (car a)) (length type-parms))
(cons (cons e (car a)) (cdr a)))
(else (cons (car a) (cons e (cdr a)))))) (list '()))))
(cons (cons (reverse (car parms-fields)) (apply funcs@ (reverse (cdr parms-fields)))) library)
#f))
(define (? . instances)
(assp (lambda (parms) (for-each (lambda rest (apply instance-of? rest)) (map list parms instances))) library))
(define (* objs . fs) (apply funcs* (cdr (apply ? objs)) fs))
(make-data $ @ ? *))))