Skip to content

Commit 2321513

Browse files
authored
Merge pull request #410 from edwinb/import-revise
A variety of namespace and import goodies
2 parents 874236a + 4c5d26f commit 2321513

File tree

30 files changed

+10412
-10042
lines changed

30 files changed

+10412
-10042
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ Language changes:
1717
+ Implemented `%macro` function flag, to remove the syntactic noise of
1818
invoking elaborator scripts. This means the function must always
1919
be fully applied, and is run under `%runElab`
20+
* Add `import X as Y`
21+
+ This imports the module `X`, adding aliases for the definitions in
22+
namespace `Y`, so they can be referred to as `Y`.
23+
* `do` notation can now be qualified with a namespace
24+
+ `MyDo.do` opens a `do` block where the `>>=` operator used is `MyDo.(>>=)`
2025

2126
Library changes:
2227

bootstrap/idris2_app/idris2.rkt

Lines changed: 5037 additions & 4957 deletions
Large diffs are not rendered by default.

bootstrap/idris2_app/idris2.ss

Lines changed: 5019 additions & 4939 deletions
Large diffs are not rendered by default.

docs/source/tutorial/interfaces.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,23 @@ are both available, or return ``Nothing`` if one or both are not ("fail fast").
272272
Main> m_add (Just 82) Nothing
273273
Nothing
274274

275+
The translation of ``do`` notation is entirely syntactic, so there is no
276+
need for the ``(>>=)`` operator to be the operator defined in the ``Monad``
277+
interface. Idris will, in general, try to disambiguate which ``(>>=)`` you
278+
mean by type, but you can explicitly choose with qualified do notation,
279+
for example:
280+
281+
.. code-block:: idris
282+
283+
m_add : Maybe Int -> Maybe Int -> Maybe Int
284+
m_add x y = Prelude.do
285+
x' <- x -- Extract value from x
286+
y' <- y -- Extract value from y
287+
pure (x' + y') -- Add them
288+
289+
The ``Prelude.do`` means that Idris will use the ``(>>=)`` operator defined
290+
in the ``Prelude``.
291+
275292
Pattern Matching Bind
276293
~~~~~~~~~~~~~~~~~~~~~
277294

docs/source/tutorial/modules.rst

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,35 @@ The module ``A`` will export the name ``a``, as well as any public or
179179
abstract names in module ``C``, but will not re-export anything from
180180
module ``B``.
181181

182+
Renaming imports
183+
----------------
184+
185+
Sometimes it is convenient to be able to access the names in another module
186+
via a different namespace (typically, a shorter one). For this, you can
187+
use `import...as`. For example:
188+
189+
::
190+
191+
module A
192+
193+
import Data.List as L
194+
195+
This module ``A`` has access to the exported names from module ``Data.List``,
196+
but can also explicitly access them via the module name ``L``. ``import...as``
197+
can also be combined with ``import public`` to create a module which exports
198+
a larger API from other sub-modules:
199+
200+
::
201+
202+
module Books
203+
204+
import Books.Hardback as Books
205+
import Books.Comic as Books
206+
207+
Here, any module which imports ``Books`` will have access to the exported
208+
interfaces of ``Books.Hardback`` and ``Books.Comic`` both under the namespace
209+
``Books``.
210+
182211
Explicit Namespaces
183212
===================
184213

libs/prelude/Prelude.idr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ module Prelude
22

33
import public Builtin
44
import public PrimIO
5-
import public Prelude.Basics
6-
import public Prelude.Uninhabited
5+
import public Prelude.Basics as Prelude
6+
import public Prelude.Uninhabited as Prelude
77

88
%default total
99

src/Core/Binary.idr

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -162,14 +162,6 @@ HasNames e => HasNames (TTCFile e) where
162162
resolvedPrim gam (MkPrimNs mi ms mc)
163163
= pure $ MkPrimNs !(resolved gam mi) !(resolved gam ms) !(resolved gam mc)
164164

