Continuing thoughts about the behaviour of dynamic scoping of type-class
instances lifted from records. Note the data declaration and of the record
creates both the report type and an equivalent type-class:

> > data Ord x = Ord {
> >    (<) :: x -> x -> Bool
> > }
> >
> > int1 = Ord {
> >     (<) = \x y -> primitive_int_less(x, y)
> > }
> >
> > int2 = Ord {
> >     count = 0;
> >     (<) = \x y -> do
> >         ++count
> >         return primitive_int_less(x, y)
> > }
> >
> > l =[5, 4, 3, 2, 1]
> >
> >
> > printStrLn $ sort i int1
> > printStrLn $ sort i int2
> >
> >
> > using int1
> > printStrLn $ sort i // implicit parameter gets int1
> >

The alternative here is to push int2 into a dynamic scoping stack for this
instance so it overrides int1. This is probably more useful as it allows
local changed to type-class resolution:

void f() {
    using int2
    // implicit int comparisons will use int2
}

// implicit int comparisons will use int1

> > using int2 // error incoherent

This is probably the best approach so far in my opinion, so I would be
interested if anyone can see any problems. I like the concept of unifying
type - classes and records, and it makes it intuitive that a stateful type
class still needs a storage location. The 'using' statement can stack
allocate that storage due to the dynamic scoping. Is also reduces the
amount of syntax, so a single definition is needed, and avoids overlapping
functionality between type-classes and records, whilst keeping the implicit
resolution coherent (by always taking the top of the dynamic scoping
stack). I think its important to have a keyword/operation that actually
brings a type definition into dynamic scope giving a stack location for any
associated state.

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

Reply via email to