简体   繁体   中英

Is there any way to declare a type representing all the callable procedures(any procedure which is callable) in Typed Racket?

I'm working on SICP's metacircular-evaluator with Typed Racket and stuck when preparing primitive procedures beforehand with a list of cons of symbol and the procedure object. In the book the authors prepare primitive-procedures as below, however, when I mimic it in Typed Racket and define accessors on it, I simply can't extract procedures nor symbols because I need to explicitly instantiate car or cdr with a type which I cannot write down, that is, all the union of procedure types in the list. So I thought that if I can declare a type representing all the callable procedures(without keyword args but with rest args), I can finally annotate the list below. I seriously googled and tried many ways myself but I have come up with no good idea. Can anyone think of a good way to define such a type? Or even better, can anyone come up with a better idea to prepare all the primitive procedures?

(define primitive-procedures
    (list (cons 'car car)
          (cons 'cdr cdr)
          (cons 'list list)
          ...))

The problem is with trying to fit a heterogeneous set of function types into a homogeneous list type / homogeneous lookup-result type.

In the comments you noted that you cannot give the type primitive-procedures : (All (ab) (Listof (Pairof Symbol (-> a * b))) because the type declared differs from every procedure in the actual list. You probably tried adding All because you were trying to accommodate all the heterogeneous types in there. But the Listof type, and more importantly the lookup function you use to get primitives out of the list, have fundamentally homogeneous types. You'll have to make the type homogeneous and work around the types within the list.

If the values in your target language are the same as your host for a meta-circular interpreter, the simplest choice for this homogeneous function type is (-> Any * Any) .

(: primitive-procedures : (Listof (Pairof Symbol (-> Any * Any))))
(: lookup-primitive : (-> (Listof (Pairof Symbol (-> Any * Any))) Symbol (-> Any * Any)))

Then the use of lookup-primitive should be fine using apply . However the most complicated part is the definition of primitive-procedures with this homogeneous type.

Just using

(define primitive-procedures
  (list (cons 'car car)
        (cons 'cdr cdr)
        (cons 'list list)
        ...))

isn't enough and gives a type mismatch.

  • car cannot be used as (-> Any * Any) . It can be used as (-> (Pairof Any Any) Any) .
  • cdr cannot be used as (-> Any * Any) . It can be used as (-> (Pairof Any Any) Any) .
  • list can be used as (-> Any * Any) . That's good.

So we must wrap car and cdr somehow, with functions that check the number of arguments and check that the argument is a pair, before passing the arguments to car and cdr .

(lambda [args : Any *]
  (match args
    [(list arg)
     (unless (pair? arg) (error 'car "wrong argument type"))
     (car arg)]
    [_
     (error 'car "wrong number of arguments")]))

Now this lambda expression can be used as (-> Any * Any) , but it's clunky. It's even worse when you consider we would have to do this for every primitive that doesn't already fit:

(define primitive-procedures
  (list (cons 'car (lambda [args : Any *]
                     (match args
                       [(list arg)
                        (unless (pair? arg) (error 'car "wrong argument type"))
                        (car arg)]
                       [_
                        (error 'car "wrong number of arguments")])))
        (cons 'cdr (lambda [args : Any *]
                     (match args
                       [(list arg)
                        (unless (pair? arg) (error 'cdr "wrong argument type"))
                        (cdr arg)]
                       [_
                        (error 'cdr "wrong number of arguments")])))
        (cons 'list list)
        ...))

This code is ugly and repeats itself a lot. I would define a macro called prim to do this pattern for me:

(require syntax/parse/define)

(define-simple-macro (prim (arg-pred ...) proc)
  #:with (arg-id ...) (generate-temporaries #'(arg-pred ...))
  (lambda [args : Any *]
    (match args
      [(list arg-id ...)
       (unless (arg-pred arg-id) (error 'proc "wrong argument type")) ...
       (proc arg-id ...)]
      [_
       (error 'proc "wrong number of arguments")])))

Then we can use it in primitive-procedures like

(define primitive-procedures
  (list (cons 'car (prim (pair?) car))
        (cons 'cdr (prim (pair?) cdr))
        (cons 'list list)
        ...))

And now it is finally defined with the homogeneous type (Listof (Pairof Symbol (-> Any * Any))) .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM