William:

I'm going to take your questions out of order, because I think it may make
things clearer:

On Thu, Jul 25, 2013 at 10:23 PM, William ML Leslie <
[email protected]> wrote:

> I keep thinking that Shap is talking about the
> int typeclass used for comparing array indexes when he's not.  Maybe I
> missed it, but I guess you're not trying to argue that typeclasses
> that were not inferred from parametric argument types need to be
> represented in the signature; not sure why I thought that.


Almost. You are right that I haven't been explicit, so let me fix that:

If a type *instantiation* doesn't cross an assembly boundary, then the type
and it's automatic instance resolution decisions are private decisions
within the assembly. The reason I emphasize "instantiation" in that
sentence is that your assembly and mine might use the same type and get
different resolutions.

The array example is unfortunate. I was looking for an example that was
simple enough to explain quickly, but also one that illustrated the
problem. Unfortunately Ord int is in the standard prologue, so the
resolution for "Ord int" is necessarily shared by all parties. Most of the
simple examples I can come up with share this problem.

Mainly because it's a simpler function, let me switch to "ArraySum", which
has exactly the same problem over Arith 'a. It sums the elements of the
array. Arith 'a is used to sum the elements, and Arith int =
bitc.prologue.ArithInt is required internally to add 1 to the iterator
during iteration. Assume that this function is the *only* function
implemented in its assembly. That is: there are no extraneous types that
are used in this assembly that we need to be concerned about. Only the type
of array indices.

The issue is th*e path* by which the type instantiation arrives.

There is no confusion about how Arith int resolves for the iterator. That
type instantiation is entirely internal to the procedure, so a second party
assembly certainly shouldn't be able to influence how "+" gets resolved. In
this case, Arith int has a resolution Arith int = bitc.prologue.ArithInt in
the standard prologue, and that's the one we use. That part seems pretty
clear.

Now we're going to sum the elements, which are of type T. And I'm going to
be very careful about how I do it:

let sum = a[0];
for (i = 1; i < a.length; i++)
  sum = sum + a[i];


Note that sum does *not* initialize to zero! This is true because (a) the
array may not contain numbers, and (b) it avoids any type unification
between the element type 'a and the index type *int*. And just as a side
note, the fact that I have to be careful about how to write this
illustrates how obscure these cases are.

OK. So this sum introduces an Arith 'a constraint. So far so good. And the
instantiation of 'a is clearly driven by the caller, and at any
instantiation *other than* 'a ~ int it's pretty clear that the caller is
going to have to supply us with a dictionary for Arith 'a. *The source of
that dictionary should not change when *'a ~ int*. *Unless we have reason
to know that caller and callee are structurally required to have the same
resolution, we have to assume that they might have different resolutions.
And if it happens that they *do* have different resolutions, then *the two
instances of *int* in this example aren't really the same type*, because
their behavior is different.

Hopefully, it's now clear why we have to follow the caller's context for
this instance resolution.

Now unfortunately, the example breaks down at this point, because the
automatically resolved instance Arith int ~ bitc.prologue.ArithInt appears
in the standard prologue. Since it's in the prologue, and only one default
resolution can be in effect in a given assembly, we actually *do* get to
assume that the two instances are the same.

But it's fairly easy to build an example in which the instance doesn't come
from the standard prologue. I/O will do nicely:

def PrintMix(o: 'a) is
  let t:T = GetMeSomeT() in
    oStream << t
    oStream << o


Assume here that the resolution of '<<' comes from an orphaned instance in
both cases. For t, we want the resolution that is internal to our assembly.
When 'a ~ T, we still need the resolution used for o to be provided by the
caller.


Given these two signatures (with apologies for mixing the syntaxes of
> four different languages to make it sufficiently expressive)
>
> sort : {{x : Ord X}} -> x[n] %r -(mut %r)-> ()
> sortR : R[n] %r -(mut %r)-> ()
>
> where
>
> sortR = sort {{ordR}}
>
> in one crate, I later come along and say, well, I'd really like to
> sort my array of R by the ordR ordering and so write
>
> sort {{ordR}} rs
>
> and that really, I probably hope to get sortR.  So, besides actually
> using sortR, is there a way I can get the correct sort instance AND
> have it inlined and fast, since I've already compiled that?
>

I'm completely lost in your newly invented type notation, and we don't want
to add complexity by bringing mutability into this, so let's go back to
mine:

sort :: Ord 'a => 'a[] -> 'a[]
sortR :: Ord R = OrphanedInstanceForR => R[] -> R[]


and you are asking whether a call on your side of the form:

let Ord R = OrphanedInstanceForR in
  sort myArray : R[]


will pick up the already-instantiated version of sort. The answer is yes.
Because sort is exported from the assembly, we also export all known
instantiations of sort. So when the caller tries to perform this
resolution, it will notice that the fast version has already been
instantiated. In effect, it's a form of partial evaluation.


> I think that what you're trying to say, Shap, is that you would like a
> way to compile the partial application and then retrieve it when I
> make the same application.


Yes. I'm also saying this is not difficult to accomplish.



Jonathan
_______________________________________________
bitc-dev mailing list
[email protected]
http://www.coyotos.org/mailman/listinfo/bitc-dev

Reply via email to