在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)
.....
回到我的原始需求,如何在[(? procedure? p ) (apply p xv*)]
中使typed/racket
成为可能?因为在这种情况下我无法检测p
的类型。像type-apply
这样的东西?
答案 0 :(得分:4)
Typed Racket不能apply
该程序的原因是因为除了它是一个程序之外它对它一无所知。它可能不会采用任何参数,例如,apply
会导致运行时错误。它可能需要不同的种参数,或者甚至可能需要关键字参数。 TR只是从成功的procedure?
谓词中知道这一点,所以它不允许你调用这样的值。
这很棘手,因为没有谓词可以让您检查有关该函数的足够详细信息,以便安全应用。你基本上有两个选择:
约束输入的类型,以便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)]))
或者,您可以使用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
:
该检查为单个值生成类型化/非类型化边界,实际上是类型化和非类型化模块之间的相同类型的边界。这意味着cast
生成一个在运行时检查的契约,与静态类型不同,它与时间紧密相关,如果在紧密循环中使用,可能会显着降低性能。
由于cast
动态执行检查,您将失去Typed Racket的一个主要好处:静态类型安全。例如,如果某人提供了与给定类型不匹配的过程,则会发生运行时错误,这正是Typed Racket旨在防止的类型。
如果可能,您可能希望使用第一种方法,以免损害类型安全性,但在谓词不够好的情况下,可以使用cast
。在选择它之前,请注意缺点。