[英]Applying to potentially empty lists in Typed Racket
以下在untyped racket中是沒有問題的:
(apply hc-append
(for/list ([colour '("red" "green" "blue")])
(colorize (filled-rectangle 10 10) colour)))
結果是pict
包含三個不同顏色方塊的圖片。
但這在 Typed Racket 中是不可能的。 首先, for/list
需要一個類型注解:
(apply hc-append
(for/list : (Listof pict)
([colour '("red" "green" "blue")])
(colorize (filled-rectangle 10 10) colour)))
但是, (curry apply hc-append)
的類型和(for/list …)
形式的結果不一致:
; […] Type Checker: Bad arguments to function in `apply':
; Domains: pict pict *
; Real pict pict *
; Arguments: (Listof pict)
這個錯誤信息看起來很費解,但它歸結為(Listof pict)
可能是空的,並且hc-append
的類型簽名需要pict pict *
,即至少一個pict
。
但這適用於有類型和無類型的 Racket:
(apply hc-append (map (curry colorize (filled-rectangle 10 10))
'("red" "green" "blue")))
(map …)
表單的類型是(Pairof pict (Listof pict))
。 偉大的。 那是一個非空列表。 但我不能在for/list
形式中將其用作類型注釋,類型檢查器拒絕它。
即使我仔細挑選類型:
(let ([potentially-empty-list
(for/list : (Listof pict)
([c '("red" "green" "blue")])
(colorize (filled-rectangle 10 10) c))])
(cond
[(null? potentially-empty-list) (blank)]
[else (apply hc-append #{(list (first potentially-empty-list)
(rest potentially-empty-list))
: (Pairof pict (Listof pict))})]))
我最終得到了一個非常令人困惑的消息,本質上是相同的,但現在它違背了我最初的理由,即。 該列表可能為空:類型明確指出它可能不為空! 球拍拒絕它:
; […] Type Checker: Bad arguments to function in `apply':
; Domains: pict pict *
; Real pict pict *
; Arguments: (List pict (Listof pict))
有兩件事讓我感到困惑:
(Pairof pict (Listof pict))
,Racket 將類型變成(List pict (Listof pict))
。 我覺得沒問題,因為兩者應該是等價的。(apply …)
形式能夠工作,就像在(map …)
的情況下一樣,在那里,我們也使用(apply hc-append ls)
where ls
has type (Pairof pict (Listof pict))
。我的直覺是至少最后一個例子應該有效,但它沒有,我不明白為什么。
您的以下代碼非常接近。
(let ([potentially-empty-list
(for/list : (Listof pict)
([c '("red" "green" "blue")])
(colorize (filled-rectangle 10 10) c))])
(cond
[(null? potentially-empty-list) (blank)]
[else (apply hc-append #{(list (first potentially-empty-list)
(rest potentially-empty-list))
: (Pairof pict (Listof pict))})]))
但是,有兩個錯誤。
(list (first....) (rest...))
是一個包含兩個元素的列表,其中第一個元素是一個pict
,第二個元素是一個(Listof pict)
。 即它有類型(List pict (Listof pict))
。 我想你反而想使用(cons (first....) (rest...))
,它有類型(Pairof pict (Listof pict)
= (Listof pict)
保證它至少有一個元素。#{(cons...):: (Pairof...)}
。 #{x: t}
表示法僅在綁定位置有效,尤其是當x
是變量時。 然而, #{e:: t}
表示法允許e
是任意表達式,這就是您在這里所做的。 Typed Racket 在某種意義上確實存在錯誤,因為當x
不是變量時它不會報告誤用#{x: t}
。 我會提交錯誤報告。以下程序工作:
#lang typed/racket
(require typed/pict)
(let ([potentially-empty-list
(for/list : (Listof pict)
([c '("red" "green" "blue")])
(colorize (filled-rectangle 10 10) c))])
(cond
[(null? potentially-empty-list) (blank)]
[else (apply hc-append #{(cons (first potentially-empty-list)
(rest potentially-empty-list))
:: (Pairof pict (Listof pict))})]))
由於 Sorawee 的回答,這是我想出的一個簡化:
(: safe-apply (∀ (E R) (→ R (→ E E * R) (Listof E) R)))
(define (safe-apply def f ls)
(cond
[(null? ls) def]
[else (apply f ls)]))
有趣的是,Racket 中的 Occurrence Typing 將使ls
在cond
的else
子句中具有類型(Pairof E (Listof E))
。
然后可以這樣使用:
(safe-apply (blank)
hc-append
(for/list : (Listof pict)
([colour '("red" "green" "blue")])
(colorize (filled-rectangle 10 10) colour)))
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.