简体   繁体   中英

Zip function in typed racket with rest arguments

I'm struggling with the syntax for a function that zips together any number of lists. I currently have:

(define (zip. [lsts: (Listof Any) *]) (apply map (inst list Any) lsts))

Which causes the following error when evaluated:

Error: struct:exn:fail:syntax /Applications/Racket v6.6/collects/racket/private/kw.rkt:929:25: Type Checker: Bad arguments to function in `apply':

Domains: (-> a b... bc) (Listof a) (Listof b)... b

(-> ac) (Pairof a (Listof a))

Arguments: (-> Any * (Listof Any)) (Listof (Listof Any)) *

in: (#%app apply map (#%expression list) lsts)

Since these evaluate okay:

(apply map (inst list Any) '(("asd" 1 2) ("cat" 3 4)));;(("asd" "cat") (1 3) (2 4))

(define (test. [lsts: (Listof Any) *]) lsts) (test '(1 2 3) '(2 3 "dogs"));;((1 2 3) (2 3 "dogs"))

I think the type checker's complaining about apply failing whenever no arguments are passed in, since I get a similar error trying to evaluate the following:

(apply map (inst list Any) '())

Error: struct:exn:fail:syntax /Applications/Racket v6.6/collects/racket/private/kw.rkt:929:25: Type Checker: Bad arguments to function in `apply':

Domains: (-> a b... bc) (Listof a) (Listof b)... b

(-> ac) (Pairof a (Listof a))

Arguments: (-> Any * (Listof Any)) Null *

in: (#%app apply map (#%expression list) (quote ()))

But I'm not sure how to specify to the function that it'll take at least one argument (list).

The function map needs to take at least one list as an argument. Consider what would happen if you called zip with zero arguments. Then you would be calling map with zero lists, which isn't allowed. So, you have to restrict your zip function to take one or more arguments. You can do that by specifying an argument before the rest argument like this:

#lang typed/racket

(define (zip [lst : (Listof Any)] . [lsts : (Listof Any) *])
  (apply map (inst list Any) lst lsts))

One more thing: This would be better if it were polymorphic.

#lang typed/racket

(: zip : (∀ (A) (-> (Listof A) (Listof A) * (Listof (Listof A)))))
(define (zip lst . lsts)
  (apply map (inst list A) lst lsts))

Notice that the domain still needs to be (Listof A) (Listof A) * and not just (Listof A) * .

Update: Even more polymorphism

It's actually possible to make the polymorphism on this even better, so that if you give it exactly 3 lists, it produces a list of exactly-3-element lists. This version of zip would have the type

(: zip : (∀ (A B ...)
            (-> (Listof A)         ; first input
                (Listof B) ... B   ; next n inputs, where n is the number of B type-arguments
                (Listof (List A B ... B)))))

However, if the body were (apply map list lst lsts) , the list function would need the type

(∀ (A B ...) (-> A B ... B (List A B ... B)))

However, list used as a function value only has the type (All (a) (-> a * (Listof a))) . Instead we can define a new function listd , which behaves exactly like list , but with the new type

;; (listd x y z) = (list x y z)
;; but it's assigned a type that's more convenient for `zip`
(: listd : (∀ (A B ...) (-> A B ... B (List A B ... B))))
(define (listd a . bs)
  (cons a bs))

Using this, the dots-polymorphic version of zip can be defined like this:

(: zip : (∀ (A B ...)
            (-> (Listof A)         ; first input
                (Listof B) ... B   ; next n inputs, where n is the number of B type-arguments
                (Listof (List A B ... B)))))
(define (zip lst . lsts)
  (apply map (inst listd A B ... B) lst lsts))

Using it:

> (zip (list 'a 'b 'c) (list 1 2 3) (list "do" "re" "mi"))
- : (Listof (List (U 'a 'b 'c) Positive-Byte String))
'((a 1 "do") (b 2 "re") (c 3 "mi"))

I think the type checker's complaining about apply failing whenever no arguments are passed in

Correct. If you just want to assert that there will be at least one argument, without doing anything fancy with arity polymorphism:

(define example (cast '((1 2) (3 4) (5 6)) (Listof (Listof Number))))
(apply map (inst list Number) (assert example pair?))

The key piece of code here is (assert example pair?) , which asserts that example is not an empty list. Typed Racket did not cause this problem - it is trying to protect you from the possibility of example being empty (with an unhelpful error message). assert bypasses Typed Racket's check without fixing this problem. However, (inst list Number) is required due to limitations in Typed Racket.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM