【问题标题】:Casting to arbitrary type in Typed Racket folding a Tree在 Typed Racket 中转换为任意类型折叠树
【发布时间】:2021-07-11 20:53:27
【问题描述】:

我正在尝试生成一个类型化的 Racket 过程,该过程对于某些类型 A,采用 Tree,以及从两个 As 到 A 的函数,另一个类型为 A 的参数,并返回一个A 类型的值。我对(All) 语法不是很熟悉,但我尝试过使用它。不幸的是,我的代码在构建时产生了以下错误消息:

Type Checker: Polymorphic function `foldr' could not be applied to arguments:
Types: (-> a b b) b (Listof a)  -> b
       (-> a b c c) c (Listof a) (Listof b)  -> c
       (-> a b c d d) d (Listof a) (Listof b) (Listof c)  -> d
Arguments: (-> A A A) A (Listof Any)
Expected result: A

我的代码:

(: fold : (All (A) (Instance Tree) (A A -> A) A -> A))
(define (fold tree f base)
  (foldr
    f
    base
    (cons
      (value tree)
      (map 
        (lambda
          ([tree : (Instance Tree)])
          (fold tree f base)
          ) 
        (children tree)
        )
      )
    )
  )

我试图简化函数直到它开始工作,这就是它开始工作的地方:

(: fold : (All (A) (Instance Tree) (A A -> A) A -> A))
(define (fold tree f base)
  (foldr f base (list base))
)

我认为正在发生的事情是类型检查器不知道(value tree) 也是A 类型。有什么办法可以让(Cast) 成为A 类型?如果没有,我将如何实现它?

【问题讨论】:

  • 您需要在您的问题中包含“树”类型,它没有内置在 Typed Racket 中。更一般地说,我想知道您是否真的想使用类和对象;这可能会给这个问题带来巨大的复杂性。 (请原谅我假设这是家庭作业,但是)如果问题陈述需要类或提供此代码,那么当然,您会坚持下去,但如果不是,您是否考虑过只使用结构?

标签: tree racket fold parametric-polymorphism typed-racket


【解决方案1】:

没有Tree 类型的定义很难回答这个问题。但一般来说,答案不是强制转换,而是拥有一种类型,即具有某种类型的 of 节点的树。我不了解 Racket 中的类,尤其是它们与类型化 Racket 的交互(这似乎都可能发生变化),但这里是你如何使用structs 来做到这一点的,它们在类型化 Racket 中得到了很好的支持:

(struct (A) tree
  ((value : A)
   (children : (Listof (Treeof A))))
  #:type-name Treeof)

现在我可以检查一下:

> (tree 1 (list (tree -1 '())))
- : (Treeof (U Negative-Fixnum One))
#<tree>
> (tree 1 (list (tree 'x '())))
- : (Treeof (U 'x One))
#<tree>

好的,它计算出来的类型有点无用,但它们是正确的。

一旦你这样做了,它就会知道tree-valuetree-children的好处:

> tree-value
- : (All (A) (-> (Treeof A) A))
#<procedure:tree-value>
> tree-children
- : (All (A) (-> (Treeof A) (Listof (Treeof A))))
#<procedure:tree-children>

这足以编写你的函数(我不确定它是否正确,但它的类型现在有意义):

(: fold-tree (All (A) (-> (Treeof A) (-> A A A) A A)))
(define (fold-tree tree f base)
  (foldr f base
         (cons
          (tree-value tree)
          ;; I don't know why it needs this since it knows about tree-children
          (map (λ ((child : (Treeof A)))
                 (fold-tree child f base))
               (tree-children tree)))))

请注意,由于某种原因,它无法确定(map (λ (child) ...) (tree-children tree)) 中的child(Treeof A),即使它知道tree 是并且它知道tree-children 做了什么。所以我不得不告诉它(旧版本有问题,使用tree而不是child,隐藏了这个问题。

【讨论】:

  • 试试(define (fold-tree tree f base) (f (tree-value tree) (foldr (λ (child r) (fold-tree child f r)) base (tree-children tree))))。它有效吗?我希望它的类型是(: fold-tree (All (A R) (-&gt; (Treeof A) (-&gt; A R R) R R)))(如果这是写下它的正确方法......)----另外,你有一个错字,它应该是 lambda 内的(fold-tree child f base)
  • @WillNess:您的版本至少输入正确,但您需要围绕lambda 使用大量显式类型(我不知道为什么)。感谢您在我的 OP 音译中找到错误,这意味着我现在还需要 lambda 周围的类型。要么是我遗漏了某些东西,要么是静态类型系统,而我根本就没有继续下去。 (另外,Racket 对未使用的绑定没有警告是怎么回事?说真的,已经是 1975 年了。)
  • 您可以将该类型简化为您拥有的类型。也许那时它就不需要那么多注释了。
  • @WillNess:看起来(map (lambda (x) ...) y) 丢失了有关y 元素类型的信息,即使(我认为)它应该能够知道它。
  • 我的意思是在我的版本中。那里没有map。 :)
【解决方案2】:

使用the answer by tfb 中的结构类型定义,

#lang typed/racket

(struct (A) tree
  ((value : A)
   (children : (Listof (Treeof A))))
  #:type-name Treeof)

以下定义有效(有点):

(: fold-tree (All (A R) (-> (Treeof A) (-> A R R) R R)))
(define (fold-tree tree f base)
  (f (tree-value tree)
     (foldr
          (lambda ((child : (Treeof A)) (r : R))
            (fold-tree child f r))
          base
          (tree-children tree))))

现在在交互窗口中尝试,

(fold-tree (tree 1 (list (tree 2 '()))) + 0)

工作并返回

- : Integer [more precisely: Nonnegative-Integer]
3

但尝试看似等效的方法

(fold-tree (tree 1 (list (tree 2 '()))) (lambda (a r) (+ a r)) 0)

会出现很多错误。

更新: 感谢 tfb 的输入,以下调用都可以正常工作:

> (fold-tree (tree 1 (list (tree 2 '()))) (lambda ((a : Number) (r : Number)) (+ a r)) 0)
- : Number
3

> (fold-tree (tree 1 (list (tree 2 '()))) (lambda ((a : Any) (b : Any)) (cons a b)) 0)
- : (U Zero (Pairof Any Any))
'(1 2 . 0)

> (fold-tree (tree 1 (list (tree 2 '()))) (lambda ((a : Any) (b : Any)) (cons a b)) '())
- : (U Null (Pairof Any Any))
'(1 2)

> (fold-tree (tree 1 (list (tree 2 '()))) (lambda ((a : Any) (b : (Listof Any)))
     (cons a b)) '())
- : (Listof Any)
'(1 2)

> (fold-tree (tree 1 (list (tree 2 '()))) (lambda ((a : Number) (b : (Listof Number))) 
     (cons a b)) '())
- : (Listof Number)
'(1 2)

> (fold-tree (tree 1 (list (tree 2 '()))) (lambda (a b) (cons a b)) '())
- : (U Null (Pairof Any Any))
'(1 2)

> (fold-tree (tree 1 (list (tree 2 '()))) cons '())
*** Type Checker: Polymorphic function `fold-tree' could not be applied to arguments:
Argument 1:
  Expected: (Treeof A)
  Given:    (Treeof Positive-Byte)
Argument 2:
  Expected: (-> A R R)
  Given:    (All (a b) (case-> (-> a (Listof a) (Listof a)) (-> a b (Pairof a b))))
Argument 3:
  Expected: R
  Given:    Null
 in: (fold-tree (tree 1 (list (tree 2 (quote ())))) cons (quote ()))
> 

【讨论】:

  • 是的,你需要把它装饰成(fold-tree ... (lambda ((x : Number) (y : Number)) (+ x y)) 0),比如说(这是在Racket 8.0中)。看起来类型化的 Racket 无法推断 (f ... (lambda (x) ...)) 之类的类型,我想是因为当它做它的时候它只能看到函数对象,而不是导致它的来源(但我不知道)。
  • @tfb 谢谢,我已经用更多工作注释示例更新了答案。有趣的是,cons 则相反:不适用于裸 cons,但适用于包裹在未注释 lambda 中的 cons
猜你喜欢
  • 1970-01-01
  • 2017-04-01
  • 2015-06-28
  • 2018-05-14
  • 1970-01-01
  • 1970-01-01
  • 2023-03-17
  • 1970-01-01
  • 2014-05-27
相关资源
最近更新 更多