[racket-dev] What are single flonums good for?

From: Vincent St-Amour (stamourv at ccs.neu.edu)
Date: Sat Sep 15 10:31:56 EDT 2012

At Fri, 14 Sep 2012 23:45:43 -0500,
Robby Findler wrote:
> The original message in this thread suggests that there is a type
> Single-Flonum and that it is making Neil wrangle his code to be
> careful about it.

Right, TR supports `Single-Flonum's, but not `f32vector's.

Part of the complexity in Neil's code is due to types, part of it is not.

Assuming he wrote the math library in untyped Racket:

    (define (foo x)
       (cond [(double-flonum? x)  (flfoo x)]
             [(single-flonum? x)  (real->single-flonum
                                   (flfoo (real->double-flonum x)))]
             [else  (flfoo (real->double-flonum x))]))

The code is exactly the same as before. The complexity comes from the
fact that this function, when given single-precision inputs, wants to
produce single-precision outputs, hence the special case.

If Neil wants `foo' to be as flexible as Racket's built-in operations
(which, when given single-precision inputs, produce single-precision
outputs), he needs that special case, types or not.

If he gives up on that flexibility, things become a lot simpler:

    (define (foo x)
       (flfoo (real->double-flonum x)))

This version still accepts single-precision inputs, but always produces
doubles.

The types simply mirror that distinction:

    (: foo (case-> (Single-Flonum -> Single-Flonum)
                    (Flonum -> Flonum)
                    (Real -> Real)))

vs

    (: foo (Real -> Flonum))

This kind of complexity gets worse for functions with multiple arguments,
and types make it worse. When expressing coercion rules, the
implementation can take shortcuts that the type system cannot currently
express, leading to unwieldy types.

These issues only come up when writing libraries that aim to propagate
Racket's numeric flexibility, such as the math library or TR's base
environment. Clients of either don't need to worry about any of that.

Single-precision floats are not the source of this problem (you would
run into the same issues, in both the untyped and typed worlds, with a
function that takes ints to ints and floats to floats), but they do add
one more type to worry about.

Vincent

Posted on the dev mailing list.