Replies: 5 comments 2 replies
-
Discussion yesterday we thought perhaps we did not need to add anything (/much) to Hugr:
However, I am wondering if we have missed something here. Rather than the extension mechanism, can we not just use linking? Hugrs can declare both types and functions (to be defined by another Hugr linked in). The only things that the extension mechanism adds on top of this are (a) ops implemented by binary not Hugr, and (b) more flexible (binary-computed) type "schemes". I'm not sure I hear anything that says we need either of these? |
Beta Was this translation helpful? Give feedback.
-
Here are my thoughts. From a Guppy perspective, I would like users to be able to write something like this: @guppy.protocol
class Hashable:
def __hash__(self) -> int: ...
# Now we can define multiple types that implement the Hashable protocol
@guppy
def foo(x: Hashable) -> int:
# Dispatches to the correct implementation for the argument
h = hash(x) Below are the different compilation options we have discussed. Option 1: Passing implementations as extra argumentsThe AdvantagesNo extra complexity added to the Hugr spec Disadvantages
Option 2: Generating ExtensionsMorally, we can think of interfaces as existential types / dependent tuples, e.g.
This makes Advantages
Disadvantages
Remark: Extending protocolsWe probably also want to allow users to extend protocols: @guppy.protocol
class Hashable(Equatable): # Hashable extends Equatable
def __hash__(self) -> int: ... In this case, the Option 3: Dynamic dispatch in ExtensionA slightly different take on the extension idea is to use dynamic dispatch in the extension instead of passing implementations to the To make dynamic dispatch work we have some options:
Note that we still need a Advantages
Disadvantages
Option 4: Native Hugr supportSomething along the lines @ss2165 suggested above. |
Beta Was this translation helpful? Give feedback.
-
So we need to be clear about what level of "dynamic" dispatch is required. If we mean - the target function is statically known (during compilation), then either the proposal of abstracting over extensions or that of linking with another Hugr is sufficient, and leads to both simple Hugrs (no massive tuples) and simple Hugr specification. In particular, I think "Qubit" is likely to fall into this category. How many programs are likely to want multiple kinds of Qubit? At the same time?? (Maybe we should work through a Tierkreis program that manipulates both optical-Qubit and atomic-spin-Qubit "circuits" in the same TierkreisGraph, but of course, only runs each on its own H/W. I don't think this will be too difficult.) However, if what you want is lists of hashables - well, my overall thought here is that we are trying to shoehorn an object-oriented language into a lower-level IR. We could make Hugr OOP, but do we seriously want to? The "fun" you are talking about with vtables is exactly what C++ compilers, amongst others, do when compiling an object-oriented program down to a lower-level, non-OOP, language like C/C--/etc. That OOP involves a lot of complexity of function-passing etc. - well it does, because that is what is really going on under the scenes, and at some level, that has to become obvious! (And, that in an object-oriented language, every value is an existential is the true horror of OOP, again best kept obvious rather than swept under the carpet? As Conor says, "anyone who isn't afraid of subtyping doesn't really understand it"...) The ideal for Hugr would seem to be, we allow standard Hugr to be a "low-level" non-OOP IR; and then there is some object-orientation Extension which enables doing the awkward stuff. This looks like it corresponds to Option 2, and if I look at the disadvantages there, they don't seem too bad to me:
Lastly, it seems to me that the "rules" for translating OOP into Hugr might well change as we refine the exact semantics (Conor's comment springs to mind again - we should not expect to get these right first time). I think that's an argument for keeping these outside core Hugr so that core Hugr doesn't have to change when the semantics do. |
Beta Was this translation helpful? Give feedback.
-
Extension 3 is fine too. The tuple/vtable is looked up by the extension rather than being passed around, so maybe this saves some tuple-passing. Guppy's own static analysis should be enough to ensure we never fail to find an expected vtable at runtime - in some sense this static analysis is much the same as what you'd do to figure out what explicit tuple/vtable's to pass in option 2, just that you don't record the results in the Hugr. (So the Hugr looks simpler.) However, note you'll have to store some Run-Time Type Information with each object (in option 3) so that you have something from which to find the vtable. I'd say it was an open question whether that RTTI was easier to work with / more compact than just storing the vtable itself... |
Beta Was this translation helpful? Give feedback.
-
A variation of Option 2: An extension provides an op: It could also provide a custom type The extension provides some way to "declare" "instances" of the "Hashable" "type class". Perhaps this is inside-hugr, maybe with additional feature, or maybe by requiring exactly one This pushes complicated instance composition into the type class registry. When this extension is lowered it can be implemented with whatever dynamic dispatch, or no dynamic dispatch, the lowering is free to do whatever it wants. The extension should validate the hugr that all calls to |
Beta Was this translation helpful? Give feedback.
-
Take as use case "hashability" of a type. For a given type
T
we can define the interfaceHashable
asAnd have for example standard library methods
So the proposal is:
Is this overly complicated to be worthwhile? I suspect it gives us a large degree of flexibility for compile-team reasoning over generic operations without losing type safety and being too generic.
Beta Was this translation helpful? Give feedback.
All reactions