In module B I have documentation with a link 'A.foo', linking to the foo member of module A. In module A I import module B. Haddock renders this as a link to A.html#t:foo, namely pointing at the type foo (which does not exist) not the function foo, which is at A.html#v:foo.
Why does Haddock link to t: for variables that start with a lower case letter? Is that a bug? For 'A.Foo' I can see that it could be a type or a constructor, so there are namespacing issues. For foo it seems a variable is at least most plausible.
Is there any way to fake a link? I am writing this in code samples, so I need it to be rendered as foo. I tried anchors, but they render as the module name, and for direct hyperlinks you have no control over the displayed text.
I considered a post processor (replacing t:[a-z] with v:), but that requires a custom Setup.hs which causes problems and is quite ugly.
I couldn't find any Haddock command line flags to obtain a more reasonable behavior, such as specifying that foo is a variable.
I can't add an import of A to B without introducing circular imports, which is vile to add purely for documentation.
I am running into this problem in the Shake documentation, where as an example removeFilesAfter does not get the right link.
I can partially answer the the first question (Why?); not sure if it is a bug or desired behaviour.
When haddock resolves references in LexParseRn.rename, it tries to look up the identifier in the environment (via lookupGRE_RdrName). This ought to fail. Next it looks as what the thing could mean (using dataTcOccs from GHC’s RnEnv). The relevant lines are:
dataTcOccs :: RdrName -> [RdrName]
-- Return both the given name and the same name promoted to the TcClsName
-- namespace. This is useful when we aren't sure which we are looking at.
dataTcOccs rdr_name
[...]
| isDataOcc occ || isVarOcc occ
= [rdr_name, rdr_name_tc]
[...]
where
occ = rdrNameOcc rdr_name
rdr_name_tc = setRdrNameSpace rdr_name tcName
so it returns the name first interpreted as whatever it was before (likely a link to a value), and then interpreted as a type constructor. How can a regular name be a type constructor? My guess is that this was added when TypeOperators were reformed in GHC 7.6, which now do share the namespace with value-level operators.
Then haddock matches on the result: If the first one is a type constructor, use that, otherwise use the second. So either it was a type constructor before, then this is used. Or it was not, but then the modified version generated by dataTcOccs is to be used.
It seems to me that haddock should just always use the first option here, and the code is just a mislead copy from how multiple results are used when they can actually be resolved.
This was a Haddock bug #228 and Neil's Haddock bug #253 and the fix has been upstream for few months. You can build GHC HEAD and rebuild your documentation or wait for 7.8 and do it then.
Related
I’m writing a type checker plugin that can be loaded unconditionally, but it should do things only whenever used in the context of a module that already has some definitions loaded. I’ve hacked together the following function to try resolving a qualified name, but it's using internal details of GHC that I feel I should not be using:
lookupOrigMaybe :: Module -> OccName -> TcPluginM (Maybe Name)
lookupOrigMaybe mod occ = do
hsc_env <- getTopEnv
unsafeTcPluginTcM $ liftIO $ updateNameCache (hsc_NC hsc_env) mod occ $ \cache0 -> do
let name = lookupOrigNameCache cache0 mod occ
return (cache0, name)
I would like to do this in a nicer way instead. It should be enough to check that the given module is already loaded, since for my use case, if the module is there but the name isn’t, then all bets are off and my plugin might as well just error out. So I was hoping I could use findImportedModule followed by lookupOrig, but alas, findImportedModule fails me in one of two ways:
If I don’t specify the package (i.e. use NoPkgQual), findImportedModule seems to return a Found{} result even when the module isn’t actually loaded yet, leading to lookupOrig failing:
attempting to use module ‘main:Cortex.Stem.Relation.Types’ (Cortex.Stem.Relation.Types) which is not loaded
If I do specify the package (using ThisPkg), then findImportedModule chokes in situations when the given unit would come “later” in the dependency graph than the currenty processed one:
panic! (the 'impossible' happened)
GHC version 9.5.20220830:
findImportModule
Cortex.Stem.Relation.Types
"main"
Just ghc-bignum
main
[ghc-bignum, cortex-prim]
Call stack:
CallStack (from HasCallStack):
callStackDoc, called at compiler/GHC/Utils/Panic.hs:188:37 in ghc-lib-0.20220830-FvKByKwp6ri5ICk6wiQO8W:GHC.Utils.Panic
pprPanic, called at compiler/GHC/Unit/Finder.hs:160:32 in ghc-lib-0.20220830-FvKByKwp6ri5ICk6wiQO8W:GHC.Unit.Finder
Please report this as a GHC bug: https://www.haskell.org/ghc/reportabug
So my question then, is what is the recommended way of looking up a loaded definition opportunistically from a type checker plugin?
The two functions mkYesodData and mkYesodDispatch in the Yesod framework are supposed to separate the handler definition and the dispatch process. Though by some miracle (to me), templates use this interesting function "resourcesApp":
mkYesodDispatch "App" resourcesApp
The only mention of this function I have found in hoogle is in the Hledger package. And it is not a yesod dependency.
In the school of Haskell by this link they give an explanation that resourcesApp is "generated" by mkYesodData, although it still does not work for my side.
https://www.schoolofhaskell.com/school/advanced-haskell/building-a-file-hosting-service-in-yesod/part%202
What is the reason for this?
There's some Template Haskell (TH) going on under the hood in Yesod, and I think this is what's confusing you. Template Haskell can be confusing when searching in documentation because it produces values at compile-time for use at runtime that aren't there before the code is compiled. resourcesApp is just one of these values.
In the code you reference, the author describes that you must have another module (which he calls Foundation) in which you have invoked mkYesodData. Indeed, without this other module, the code in the Dispatch module won't work. Strangely, it's not until (Part 4)[https://www.schoolofhaskell.com/school/advanced-haskell/building-a-file-hosting-service-in-yesod/part%204] that he seems to define the Foundation module, but you can see that there is a line:
mkYesodData "App" $(parseRoutesFile "config/routes")
That may not look like it defines a value called resourcesApp, but sure enough, it does.
In short, you should be able to get your code working by just finishing the entire tutorial and running the code altogether.
In case you're wondeering, a call to mkYesodData takes a String and then literally generates code that defines a value names resources**** where the **** is the string you passed. In this case, that would be a value resourcesApp, but in someone else's Yesod project, it could be resourcesFoo. Furthermore, since this resourcesFoo value isn't concretely in the code, projects that use Yesod typically wouldn't have it show up in their export lists or haddock documentation. It's actually very strange that you found even one hit for resourcesApp on hoogle at all, but upon closer examination, it kind of makes sense: Hledger seems to be some sort of extended interface around yesod, so they pre-generated the TH values so that they would be easily accessible to users.
As another note, TH has some restrictions in its use. For one, you typically need to perform the TH invocations ("splices" as they're typically called) in a separate module than the one you use the generated values. This is probably why the author has you create a separate Foundation module rather than just putting the line mkYesodData ... in the Dispatch module.
A Haskell package (let's call it package A) has stopped compiling for me due to updated dependencies, with an error about an undefined variable. I have access to an old environment of dependencies where the exact same code does still compile, so I can open the module in GHCi and use :info to track down where it originally came from, which leads me to package B.
The trouble is that the now-broken module in A does not directly import the module from B defining this missing identifier; it must have been getting it through another import that re-exports it. But neither A nor B have changed at all changed between the two environments. So that means that a module in some third package C must previously have imported and re-exported my the identifier from B, to then be imported (possibly via further re-exports) by the broken module in A, and C has changed between the two environments to no longer re-export the identifier.
Is there some way I can ask GHC or Cabal in the working environment for the full chain of imports that leads to an identifier being in scope, so that I can identify the package C? Otherwise I don't know how to narrow down the cause of the problem without manually reviewing all of the (transitive) dependencies of A that could plausibly import something from B.
I don't know a way to ask for the whole chain, but you could use -ddump-minimal-imports to ask for the first step in the chain, then iterate.
It turns out that Haddock does not render per-argument docs for type class
methods:
class Foo a where
foo
:: Int -- ^ This string will be ignored by Haddock
-> a
This causes certain issues for users of a library I maintain, because
the methods in my case have quite lengthy signatures. I have always had the descriptions in
the source formatted like that (certainly works for ordinary functions), but
it turns out Haddock does not display them (and does not complain about them
either).
Is there a way to display the per-argument docs with Haddock? Some workaround perhaps?
OK, this was a regression. This thing should work (and worked in version 2.16.1), but stopped (2.17.1 and later).
I have reported this: https://github.com/haskell/haddock/issues/647, should be fixed in version 2.18 (you can see there is a PR for this already).
It appears to be impossible to introspect type class constraints on functions and data types and such. However, ghci appears to do it.
Prelude> :t show
show :: (Show a) => a -> String
So... somehow it knows the type class constraint since it's printing it out. How is it doing that?
The information is kept in interface files (module.hi). To get at it from in a running program you would need to find and read the .hi files (the Hint package on Hackage does this, I believe); since ghci reads the .hi files in the course of compiling to bytecode, it has that information conveniently available.
You can see what's in a .hi file with ghc --show-iface module.hi.
The separately compiled "binaries" are the ".hi" files. These contain all the type information so that you can write code that uses them, and they contain all the type class definitions and all the type class instances so that your code can use or extend them.
Thus ghci compile source to ".hi" and loads all the dependent ".hi" files. This gives it perfect knowledge of all the types. What ghci does not need to do is go back to the source of all the imported modules, it only needs the ".hi" files.