Is it possible to write something similar to C++'s
template <typename... T> std::string dup(int n, std::string s, T... tail) {
std::string result;
for (int i = 0; i < n; ++i) {
result += s;
}
if constexpr (sizeof...(tail) == 0) {
return result;
} else {
return result + dup(tail...);
}
}
std::cout << dup(1, std::string("abc"), 2, std::string("xyz"), 3, std::string("a")) << std::endl; // abcxyzxyzaaa
I tried to write the following function in TypedRacket
#lang typed/racket
(: repeat-string (-> Integer String String))
(define (repeat-string n str)
(apply string-append (build-list n (λ (_) str))))
(: dup (All (T ...) (-> Integer String T ... T String)))
(define (dup n s . tail)
(define result (repeat-string n s))
(cond [(empty? tail) result]
[else (string-append result (apply dup tail))])
)
but i have typecheck error:
Type Checker: Bad arguments to function in 'apply': Domain: Integer String T ... T Arguments: (List T ... T) in: (apply dup tail)
Honestly, i dont understand why arguments are (List T ... T)
but not (T ... T)
. Moreover, the function is correct, untyped Racket produces the correct results
The key to doing this with proper type safety is to specify the type of your function using ->*
with a #:rest-star
keyword:
#lang typed/racket
;; Version that avoids building an intermediate list
(: repeat-string : Integer String -> String)
(define (repeat-string n s)
(define len (string-length s))
(build-string (* n len) (lambda (i) (string-ref s (remainder i len)))))
(: dup (->* (Integer String) #:rest-star (Integer String) String))
(define (dup n s . rest)
(if (null? rest)
(repeat-string n s)
(string-append (repeat-string n s) (apply dup rest))))
(dup 1 "abc" 2 "xyz" 3 "a") ; "abcxyzxyzaaa"
If your function takes a list directly, you can use Rec
and List*
to create the type signature. You can also then define dup
in terms of it:
(: dup* : (Rec IntStrRepeated (U Null (List* Integer String IntStrRepeated))) -> String)
(define (dup* pairs)
(if (null? pairs)
""
(string-append (repeat-string (car pairs) (cadr pairs)) (dup* (cddr pairs)))))
(: dup (->* (Integer String) #:rest-star (Integer String) String))
(define (dup n s . rest)
(dup* (list* n s rest)))
(dup* '(1 "abc" 2 "xyz" 3 "a")) ; "abcxyzxyzaaa"