[racket] Typed Racket reactions on developing polymorphic typed array library, and questions

From: Sam Tobin-Hochstadt (samth at ccs.neu.edu)
Date: Mon Aug 23 07:52:09 EDT 2010

On Mon, Aug 23, 2010 at 12:31 AM, Neil Toronto <neil.toronto at gmail.com> wrote:

> This email is feedback on using Typed Racket to develop a new library (i.e.
> it isn't originally untyped code) that's about 3500 lines long, and some
> questions.

Thanks for the feedback!

> Some macros with names starting with "forall" provide syntactic sugar for
> mapping over arrays simultaneously. Here's an example of use:
> (foralln/flvectorn:
>  ([x : Float  (<-flvectorn xs)]
>  [y : Float  (<-flvectorn ys)])
>  (for/fold: : Float ([dm : Float  0.0]) ([pt : vec2  (in-list pts)])
>            (+ dm (delta-mass-from pt (vec2 x y))))))
> Here, "flvectorn" is an N-dimensional flvector, and "<-flvectorn" is special
> syntax like "in-list" (i.e. forall recognizes it as creating an array backed
> by an flvectorn and inlines unsafe-flvectorn-ref accordingly).

Is this the same as the (brand-new) `for/flvector' and `in-flvector'?

>  - Why require "return" or body types for for/*: loops in TR? I haven't
> needed them in forall/*: loops. Also, writing the "Listof" in "for/list: :
> (Listof T)" is annoying. I know it's a list from reading "for/list:", and
> for/list: knows it, too. I also know the return type of a "for/fold:" from
> the accumulators' types; why require them twice? I'm also annoyed by having
> to give a body type for every named "let:".
> Similarly, why require return types for functions defined with "define:"?
> I'd almost rather write lambdas than give another annotation. (Not that I
> use define: very much.)

In general, `define:' and `let:' require return types since they
define (potentially) recursive functions, and therefore a type needs
to be given to the function, which wouldn't be known without
typechecking the body, which could reference the function.  This is
also why `for/list:' needs a return type annotation - it's expanding
into a `let loop' underneath.

>  - Related: can we get a runtime cast/refine macro? Occurrence typing makes
> a naive one easy to implement: (let ([x <expr>]) (if (pred? x) x (error
> ...))). Easy implementation means everyone will duplicate it; might as well
> incorporate a good one.

See `assert' in the docs.

>  - Polymorphic structs being proper subtypes of their parent structs would
> be really nice. As it is, I can't figure out how to cleanly have a
> "(vector-array T)" subtype of "(array T)" that gives private array functions
> access to the vector backing an array. If I had that, I could collapse
> compositions of certain array ops at runtime and remove some duplicate
> bounds checks.

I'm not sure exactly what you mean here.  Can you give an example?

>  - Futures don't work with TR. I had to use require/typed to get them, so I
> gather that they're not officially supported. From what I can tell, for TR
> code, the JIT gives a large max_let_depth to even simple thunks like (lambda
> () 1). It's a shame, because splitting up forall loops with bodies that use
> nothing but float ops seems like a killer app for futures.

I'll add type annotations for `future' and `touch'.   (lambda () 1)
should be the same in TR as in regular Racket, so I'm surprised the
JIT sees them differently.

>  - Pie-in-the-sky: notice that "<-flvectorn" above is really just a type
> annotation that the forall macro understands. It would be nice if
> compositional macros could get the types of their arguments - I could have
> forall map over any array-like thing without requiring a declaration of what
> it is.
> I understand why this is hard: the inferred types depend on expansion, and
> the expansion would depend on types. While tricky, it seems that finding a
> fixpoint w.r.t. the subtype relation could work. Most macros would iterate
> zero times or once. Expanding to expressions with uncomparable types would
> be an error.
> Typeclasses allow function dispatch on the types of arguments; this would
> allow a sort of macro dispatch on the types of expanded inner syntax. You
> could probably define typeclasses in terms of it.
> Don't attack it too fiercely, though, as I haven't given it much deep
> thought. :) Also, it is a pie... in the sky.

Yes, other people have wanted this as well.  It isn't at all obvious
how to do this in TR current architecture, but I'll keep thinking.
For the particular `<-flvector' case, right now the optimizer makes

(for ([i some-vector]) ...)

just as fast as this:

(for ([i (in-vector some-vector)]) ...)

so perhaps the same can be done for `in-flvector' and other such operations.

Thanks again for the feedback.
sam th
samth at ccs.neu.edu

Posted on the users mailing list.