[racket] Typed Racket reactions on developing polymorphic typed array library, and questions
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
this:
(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