goldfirere / singletons

Fake dependent types in Haskell using singletons
286 stars 36 forks source link

Qualified import of singletons #306

Closed odr closed 6 years ago

odr commented 6 years ago

If I define in one module

singletons [d| foo = ... |]

and import it qualified (as F) to another module and use F.foo in another template:

singletons [d| bar = F.foo |]

I get errors like Not in scope: type constructor or class ‘FooSym0’ If I import FooSym0 unqualified I also get an error Variable not in scope: sFoo

Is it possible to use qualified names (F.FooSym0, F.sFoo) for qualified name in template?

RyanGlScott commented 6 years ago

Is it possible to use qualified names (F.FooSym0, F.sFoo) for qualified name in template?

Unfortunately, no. Template Haskell doesn't give you any way to distinguish between a qualified and non-qualified name, as the following program demonstrates:

{-# LANGUAGE TemplateHaskell #-}

import Language.Haskell.TH
import qualified Prelude
import Prelude

main :: IO ()
main = putStr
  $(do ConT nonQualName <- [t| Bool |]
       ConT qualName    <- [t| Prelude.Bool |]
       stringE $ unlines
                 [ "Non-qualified name " ++ show nonQualName
                 , "Qualified name "     ++ show qualName
                 , "Are they equal? "    ++ show (nonQualName == qualName)
                 ])
λ> main
Non-qualified name GHC.Types.Bool
Qualified name GHC.Types.Bool
Are they equal? True

Therefore, singletons can't generate F.FooSym0 or F.sFoo, as it has no way to know that the original F.foo name was qualified to begin with.

I can't recall if there's an open GHC ticket about this, but one thing is for sure: this is not possible unless Template Haskell were to be changed so that qualified names would be preserved when quoted.

odr commented 6 years ago

Thank you for explanation. I think it could be done with new QuasiQuoter (say, [dq| ... |]). Am I right? Though I am not too much interested in it.

RyanGlScott commented 6 years ago

I think it could be done with new QuasiQuoter (say, [dq| ... |]). Am I right?

I don't think it would even be possible with quasiquoting. You'd still have to deal with names like F.foo, where F is some qualified prefix. The Template Haskell API for names only gives you NameG and NameQ for dealing with qualified names, but they require giving a full module name, not just a prefix. For instance, if you had import Prelude as Prel, then you'd need to be able to figure out that Prel.show actually means NameG "base" "Prelude" "show", which is a very non-trivial task (especially since Template Haskell doesn't give you the Prel prefix in the first place).

odr commented 6 years ago

Hmm... Maybe it could be done more simple then with QQ? [d| bar =F.foo|] is something like

[ValD (VarP bar_1) (NormalB (VarE ModuleFoo.foo)) []]

We have to generate other definitions with changing e.g. ModuleFoo.foo to ModuleFoo.sFoo. Isn't it how it works now? I suppose that now Singletons use just "local" name sFoo, no? I assume that if we import ModuleFoo qualified it is accessible from TH with its full name (i.e. it doesn't depend on qualified name). It looks natural to me but probably I am too optimistic... I didn't check all these stuffs.

RyanGlScott commented 6 years ago

It is true that quoted names do give you the module information, yes. However, this is often not enough to figure out where the singleton definitions are!

For instance, if you quote [| id |], then you'll get a Name that's (roughly) NameG "base" "Prelude" "id". However, the corresponding singleton definitions for id are not in the Prelude—they're in singletons, a completely different package! You'd have the same problem in your scenario, since you cannot know in general whether the singleton definitions for F.foo were defined in the same module that F refers to or not.

In short, this is a complicated problem that's further complicated by Template Haskell inadequacies, and I don't see a straightforward solution.

goldfirere commented 6 years ago

I agree with Ryan's summary above. Even if TH could track such qualifications, making use of them would require the assumption that singleton definitions are in the same module as the original definitions. This is not true all the time. I suppose you could also teach singletons a mapping from qualified original names to the modules where the singletons are defined, but that seems overwrought, to me.

My bottom line: singletons does unhygienic, unpleasant things with names. This means that we're never going to be handle all naming situations gracefully. And I don't see a better way while still allowing users to access singletons names without yet more TH.

RyanGlScott commented 6 years ago

I'll opt to close this, as we can't fix this on singletons's end cleanly. I'll note that there are really two problems here:

  1. Template Haskell cannot generate names of the form F.foo, where F is a qualified prefix.
  2. singletons does not grant users the power to change what names are generated if the default scheme isn't to their liking.

The second issue is surmountable if we fixed #204. The first issue, on the other hand, would need to be fixed by adding more functionality to Template Haskell. So perhaps fixing that could be a jumping point into getting back to this issue.