165-
166-
asName : List String -> Maybe (List String) -> Name -> Name
167-
asName mod (Just ns) (NS oldns n)
168-
= if mod == oldns
169-
then NS ns n -- TODO: What about if there are nested namespaces in a module?
170-
else NS oldns n
171-
asName _ _ n = n
172-
173165
-- NOTE: TTC files are only compatible if the version number is the same,
174166
-- *and* the 'annot/extra' type are the same, or there are no holes/constraints
175167
writeTTCFile : (HasNames extra, TTC extra) =>
@@ -284,7 +276,7 @@ writeToTTC extradata fname
284276
addGlobalDef : {auto c : Ref Ctxt Defs} ->
285277
(modns : List String) -> (importAs : Maybe (List String)) ->
286278
(Name, Binary) -> Core ()
287-
addGlobalDef modns as (n, def)
279+
addGlobalDef modns asm (n, def)
288280
= do defs <- get Ctxt
289281
codedentry <- lookupContextEntry n (gamma defs)
290282
-- Don't update the coded entry because some names might not be
@@ -295,8 +287,11 @@ addGlobalDef modns as (n, def)
295287
codedentry
296288
if completeDef entry
297289
then pure ()
298-
else do addContextEntry (asName modns as n) def
290+
else do addContextEntry n def
299291
pure ()
292+
maybe (pure ())
293+
(\as => addContextAlias (asName modns as n) n)
294+
asm
300295
where
301296
-- If the definition already exists, don't overwrite it with an empty
302297
-- definition or hole. This might happen if a function is declared in one
@@ -429,23 +424,26 @@ readFromTTC nestedns loc reexp fname modNS importAs
429424
-- interested in is returning which other modules to load.
430425
-- Otherwise, add the data
431426
let ex = extraData ttc
432-
if ((modNS, importAs) `elem` map getNSas (allImported defs))
427+
if alreadyDone modNS importAs (allImported defs)
433428
then pure (Just (ex, ifaceHash ttc, imported ttc))
434429
else do
435430
traverse (addGlobalDef modNS as) (context ttc)
436431
traverse_ addUserHole (userHoles ttc)
437432
setNS (currentNS ttc)
438433
when nestedns $ setNestedNS (nestedNS ttc)
434+
-- Only do the next batch if the module hasn't been loaded
435+
-- in any form
436+
when (not (modNS `elem` map (fst . getNSas) (allImported defs))) $
439437
-- Set up typeHints and autoHints based on the loaded data
440-
traverse_ (addTypeHint loc) (typeHints ttc)
441-
traverse_ addAutoHint (autoHints ttc)
442-
-- Set up pair/rewrite etc names
443-
updatePair (pairnames ttc)
444-
updateRewrite (rewritenames ttc)
445-
updatePrims (primnames ttc)
446-
updateNameDirectives (reverse (namedirectives ttc))
447-
updateCGDirectives (cgdirectives ttc)
448-
updateTransforms (transforms ttc)
438+
do traverse_ (addTypeHint loc) (typeHints ttc)
439+
traverse_ addAutoHint (autoHints ttc)
440+
-- Set up pair/rewrite etc names
441+
updatePair (pairnames ttc)
442+
updateRewrite (rewritenames ttc)
443+
updatePrims (primnames ttc)
444+
updateNameDirectives (reverse (namedirectives ttc))
445+
updateCGDirectives (cgdirectives ttc)
446+
updateTransforms (transforms ttc)
449447

450448
when (not reexp) clearSavedHints
451449
resetFirstEntry
@@ -455,6 +453,19 @@ readFromTTC nestedns loc reexp fname modNS importAs
455453
ust <- get UST
456454
put UST (record { nextName = nextVar ttc } ust)
457455
pure (Just (ex, ifaceHash ttc, imported ttc))
456+
where
457+
alreadyDone : List String -> List String ->
458+
List (String, (List String, Bool, List String)) ->
459+
Bool
460+
alreadyDone modns importAs [] = False
461+
-- If we've already imported 'modns' as 'importAs', or we're importing
462+
-- 'modns' as itself and it's already imported as anything, then no
463+
-- need to load again.
464+
alreadyDone modns importAs ((_, (m, _, a)) :: rest)
465+
= if ((modns == m && importAs == a) ||
466+
(modns == m && modns == importAs))
467+
then True
468+
else alreadyDone modns importAs rest
458469

459470
getImportHashes : String -> Ref Bin Binary ->
460471
Core (List (List String, Int))

src/Core/Context.idr

Lines changed: 101 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,12 @@ data ContextEntry : Type where
298298
Coded : Binary -> ContextEntry
299299
Decoded : GlobalDef -> ContextEntry
300300

301+
data PossibleName : Type where
302+
Direct : Name -> Int -> PossibleName -- full name and resolved name id
303+
Alias : Name -> -- aliased name (from "import as")
304+
Name -> Int -> -- real full name and resolved name, as above
305+
PossibleName
306+
301307
-- All the GlobalDefs. We can only have one context, because name references
302308
-- point at locations in here, and if we have more than one the indices won't
303309
-- match up. So, this isn't polymorphic.
@@ -309,7 +315,7 @@ record Context where
309315
-- Map from full name to its position in the context
310316
resolvedAs : NameMap Int
311317
-- Map from strings to all the possible names in all namespaces
312-
possibles : StringMap (List (Name, Int))
318+
possibles : StringMap (List PossibleName)
313319
-- Reference to the actual content, indexed by Int
314320
content : Ref Arr (IOArray ContextEntry)
315321
-- Branching depth, in a backtracking elaborator. 0 is top level; at lower
@@ -327,6 +333,7 @@ record Context where
327333
allPublic : Bool -- treat everything as public. This is only intended
328334
-- for checking partially evaluated definitions
329335
inlineOnly : Bool -- only return things with the 'alwaysReduce' flag
336+
hidden : NameMap () -- Never return these
330337

331338
export
332339
getContent : Context -> Ref Arr (IOArray ContextEntry)
@@ -348,21 +355,31 @@ initCtxtS : Int -> Core Context
348355
initCtxtS s
349356
= do arr <- coreLift $ newArray s
350357
aref <- newRef Arr arr
351-
pure (MkContext 0 0 empty empty aref 0 empty [["_PE"]] False False)
358+
pure (MkContext 0 0 empty empty aref 0 empty [["_PE"]] False False empty)
352359

353360
export
354361
initCtxt : Core Context
355362
initCtxt = initCtxtS initSize
356363

357364
addPossible : Name -> Int ->
358-
StringMap (List (Name, Int)) -> StringMap (List (Name, Int))
365+
StringMap (List PossibleName) -> StringMap (List PossibleName)
359366
addPossible n i ps
360367
= case userNameRoot n of
361368
Nothing => ps
362369
Just nr =>
363370
case lookup nr ps of
364-
Nothing => insert nr [(n, i)] ps
365-
Just nis => insert nr ((n, i) :: nis) ps
371+
Nothing => insert nr [Direct n i] ps
372+
Just nis => insert nr (Direct n i :: nis) ps
373+
374+
addAlias : Name -> Name -> Int ->
375+
StringMap (List PossibleName) -> StringMap (List PossibleName)
376+
addAlias alias full i ps
377+
= case userNameRoot alias of
378+
Nothing => ps
379+
Just nr =>
380+
case lookup nr ps of
381+
Nothing => insert nr [Alias alias full i] ps
382+
Just nis => insert nr (Alias alias full i :: nis) ps
366383

367384
export
368385
newEntry : Name -> Context -> Core (Int, Context)
@@ -390,6 +407,11 @@ getPosition n ctxt
390407
do pure (idx, ctxt)
391408
Nothing => newEntry n ctxt
392409

410+
newAlias : Name -> Name -> Context -> Core Context
411+
newAlias alias full ctxt
412+
= do (idx, ctxt) <- getPosition full ctxt
413+
pure $ record { possibles $= addAlias alias full idx } ctxt
414+
393415
export
394416
getNameID : Name -> Context -> Maybe Int
395417
getNameID (Resolved idx) ctxt = Just idx
@@ -498,27 +520,41 @@ lookupCtxtName n ctxt
498520
Just r =>
499521
do let Just ps = lookup r (possibles ctxt)
500522
| Nothing => pure []
501-
ps' <- the (Core (List (Maybe (Name, Int, GlobalDef)))) $
502-
traverse (\ (n, i) =>
503-
do Just res <- lookupCtxtExact (Resolved i) ctxt
504-
| _ => pure Nothing
505-
pure (Just (n, i, res))) ps
506-
getMatches ps'
523+
lookupPossibles [] ps
507524
where
508-
matches : Name -> (Name, Int, a) -> Bool
509-
matches (NS ns _) (NS cns _, _, _) = ns `isPrefixOf` cns
525+
matches : Name -> Name -> Bool
526+
matches (NS ns _) (NS cns _) = ns `isPrefixOf` cns
510527
matches (NS _ _) _ = True -- no in library name, so root doesn't match
511528
matches _ _ = True -- no prefix, so root must match, so good
512529

