In a sense, you put your finger on a sore spot of TR from R -- we are still
figuring out how to help programmers along here. I rewrote your program like
this:
#lang typed/racket
(: 1+ (Nonnegative-Integer -> Positive-Integer))
(define (1+ n)
(+ n 1))
;; entirely unnecessary but helped me read; IGNORE
(define-type (Maybe α) (U False α))
(define-type NI Nonnegative-Integer)
(define-type LNI (Listof NI))
(define-type VNI (Vectorof NI))
(let* ((idx+level (map #{vector->list @ NI} (cast '(#(1 2) #(3 4) #(5 6))
(Listof VNI))))
(res (#{make-vector @ [Maybe NI]} (1+ (apply max (map #{car @ NI LNI}
idx+level))) #f)))
(for-each (λ ((p : LNI))
(let ((i (car p))
(l (cadr p)))
(vector-set! res i l)))
idx+level)
res)
;; expect
'#(#f 2 #f 4 #f 6)
I focused on what the type checker really needs -- hints on how to specialize
those functions, and only those, that depend on your initial insight that the
TC needs a hand with make-vector. Then I checked on the maps and realized that
I need to help the TC along with the instantiations of vector->list (how can it
know with context-free reasoning what kind of vector it is) and the same for
car (how can it know what kind of list you're getting here).
[[ Also, I don't know why this code looks like it does. There are alternative:
#lang typed/racket
(: 1+ (Nonnegative-Integer -> Positive-Integer))
(define (1+ n)
(+ n 1))
;; entirely unnecessary but helped me read
(define-type (Maybe α) (U False α))
(define-type NI Nonnegative-Integer)
(define-type LNI (Listof NI))
(define-type VNI (Vectorof NI))
(let* ((i0 (cast '(#(1 2) #(3 4) #(5 6)) [Listof VNI]))
(idx+level (map #{vector->list @ NI} i0))
(res (#{make-vector @ [Maybe NI]} (1+ (apply max (map #{car @ NI LNI}
idx+level))) #f)))
(for ((q : [Vectorof NI] (in-list i0)))
(define i (vector-ref q 0))
(define l (vector-ref q 1))
(vector-set! res i l))
res)
]]
On Aug 3, 2014, at 6:46 PM, Norman Gray wrote:
>
> Greetings.
>
> Short version: I repeatedly find myself adding TR type annotations by
> trial-and-error -- I'm sure I'm missing something.
>
> I had a particularly acute case of this this afternoon. I have a bit of
> typed/racket/base code as follows:
>
> (: 1+ (Nonnegative-Integer -> Positive-Integer))
> (define (1+ n)
> (+ n 1))
>
> (define map1 #{map @ (Listof Nonnegative-Integer) (Vectorof
> Nonnegative-Integer)})
> (define map2 #{map @ Nonnegative-Integer (Listof Nonnegative-Integer)})
> (let* ((idx+level (map1 vector->list
> (cast '(#(1 2) #(3 4) #(5 6))
> (Listof (Vectorof Nonnegative-Integer)))))
> (res (#{make-vector @ (U Nonnegative-Integer False)} (1+ (apply max
> (map2 car idx+level))) #f)))
> (for-each (λ: ((p : (Listof Nonnegative-Integer)))
> (let ((i (car p))
> (l (cadr p)))
> (vector-set! res i l)))
> idx+level)
> res)
>
> It doesn't much matter here what this does; but it typechecks and works OK.
>
> What I'm puzzled by is the amount of explicit type information I had to add,
> in order to get it to typecheck, and I confess that I added that type
> annotation by simply going round and round the compile cycle in DrRacket,
> adding more and more until Racket stopped complaining. Apart from anything
> else, the annotations end up making the code less readable than it might
> otherwise be.
>
> I can pretty much see why I have to instantiate the make-vector explicitly
> (it'd probably be asking a bit much for Racket to look around and spot that
> the only thing called in the third argument of vector-set! is a
> Nonnegative-Integer), and I can sort-of see why I have to cast the '(#(1 2)
> ...) list [Racket identifies '(#(1 2) #(3 4) #(5 6)) as (Listof (Vector
> Integer Integer)) which is more restricted than (Listof (Vectorof Real)), but
> less restricted than the actual (Listof (Vectorof Positive-Integer)) ].
>
> But I can't really see why I have to spell out the instantiations of map1 and
> map2 (given the types of their arguments, I can unambiguously (?) deduce the
> type of the instantiation in both cases), and the type of the for-each
> argument, but I _don't_ have to spell out the type of idx+level. And I have
> to spell out the maps , but not the car in (map2 car idx+level).
>
> I can sort of see that type information propagates 'outwards', so that, for
> example, the type of map1 (specifically its domain) implies the type of
> idx+level, which the result of map1 is assigned to. Analogously, because the
> type of p is known, then the corresponding car can be instantiated
> unambiguously.
>
> However I don't have to instantiate vector->list, or the car argument to
> map2, so this appears to be type information propagating either 'inwards'
> from the type of map1/map2, or 'across' from the other argument.
>
> Working through that, and looking at the simple case of
>
> (map car (cast '((1 2) (3 4) (5 6)) (Listof (Listof Byte)))) ,
>
> we have map's type as
>
> : (All (c a b ...)
> (case->
> (-> (-> a c) (Pairof a (Listof a)) (Pairof c (Listof c)))
> (-> (-> a b ... b c) (Listof a) (Listof b) ... b (Listof c))))
>
> Now, the type (Listof (Listof Byte)) fixes a in the first clause to be
> (Listof Byte), and thus, in the type of car
>
> : (All (a b) (case-> (-> (Pairof a b) a) (-> (Listof a) a))) ,
>
> (Pairof a b) is (Listof Byte), meaning that a, here, must be Byte and thus
> the domain of car must be Byte, fixing c in the type of map. So there
> appears to be no ambiguity, and while the algorithm for the general case
> would clearly be entertainingly challenging (am I correct that this was Sam's
> PhD work?), I'm not seeing a fundamental blocker.
>
> In summary, I'm not saying anything's amiss here, but there are rather more
> 'sort-of's in the above account than I'm comfortable with.
>
> So: (a) Am I doing something wrong, or writing unidiomatically?; and (b) is
> there a heuristic to guide me in what annotation I actually need to add and
> what can be inferred, so it's all a bit less trial-and-error?
>
> All the best,
>
> Norman
>
>
> --
> Norman Gray : http://nxg.me.uk
> SUPA School of Physics and Astronomy, University of Glasgow, UK
>
>
> ____________________
> Racket Users list:
> http://lists.racket-lang.org/users
____________________
Racket Users list:
http://lists.racket-lang.org/users