By the time it gets to the type checker, the %
operator has been desugared to
Var (Qualified (Just (ModuleName [ProperName "Main"])) (Ident "f"))
.
The f
introduced by the let
binding is unqualified (Qualified Nothing (Ident "f")
), and it seems that
the qualified f
is removed from the environment inside the scope of the let
. I think that they should both be in
scope, since they are different variables- one qualified, and one not.
It doesn't seem to be a general problem with let
bindings and qualified names, because this compiles:
-- Test2.purs
module Test2 where
import Prelude ((+))
f :: Int -> Int -> Int
f = (+)
-- Test.purs
module Test where
import Prelude ((+), (-))
import Test2 as Test2
g :: Int -> Int -> Int
g a b = let f = (-) in Test2.f a b
In typeCheckAll
(Language.PureScript.TypeChecker:223), main
is checked, then g
, but not f
. I think there's going to be a
dependency graph from which f
is excluded.
Funny discovery- this works:
module Test where
import Prelude ((+), (-))
f :: Int -> Int -> Int
f = (+)
infixl 6 f as %
g :: Int -> Int -> Int
g a b = let f = (-) in asdf % b
asdf :: Int
asdf = 1
Addendum- this only worked coincidentally
typeCheckAll
is called by typeCheckModule
(Language.PureScript.TypeChecker:427), but it doesn't do any dependency analysis.
The related code might be in Language.PureScript.Sugar.BindingGroups.
Looking in the code for BindingGroups, it seems like it gets all the identifiers in a module, then finds where they're used
to build a graph. I think maybe this process is happening before the operator desugaring stage, and it's not picking up the
dependency of g
on f
.
Well, it seems that they already thought of this and createBindingsGroupsModule
is the last thing in the desugar process.
Time to dive into createBindingGroups
(Language.PureScript.Sugar.BindingGroups:57)
f
does appear as a value declaration in values
(BindingGroups:70)
allIdents
doesn't seem to be evaluating (BindingGroups:75)
usedIdents
is probably the identifiers used in the declaration, but it seems to be returning []
for all identifiers?
It seems to be putting g
+ main
and f
in different binding groups.
main
doesn't even use g
, so it's purely the fact that f
is not recognised as being used in g
. Maybe it's being deleted from the dependency set because of the shadowed definition. So it'll be a problem with usedIdents
(BindingGroups:106)
It's not being deleted, but usedIdents'
thinks it's already in scope
because of the let
binding
The problem was in usedIdents
, specifically the behaviour of the everywhereWithScope
traversal. It was using
a Set Ident
to represent the scope, causing a shadowing issue because the compiler couldn't differentiate between the
let-bound f
and the globally bound one. It was fixed by changing the everywhereWithScope
traversal to use
Set (Qualified Ident)
for the scope instead. This provides the information necessary to disambiguate the shadowed bindings.