513-
getMatches : List (Maybe (Name, Int, GlobalDef)) ->
514-
Core (List (Name, Int, GlobalDef))
515-
getMatches [] = pure []
516-
getMatches (Nothing :: rs) = getMatches rs
517-
getMatches (Just r :: rs)
518-
= if matches n r
519-
then do rs' <- getMatches rs
520-
pure (r :: rs')
521-
else getMatches rs
530+
resn : (Name, Int, GlobalDef) -> Int
531+
resn (_, i, _) = i
532+
533+
lookupPossibles : List (Name, Int, GlobalDef) -> -- accumulator
534+
List PossibleName ->
535+
Core (List (Name, Int, GlobalDef))
536+
lookupPossibles acc [] = pure (reverse acc)
537+
lookupPossibles acc (Direct fulln i :: ps)
538+
= case lookup fulln (hidden ctxt) of
539+
Nothing =>
540+
do Just res <- lookupCtxtExact (Resolved i) ctxt
541+
| Nothing => lookupPossibles acc ps
542+
if matches n fulln && not (i `elem` map resn acc)
543+
then lookupPossibles ((fulln, i, res) :: acc) ps
544+
else lookupPossibles acc ps
545+
_ => lookupPossibles acc ps
546+
lookupPossibles acc (Alias asn fulln i :: ps)
547+
= case lookup fulln (hidden ctxt) of
548+
Nothing =>
549+
do Just res <- lookupCtxtExact (Resolved i) ctxt
550+
| Nothing => lookupPossibles acc ps
551+
if (matches n asn) && not (i `elem` map resn acc)
552+
then lookupPossibles ((fulln, i, res) :: acc) ps
553+
else lookupPossibles acc ps
554+
_ => lookupPossibles acc ps
555+
556+
hideName : Name -> Context -> Context
557+
hideName n ctxt = record { hidden $= insert n () } ctxt
522558

523559
branchCtxt : Context -> Core Context
524560
branchCtxt ctxt = pure (record { branchDepth $= S } ctxt)
@@ -918,6 +954,38 @@ clearCtxt
918954
resetElab : Options -> Options
919955
resetElab = record { elabDirectives = defaultElab }
920956

957+
-- Get the canonical name of something that might have been aliased via
958+
-- import as
959+
export
960+
canonicalName : {auto c : Ref Ctxt Defs} ->
961+
FC -> Name -> Core Name
962+
canonicalName fc n
963+
= do defs <- get Ctxt
964+
case !(lookupCtxtName n (gamma defs)) of
965+
[] => throw (UndefinedName fc n)
966+
[(n, _, _)] => pure n
967+
ns => throw (AmbiguousName fc (map fst ns))
968+
969+
-- If the name is aliased, get the alias
970+
export
971+
aliasName : {auto c : Ref Ctxt Defs} ->
972+
Name -> Core Name
973+
aliasName fulln
974+
= do defs <- get Ctxt
975+
let Just r = userNameRoot fulln
976+
| Nothing => pure fulln
977+
let Just ps = lookup r (possibles (gamma defs))
978+
| Nothing => pure fulln
979+
findAlias ps
980+
where
981+
findAlias : List PossibleName -> Core Name
982+
findAlias [] = pure fulln
983+
findAlias (Alias as full i :: ps)
984+
= if full == fulln
985+
then pure as
986+
else findAlias ps
987+
findAlias (_ :: ps) = findAlias ps
988+
921989
-- Beware: if your hashable thing contains (potentially resolved) names,
922990
-- it'll be better to use addHashWithNames to make the hash independent
923991
-- of the internal numbering of names.
@@ -992,6 +1060,16 @@ addContextEntry n def
9921060
put Ctxt (record { gamma = gam' } defs)
9931061
pure idx
9941062

1063+
export
1064+
addContextAlias : {auto c : Ref Ctxt Defs} ->
1065+
Name -> Name -> Core ()
1066+
addContextAlias alias full
1067+
= do defs <- get Ctxt
1068+
Nothing <- lookupCtxtExact alias (gamma defs)
1069+
| _ => pure () -- Don't add the alias if the name exists already
1070+
gam' <- newAlias alias full (gamma defs)
1071+
put Ctxt (record { gamma = gam' } defs)
1072+
9951073
export
9961074
addBuiltin : {arity : _} ->
9971075
{auto x : Ref Ctxt Defs} ->
@@ -1347,7 +1425,7 @@ hide fc n
13471425
[(nsn, _)] <- lookupCtxtName n (gamma defs)
13481426
| [] => throw (UndefinedName fc n)
13491427
| res => throw (AmbiguousName fc (map fst res))
1350-
setVisibility fc nsn Private
1428+
put Ctxt (record { gamma $= hideName nsn } defs)
13511429

13521430
export
13531431
getVisibility : {auto c : Ref Ctxt Defs} ->

0 commit comments

Comments
 (0)