Skip to content

Conversation

pieter-bos
Copy link

Hello! We'd like to contribute some work to get to a state where we can add refinement types to the prelude of Clash. I thought it would be good to start by improving the support for mentioning value-kind type variables in refinements; here phrased as a proposal so that we can discuss a plan.

It is still somewhat light on details, but broadly I'd suggest to continue adopting LHName over Symbol to represent names, in our case especially for local names. I'd also suggest adding an equivalent of GHC's Unique to LHName, to remove the need to make names unique prior to or along resolution.

Curious to hear what you think!

@ranjitjhala
Copy link
Member

Hi @pieter-bos — thanks! This sounds like an excellent idea to me. I also like the proposal: @facundominguez or @nikivazou do you see any issues?

A (much!) more “extreme” approach would be to have some version of fixpoints ExprV just for LH and make these different kinds of names different Var variants there. (This is what “flux” the LH for rust does which allows us to handle const generics there) but this proposal seems more pragmatic given the present state.

thanks!!!

@facundominguez
Copy link
Collaborator

facundominguez commented Sep 9, 2025

👋 Thanks for the proposal!

Introducing uniques looks plausible. I share some questions below.


The proposal mentions Constraint.Split as a place where things would improve, are there other places where you expect uniques to be necessary?


Considering alternatives, I could only think of the following. Maybe you already have a good argument to prefer uniques:
Instead of making names of local bindings unique, could the code in Constraint.Split be adapted to deal with bindings which use duplicated names?


It would be helpful to collect here the issues that would be helped with this proposal. Or you could create some, if they are not written yet. At the moment, I don't know what examples would be possible to verify after the proposal is implemented.

@pieter-bos
Copy link
Author

Thanks for the prompt feedback!

The proposal mentions Constraint.Split as a place where things would improve, are there other places where you expect uniques to be necessary?

On review I specifically meant consE from Constraint.Generate here, the case for application of a value-kinded type. My other thought was that it would probably be necessary to have type arguments like this as ANF values, which is required similarly by argType and argExpr - though I'm no longer sure it is strictly required to have unique names to transform them.

Instead of making names of local bindings unique, could the code in Constraint.Split be adapted to deal with bindings which use duplicated names?

Hmm, I see two obstacles (please correct me if I'm mistaken about anything):

  • I think it would involve adapting the use of F.subst1 at the site above to rename bindings from RAllT that shadow names mentioned in the type that is being spliced in.
  • I think that in SpecType the type variable is a GHC name in the end, which makes it hard to ensure it has a fresh text name. GHC names are already unique, but this does not help us, since refinements refer to it via a Symbol at the moment.

Actually I suppose then that collisions between tv and tv could be resolved by using LHName up to Constraint.Split without the need for a unique, since they would store a GHC Name, but I don't know how things would work out for collisions between e.g. type variables and dependent bindings.

It would be helpful to collect here the issues that would be helped with this proposal.

Will do!

@facundominguez
Copy link
Collaborator

facundominguez commented Sep 10, 2025

... rename bindings from RAllT that shadow names mentioned in the type that is being spliced in.
... but I don't know how things would work out for collisions between e.g. type variables and dependent bindings.

That looks like adopting the rapier approach. Section 2 of this paper [link] gives a short account of the difficulties. If it were only F.subst1 perhaps it may not be a lot of effort to get right.

In this place of the code, I had to determine the scope of the various bindings in RTypeV [link]. It may help define other functions that also need to understand the scope of bindings like F.subst1.

GHC names are already unique, but this does not help us, since refinements refer to it via a Symbol at the moment.

Maybe the transformation from GHC name to symbol could include the unique as a suffix. But I don't claim to know all the trouble that this will create.

Also, note that there is a place in the code that is hardcoding the UniqueId when creating GHC names.

symbolRTyCon :: F.Symbol -> RTyCon
symbolRTyCon n = RTyCon (stringTyCon 'x' 42 $ F.symbolString n) [] defaultTyConInfo

Might not affect bindings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants