"Ordinary" functions are usually only defined on a domain of objects of a given type, but certain functions, such the Scheme type predicates list?
or procedure?
, are defined for arguments of any type, and can even be applied to themselves. So e.g. (list? procedure?)
evaluates to #f
and (procedure? procedure?)
evaluates to #t
. I am trying to figure out how one would write a totally defined function of this sort, but haven't been able to find a source that discusses this.
For example, suppose we implement a pair data type as described in Exercise 2.4 of Structure and Interpretation of Computer Programs using the following constructor and selectors:
(define (cons x y)
(lambda (m) (m x y)))
(define (car z)
(z (lambda (p q) p)))
(define (cdr z)
(z (lambda (p q) q)))
How would I then define a predicate pair?
that returns #t
for anything that was constructed using cons
, and #f
for anything that wasn't? More generally, how are type predicates like list?
and procedure?
implemented?
A predicate is a procedure that always returns a boolean value (#t or #f). An equivalence predicate is the computational analogue of a mathematical equivalence relation (it is symmetric, reflexive, and transitive).
Scheme numbers are either exact or inexact. A number is exact if it was written as an exact constant or was derived from exact numbers using only exact operations. A number is inexact if it was written as an inexact constant, if it was derived using inexact ingredients, or if it was derived using inexact operations.
In this case, that means it will check if (number? x) returns true, where x is exp . If it does, the function will return whatever you passed to it as exp . If not, it will raise a match exception.
However since we can code information in the name it should contain to the point what it is about, which can be any word or even sentence delimited by hyphens, ending with question mark to indicate that it is indeed a predicate procedure.
That is easy. Redefine you procedures to have the first argument the type of object you are creating:
(define +type-pair+ 'pair)
(define +type-number+ 'number)
(define (cons a d)
(lambda (m) (m +type-pair+ a d)))
(define (type x)
(x (lambda (t . rest) t)))
(define (pair? c)
(eq? (type c) +type-pair+))
(define (car c)
(if (pair? c)
(c (lambda (t a d) a))
(error "Argument is not pair!")))
(define (cdr c)
(if (pair? c)
(c (lambda (t a d) d))
(error "Argument is not pair!")))
(define (number? c)
(if (type c) +type-number+))
You may use this to define everything in your language, even procedures, but all must support giving it's type with (type x) and all procedures that use something must make sure the type is correct. Implementing pairs as closures are probably the slowest way so it's just SICP thought experiment to make you understand what closures are. Arc uses tags for all data types, but keeps data as data. you should have a look at Arc to see how tagging can be a way to go.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With