Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code generation target for emitting name and type information #4247

Open
purefunctor opened this issue Feb 26, 2022 · 12 comments · May be fixed by #4284
Open

Code generation target for emitting name and type information #4247

purefunctor opened this issue Feb 26, 2022 · 12 comments · May be fixed by #4284

Comments

@purefunctor
Copy link
Member

purefunctor commented Feb 26, 2022

Proposal

Currently, PureScript's IDE capabilities make use of externs files to provide completion and type information to clients of purs ide server. It's sufficient enough for most use-cases although a coveted feature that's still missing is completion and type information for local bindings e.g. let-bound values or function arguments. Likewise, purs ide can be unusable for lower-spec machines especially on larger web projects.

This proposal aims to add a new JSON-based codegen target based on GHC's HIE files, which is a plaintext binary format which stores name and type information for source files. It's used by ghci to provide type-at-point capabilities as well as completion, and I believe we can likely do the same with purs repl. There also exists hiedb, which is a command-line tool for building up a database of .hie files that can be queried.

The data to be emitted will most likely come from the type checker, likely after a declaration has been type-checked and added to the environment. We could also feed this information to the IDE infrastructure in order to provide richer information, though we'd also want to remove externs in said infrastructure.

I'd like to work on this in the future once we've released 0.15.x, or at least once #4207 and #4235 is resolved.

@thomashoneyman
Copy link
Member

@purefunctor We have a use case in the registry in which we'd like to check whether the public interface of a module has changed (ie. any public types or type signatures have changed, been added, removed, etc). Would this proposal make it possible to verify this information?

@purefunctor
Copy link
Member Author

@thomashoneyman Yes, it will also have information about exported symbols similar to externs files, on top of information for local bindings and such.

@kritzcreek
Copy link
Member

If you're only interested in the public interface, the externs files already carry that information today.

@JordanMartinez
Copy link
Contributor

JordanMartinez commented Mar 1, 2022

If you're only interested in the public interface, the externs files already carry that information today.

I think the goal is the non-public info found in 'let bindings'. While I can see what the type of foo is when I hover over it with my mouse, I don't get that same information when I do likewise for bar:

foo :: String
foo = show bar
  where
  bar = ...

@purefunctor
Copy link
Member Author

I'm wondering how this would interact with #3724, though. If we're going to go through with both, we might want to take a conservative approach to fast rebuilds.

@rhendric
Copy link
Member

rhendric commented Mar 2, 2022

#3724 doesn't seem to me like it would be a problem for this idea, but maybe I'm missing something? If module B depends on module A, and A changes but in a way that doesn't change its interface, B doesn't need to be rebuilt either for current purposes or for purposes of this new target, because the local terms in B have no more reason to change type based on the changes to A than the top-level terms.

(On the topic of interactions with future work, I would love for this to be designed in a way that's friendly to #3708, as that would eliminate an objection to that enhancement.)

@purefunctor
Copy link
Member Author

Ah, yes I got it the other way around. I was thinking of upstream instead of downstream.

@nwolverson
Copy link
Contributor

nwolverson commented Mar 2, 2022

I have an interest in this - of course supporting purs ide functionality that can enhance language server support, but also in that purerl currently has a dependency on externs on top of corefn for type information (leading to unfortunate version dependency). Specifically

  • Types for top level identifiers including non-exported

It sounds like this proposal would be good for alternative backends that could leverage type information, without the issue discussed previously of defining what "typed corefn" might mean.

For clarity on a previous comment:

If you're only interested in the public interface, the externs files already carry that information today.

I think the goal is the non-public info found in 'let bindings'

@JordanMartinez I think that @kritzcreek's message was specifically directed at the comment of @thomashoneyman on registry specifically addressing the public interface, rather than questioning the scope of this PR.

@purefunctor
Copy link
Member Author

I'm currently experimenting with approaches for collecting information for symbols and one thought I ran into is: Wouldn't be what we're doing essentially just end up being the current externs files format + local bindings and reference tracking?

The approach that I'm trying at the moment is baking the information collection step into the type-checker. I was actually surprised at how simple this was although there's the implication that performance might take a hit especially for really large source files.

The second approach would be something syntax-driven. Essentially, after all declarations are type checked, they're traversed to find types for different syntax forms. This is very hand-wavey at the moment and I've only thought about it but given:

module Main where

zero = go 1
  where
  go _ = 0

The type checker ends up applying TypedValue to most Expr nodes:

zero = ((go :: ? -> Int) (1 :: Int) :: Int)
  where
  go = (\_ -> (0 :: Int)) :: ? -> Int

In this particular case, we should pick up that zero :: Int and go :: ? -> Int, but not that 0 :: Int/1 :: Int since literals are trivial.

@purefunctor
Copy link
Member Author

What about extending externs files to contain this information instead of a separate target? This would help minimize the changes that would need to be added especially for the IDE. I think this change would also work well alongside #4352.

@purefunctor
Copy link
Member Author

I started working on this feature and I'm encountering a few blocking bugs in the compiler. I'll be linking to them as I go

@flip111
Copy link

flip111 commented Mar 14, 2024

This is a cool compiler feature. I would like to use it to check of usage of Data.Foldable.any on predicate and value. Could the type export be used to see if the value is of type Array? Then with some scripting the import can be modified to use Data.Array.any which should be a faster implementation.

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

Successfully merging a pull request may close this issue.

7 participants