如何在typed/racket申请?

How to apply in typed/racket?

typed/racket 我有一个像 [(? procedure? p ) (apply p xv*)] 这样的案例 会导致错误:

Type Checker: Function has no cases in: (apply p xv*)

所以我写了一个测试用例来检测原因:

#lang typed/racket

(: test-match-apply-0 (-> (-> Any * Any) (Listof Any) Any))
(define test-match-apply-0
  (lambda (x args)
    (match x
      [(? procedure? p) (apply p args)])))
;; Type Checker: Function has no cases in: (apply p args)

(test-match-apply-0 + (list 1 2 3))  ;; not ok

(apply + (list 2 4))  ;; ok



(: test-match-apply-1 (-> (-> (Listof Any) Any) (Listof Any) Any))
(define test-match-apply-1
  (lambda (x args)
    (match x
      [(? procedure? p) (apply p args)])))

(test-match-apply-1 + (list 1 2 3))  ;; not ok


;; For int is it right


(: test-match-apply-2 (-> (-> (Listof Any) Any) (Listof Number) Number))
(define test-match-apply-2
  (lambda (x args)
    (match x
      [(? procedure? p) (apply p args)])))

(test-match-apply-2 + (list 1 2 3))  ;; not ok



(: test-match-apply-3 (-> (-> Number * Number) (Listof Number) Number))
(define test-match-apply-3
  (lambda (x args)
    (match x
      [(? procedure? p) (apply p args)])))

(test-match-apply-3 + (list 1 2 3))  ;; it is ok 

我打印 + 本身:

> (:print-type +)
(case->
 (-> Zero)
 (-> Number Number)
 (-> Zero Zero Zero)
 (-> Number Zero Number)
 (-> Zero Number Number)
 (-> Positive-Byte Positive-Byte Positive-Index)
 (-> Byte Byte Index)
 (-> Positive-Byte Positive-Byte Positive-Byte Positive-Index)
 (-> Byte Byte Byte Index)
 (-> Positive-Index Index Positive-Fixnum)
 (-> Index Positive-Index Positive-Fixnum)
 (-> Positive-Index Index Index Positive-Fixnum)
 (-> Index Positive-Index Index Positive-Fixnum)
 (-> Index Index Positive-Index Positive-Fixnum)
 (->* (Index Index) (Index) Nonnegative-Fixnum)
 .....

回到我的原始需求,我怎样才能在typed/racket中实现[(? procedure? p ) (apply p xv*)]?因为在这种情况下我无法检测到 p 的类型。像 type-apply 这样的东西?

Typed Racket 不能 apply 该过程的原因是除了它是一个过程这一事实之外,它对它一无所知。它可能不接受任何参数,例如,在这种情况下 apply 会导致运行时错误。它可能需要不同的 kind 参数,或者它甚至可能需要关键字参数。 TR 仅从成功的 procedure? 谓词中不知道任何这些,因此它不允许您调用这样的值。

这很棘手,因为没有谓词可以让您检查有关函数的足够详细信息,从而可以安全地应用它。你基本上有两个选择:

  1. 限制输入的类型,以便procedure?将其限制为特定的函数类型。您可以通过使输入成为特定类型的联合来做到这一点。例如,这个类型检查:

    (: constrained ((U String Number (String * -> String)) -> String))
    (define (constrained x)
      (match x
        [(? string?) x]
        [(? number?) (number->string x)]
        [(? procedure?) (apply x '("a" "b" "c"))]))
    

    虽然这里的类型是联合类型,但由于只有一种可能的情况procedure?谓词为真,TR可以将类型限制为适当的适用值。

    函数本身的类型可以很花哨,TR 仍然可以弄清楚。例如,它仍然适用于多态类型:

     (: poly-constrained (All [a] (U String Number (a * -> String)) (Listof a) -> String))
     (define (poly-constrained x lst)
       (match x
         [(? string?) x]
         [(? number?) (number->string x)]
         [(? procedure?) (apply x lst)]))
    
  2. 或者,您可以使用 cast。这将允许您告诉 TR 执行动态检查某个值是否匹配特定类型。

    (: unconstrained (Any -> String))
    (define (unconstrained x)
      (match x
        [(? string?) x]
        [(? number?) (number->string x)]
        [(? procedure?) (apply (cast x (String * -> String)) '("a" "b" "c"))]))
    

    不过注意,这个有点大危险!使用 cast:

    有几个陷阱
    • 检查为单个值生成一个 typed/untyped 边界,实际上是类型化模块和非类型化模块之间的相同类型的边界。这意味着 cast 生成一个在运行时检查的合同,与静态类型不同,它需要时间并且如果在紧密循环中使用会显着降低性能。

    • 由于 cast 动态执行检查,您失去了 Typed Racket 的主要优点之一:静态类型安全。例如,如果某人提供的过程与给定类型不匹配,则会发生 运行时错误,这正是 Typed Racket 旨在防止的事情。

如果可能,您可能希望使用第一种方法,以免损害类型安全,但在谓词不够好的情况下,您可以使用cast。在选择它之前,请注意它的缺点。