Typed/Racket中函数类型的类型谓词

Typed/Racket中函数类型的类型谓词,racket,typed-racket,Racket,Typed Racket,我正处于设计框架的早期阶段,正在玩弄typed/racket。假设我有以下类型: (define-type Calculate-with-one-number (-> Number Number)) (define-type Calculate-with-two-numbers (-> Number Number Number)) 我想有一个函数,可以根据类型进行分派: (: dispatcher (-> (U Calculate-with-one-number

我正处于设计框架的早期阶段,正在玩弄
typed/racket
。假设我有以下类型:

   (define-type Calculate-with-one-number (-> Number Number))
   (define-type Calculate-with-two-numbers (-> Number Number Number))
我想有一个函数,可以根据类型进行分派:

(: dispatcher (-> (U Calculate-with-one-number Calculate-with-two-numbers) Number))

(define (dispatcher f args)
   (cond [(Calculate-with-one-number? f)
          (do-something args)]
         [(Calculate-with-two-numbers? f)
          (do-something-else args)]
         [else 42]))
如何在
Typed/Racket
中创建类型谓词
用一个数字计算?
用两个数字计算?
?对于非函数谓词,我可以使用
定义谓词
。但是不清楚如何实现函数类型的谓词。

在一般情况下,由于类型是如何在Racket中实现的,所以您不可能想要什么。Racket有一些契约,它们是运行时包装器,用于保护程序的某些部分不受其他部分的影响。函数契约是将函数视为黑盒的包装器-形式为
(->number?number?
的契约可以包装任何函数,新的包装器函数首先检查是否收到一个
number?
,然后将其传递给包装的函数,然后检查包装函数是否返回一个
数字?
。这一切都是动态完成的,每次调用函数时都是如此。类型化Racket增加了静态检查类型的概念,但由于它可以向非类型化模块提供和要求值,这些值由表示其类型的契约保护

在函数
dispatcher
中,您可以在运行时动态地接受函数
f
,然后根据得到的函数类型执行操作。但是函数是黑匣子——契约实际上并不知道它们所包装的函数的任何信息,它们只是检查它们的行为是否正常。无法判断是否为
调度程序
提供了形式为
(>number?number?
的函数或形式为
(>string?string?
的函数。由于
dispatcher
可以接受任何可能的功能,因此这些功能都是黑匣子,没有关于它们接受或承诺什么的信息
dispatcher
只能假设合同中的函数是正确的,并尝试使用它。这也是为什么
define type
不会自动为函数类型生成谓词的原因-无法动态证明函数具有该类型,只能将其包装在契约中并假设其行为

唯一的例外是算术信息——所有函数都知道它们接受多少个参数。该函数将为您提供此信息。因此,虽然通常不能在运行时对函数类型进行分派,但可以对函数的arity进行分派。这就是
case lambda
所做的-它生成一个函数,该函数根据收到的参数数量进行调度:

(: dispatcher (case-> [-> Calculate-with-one-number Number Void]
                      [-> Calculate-with-two-numbers Number Number Void]))

(define dispatcher
  (case-lambda
    [([f : Calculate-with-one-number]
      [arg : Number])
     (do-something arg)]
    [([f : Calculate-with-two-numbers]
      [arg1 : Number]
      [arg2 : Number])
     (do-something-else arg1 arg2)]
    [else 42]))
因为我是自我回答的,所以我冒昧地澄清了我的问题的要点,因为讨论了算术作为一种解决方案。算术上的差异是因为我在指定问题时没有考虑其含义

问题 在
#lang typed/racket
中,与许多Lisp一样,函数或更恰当地说:,是第一类数据类型

默认情况下,
#lang racket
程序类型以及参数类型中的任何附加特殊性都必须通过契约完成。在
#lang typed/racket
中,由于语言的“烘焙契约”,过程是通过算术、参数类型和返回值来键入的

以数学为例 提供了用于定义过程类型的方法:

 (define-type NN (-> Number Number))
这样可以更简洁地指定过程:

 ;; Takes two numbers, returns a number
 (define-type 2NN (-> Number Number Number))

 (: trigFunction1 2NN)
 (define (trigFunction1 x s)
   (* s (cos x)))

 (: quadraticFunction1 2NN)
 (define (quadraticFunction1 x b)
   (let ((x1 x))
     (+ b (* x1 x1))))
目标 在像数学这样的领域中,最好使用更抽象的过程类型,因为知道一个函数在上下界(如
cos
)之间是循环的,而不是只有一个界(如我们的二次函数)或渐近的(如双曲函数)提供关于问题域的更清晰的推理。如果能够访问以下抽象内容,那就太好了:

 (define-type Cyclic2NN (-> Number Number Number))
 (define-type SingleBound2NN (-> Number Number Number))

 (: trigFunction1 Cyclic2NN)
 (define (trigFunction1 x s)
   (* s (cos x)))

 (: quadraticFunction1 SingleBound2NN)
 (define (quadraticFunction1 x b)
   (let ((x1 x))
     (+ b (* x1 x1))))

 (: playTone (-> Cyclic2NN))
 (define (playTone waveform)
   ...)

 (: rabbitsOnFarmGraph (-> SingleBound2NN)
 (define (rabbitsOnFarmGraph populationSize)
   ...)
唉,
define type
在过程中并没有提供这种粒度级别。此外,我们可能很容易通过手动方式为程序编写此类类型差异的短暂错误希望也因以下原因而破灭:

计算为类型t的谓词,类型为(Any->Boolean:t)。t可能不包含函数类型,或者可能引用可变数据的类型,例如(整数向量)

从根本上说,类型的用途超出了静态检查和契约。作为该语言的第一类成员,我们希望能够分派更细粒度的过程类型。从概念上讲,所需要的是符合
Cyclic2NN?
SingleBound2NN?
的谓词。仅仅有用于分派的算术是不够的

非类型球拍的指导 幸运的是,Lisp是特定于领域的语言,一旦我们拉开帷幕展示向导,就可以编写Lisp,最终我们可以得到我们想要的东西。关键是以另一种方式来讨论这个问题,并询问“我们如何使用
类型化/racket
为过程提供的谓词?”

结构是Racket的用户定义的数据类型,是扩展其类型系统的基础。结构非常强大,即使在基于类的对象系统中,“.”

#lang racket
中,结构可以作为过程应用,给出
#:属性
关键字,使用
prop:procedure
,后跟一个过程作为其值。本文档提供了两个示例:

指定要作为过程应用的结构的字段。显然,至少在指出之后,该字段必须包含一个计算结果为过程的值

> ;; #lang racket
> (struct annotated-proc (base note)
     #:property prop:procedure
      (struct-field-index base))
> (define plus1 (annotated-proc
     (lambda (x) (+ x 1))
     "adds 1 to its argument"))
> (procedure? plus1)
#t
> (annotated-proc? plus1)
#t
> (plus1 10)
11
> (annotated-proc-note plus1)
"adds 1 to its argument"
在中,匿名过程[lambda]直接作为属性值的一部分提供。lambda在第一个位置获取一个操作数,该操作数被解析为用作过程的结构的值。这允许访问存储在结构的任何字段中的任何值
> ;; #lang racket
> (struct greeter (name)
    #:property prop:procedure
    (lambda (self other)
       (string-append
         "Hi " other
          ", I'm " (greeter-name self))))
> (define joe-greet (greeter "Joe"))
> (greeter-name joe-greet)
"Joe"
> (joe-greet "Mary")
"Hi Mary, I'm Joe"
> (joe-greet "John")
"Hi John, I'm Joe
    (define-struct/exec name-spec ([f : t] ...) [e : proc-t])

      name-spec     =       name
                    |       (name parent)
#lang typed/racket

(define-type 2NN (-> Number Number Number))

(define-struct/exec Cyclic2NN
   ((f : 2NN))
   ((lambda(self x s)
     ((Cyclic2NN-f self) x s))
      : (-> Cyclic2NN Number Number Number)))

 (define-struct/exec SingleBound2NN
   ((f : 2NN))
   ((lambda(self x s)
     ((SingleBound2NN-f self) x s))
       : (-> SingleBound2NN Number Number Number)))

 (define trigFunction1 
   (Cyclic2NN 
    (lambda(x s) 
      (* s (cos x)))))

(define quadraticFunction1
  (SingleBound2NN
    (lambda (x b)
      (let ((x1 x))
        (+ b (* x1 x1)))))
> (SingleBound2NN? trigFunction1)
- : Boolean
#f
>  (SingleBound2NN? quadraticFunction1)
- : Boolean
#t