I ran into a problem about collision of theorem names in different projects.
I have two personal projects, say A and B, using Lean4; A doesn't depend on Mathlib while B does.
The problem is that I cannot use codes from the project A in B because some auxiliary lemmas in A have the same names as ones in Mathlib.
My questions are:
is there any "standard" solution in such a situation?
lake build stops at each collision pair with the error message like
@Jun Yoshida The usual way to solve this is for packages to not define all their definitions within a namespace that matches the package / root module name (i.e., A and B and your example). Only very standard libraries like Mathlib and Std should stick stuff in the root namespace or augment existing namespaces like List.
If List.dropLast_eq_take, for example, is defined inbetween namespace A-end A, then I cannot write l.dropLast_eq_take for l : List Nat.
This seems a drawback.
The safest solution is to do what Mac suggested, put your own things into your own namespace. It's true that it's unfortunate that you can't use l.dropLast_eq_take notation, but that's the tradeoff you have to take if you want to be safe against name collisions.
The "standard" solution for having names in library namespaces might be to try to contribute such lemmas to mathlib or std.
In this particular case it sounds like the lemma is already in mathlib; the problem is that project A decided to reimplement this lemma rather than depending on mathlib
Thank you for a lot of advice. I do not use mathlib in the project A because it is a library for data structures/algorithms to be used in constructive context. Some name collision are actually due to Classical-free re-proof of theorems in std for which I just stupidly add primes in the last of the original name. I understand that the library name space is the safest solution, but it requires a huge refactor in the project A, which I want to avoid as much as possible.
I also consider the use of private declarations. If I keep track with such declarations in the project, I can export them locally in the same way as export private does.
Here is my attempt to this approach:
Definition of pkg_local attribute and pkg_include command
importLean/-I define* `pkg_local (pkg := PackageName)?` attribute, which register a private declaration in a declaration list associated to `PackageName`, and* `pkg_include (pkg := PackageName)? ident*` command, which imports declarations specified by `decl`s from the declaration list associated to `PackageName` into the current file.In both cases, the part `(pkg := PackageName)` is optional; if it is not served, `PackageName` is the head of the current module name (i.e. in the module `A.B.C`, ``PackageName = `A``).-/openLeanElabTermCommandMeta/-!### `PkgLocalDecl`-//-- The data structure that carries major data of package local declarations -/structurePkgLocalDeclwherepkgName:NamedeclName:NamederivingInhabited,Repr,BEq,Hashable/-!### `pkg_local` attribute-/syntax(name:=pkg_local)"pkg_local "("("&"pkg"" := "ident")")?:attrinitializeregisterTraceClass`pkg_local/-- The environment extension to track declarations with @[pkg_local] attribute. -/initializepkglocalExtension:SimpleScopedEnvExtensionPkgLocalDecl(HashSetPkgLocalDecl)←registerSimpleScopedEnvExtension{addEntry:=fundt=>dt.insertinitial:={}}privatedefLean.Name.head:Name→Name|.anonymous=>.anonymous|.str.anonymousx=>x|.strchild_=>child.head|.numchild_=>child.headinitializeregisterBuiltinAttribute{name:=`pkg_localdescr:="Attribute for package-local definitions/theorems."applicationTime:=.afterTypeCheckingadd:=fundeclNamestxkind=>dolet`(attr|pkg_local$[(pkg:=$pkgid)]?):=stx|throwError"unexted `@[pkg_local]` attribute {stx}"let.global:=kind|throwError"`@[pkg_local]` can only be used as a global attribute."if!(isPrivateNamedeclName)thenlogWarning"`[@pkg_local]` is used on a non-private declaration."MetaM.run'dodbg_trace"declName : {declName}"letmoduleName←getMainModuleletpkgName:=(pkgid.mapfunid=>(id.getId)).getDmoduleName.headdbg_trace"pkgName : {pkgName}"pkglocalExtension.add{pkgName,declName}}/-!### `pkg_include` command-/syntax(name:=pkg_include)"pkg_include "("("&"pkg"" := "ident")")?ident,*:commandsyntax(name:=pkg_include_all)"pkg_include "("("&"pkg"" := "ident")")?" * ":command@[inline]defgetPkgLocalDecls{m:Type→Type}[Monadm][MonadEnvm](pkgName:Name):m(ArrayPkgLocalDecl):=doletdeclsAll:=pkglocalExtension.getState(←getEnv)bifpkgName==.anonymousthenreturndeclsAll.toArrayelsedoletmutdecls:ArrayPkgLocalDecl:={}fordindeclsAlldoifd.pkgName=pkgNamethendecls:=decls.pushdreturndecls/-- Construct `Lean.Declaration` from `PkgLocalDecl` -/@[inline]defmakeDecl{m:Type→Type}[Monadm][MonadEnvm][MonadErrorm](pkgdecl:PkgLocalDecl):mDeclaration:=doletcinfo←getConstInfopkgdecl.declNameletname:Name:=mkPrivateName(←getEnv)((privateToUserName?pkgdecl.declName).getDpkgdecl.declName)if(←getEnv).containsnamethenthrowErrors!"'{name}' has already been declared"letconstval:ConstantVal:={name:=name,levelParams:=cinfo.levelParams,type:=cinfo.type}matchcinfowith|.thmInfotinfo=>returnDeclaration.thmDecl{constvalwithvalue:=mkConstpkgdecl.declName(tinfo.levelParams.mapmkLevelParam),}|_=>returnDeclaration.defnDecl{constvalwithvalue:=mkConstpkgdecl.declName(cinfo.levelParams.mapmkLevelParam),hints:=ReducibilityHints.abbrev,safety:=ifcinfo.isUnsafethen.unsafeelse.safe}@[command_elab «pkg_include»]defelabPkgInclude:CommandElab:=funstx=>dolet`(command|pkg_include$[(pkg:=$pkgid)]?$ids,*):=stx|throwError"invalid use of 'pkg_include' command: {stx}"letpkgName:Name:=(pkgid.mapfunid=>id.getId).getD(←getMainModule).headletpkgdecls←getPkgLocalDeclspkgNameletmutdecls:ArrayPkgLocalDecl:={}foridinids.getElemsdodbg_trace"finding {id.getId} from {repr pkgdecls}"letsomepkgdecl:=pkgdecls.find?fund=>id.getId.isSuffixOfd.declName|throwError"local declaration not found: {id}"dbg_trace"{id} → {repr pkgdecl} found"decls:=decls.pushpkgdeclliftCoreMdofordeclindeclsdoletdecl←makeDecldecladdDecldeclcompileDecldecl@[command_elab «pkg_include_all»]defelabPkgIncludeAll:CommandElab:=funstx=>dolet`(command|pkg_include$[(pkg:=$pkgid)]?*):=stx|throwError"invalid use of 'pkg_include' command: {stx}"letpkgdecls:=pkglocalExtension.getState(←getEnv)liftCoreMdoforpkgdeclinpkgdeclsdoletdecl←makeDeclpkgdecladdDecldeclcompileDecldecl
Expected workflow is
Define a private def/theorem with pkg_local attribute: