# Re: [stack] comments - 2

## Louis Madon — 2000-07-11 00:06:17

    phimvt@... wrote:

     > 3. Type checking

    >     In static languages all or almost all typechecking can be done

    > at compile time (= as the program is being read). In dynamic languages

    > this is not possible, and hence either programs are not checked at

    > all, or are checked at run time. In Joy this means, in the case

    > of the addition operator + :

    >     1. Do we have two parameters?

    >     2. Is the top an integer?

    >     3. Is the second an integer?

    >     4. Add them!

    > Tests 2. and 3. are done by looking at a (one byte) field of the

    > stack nodes. These fields are in addition to the value fields of the

    > nodes (and the "next-link" fields of the nodes).

    >     Often the checks are unnecessary. Consider

    >         foo  ==  + 10 *

    > This will add two (hopefully) numbers and multiply the sum by 10.

    > At compile time it will not be known whether the two parameters

    > really are numbers, so the usual dynamically typechecked version

    > of + has to be compiled. On the other hand, the sum is an integer,

    > 10 is an integer, and so the compiler knows that the multiplication

    > will receive correct arguments, and a stripped down version of

    > multiplication is all that is needed.

    >     Again, you already know my views on premature optimisation.

    >     It would be of considerable interest to examine what kind of

    > subset of Joy could be (easily) statically typechecked.

     I'm very interested in a (mostly) static Joy too. Since you mentioned

    the subject I'd like to give some brief examples of what my system can

    do [I indicated some time back on the list that I was going to document

    my type system fully but that is taking longer than expected due to some

    niggling issues that remain to be resolved].

     I won't say its "easy" but a static type system could do things that a

    dynamic one can't. In particular, the runtime type passing model is (I

    believe) impractical for polymorphism on return values or on implicit

    context. Consider:

    area-of-circle == dup * pi *

    depending on whether the radius value on the top of stack is (say) a

    floating point or a rational, we'd want the type of pi to be selected

    accordingly. But since the pi function takes no arguments, the type

    passing model has no way to disambiguate it.  In my system,

    pi (float)    == 3.1416

    pi (double)   == 3.1415926535

    pi (rational) == #314159/10000

    ... etc

    and the system infers the correct selection based on the dataflow

    context the function is instantiated into.

    In graphics work its common to permit some tolerance when comparing

    values:

    =.approx == - abs epsilon <

    (for now treat '=.approx' as if it were a single name)

    this function expects two like values on top, subtracts them, converts

    the result to an absolute value and finally checks to see if this is

    below some threshold (denoted here by 'epsilon').  The precise value of

    epsilon will depend on the types involved (eg. are we comparing floats

    or doubles or ...) and also can vary depending on the intentions of the

    programmer. For example, during curved surface intersection the

    calculations tend to be quite complex and therefore precision may be

    lower, hence a larger epsilon value could be desired. In my system you

    can specify whatever cases are needed:

    epsilon.small (float)  = 1e-30

    epsilon.big   (float)  = 1e-28

    epsilon.small (double) = 1e-70

    epsilon.big   (double) = 1e-68

    ... etc

    Then use explicit context refinement to indicate intention:

    foo == (=/approx) (epsilon/small) = swap = and

    The (../..) are context refinements. In this case, we're saying that we

    want the 'approx' notion of equality and the 'small' notion of epsilon

    (the refinements stay in place for the scope of the function).

    Explicit context refinement also allows polymorphism on global

    properties like the target operating system, build configuration, cpu,

    etc.  For example:

    main ==

      (os/linux)

      (build.fileformat/gif.jpg.bmp.pcx)

      (build.type/ELF)

      (cpu/alpha)

      do-stuff

    then within the program you can distinguish cases like:

    foo.alpha       ==  ... do foo in an alpha specific way

    foo.x86.linux   ==  ... do foo in an intel & linux specific way

    foo.x86.windows ==  ... do foo in an intel & windows specific way

    ... etc but can then use it everywhere simply as 'foo'

    I've left out _lots_ of detail in these explanations but I hope that the

    basic ideas come across.  Overall, you can do some interesting things

    statically.

    --

    Louis.

## Louis Madon — 2000-07-11 00:18:24

    Louis Madon wrote:

     > foo == (=/approx) (epsilon/small) = swap = and

     Oops, I meant '= rollup = and' in this example

    --

    Louis.