As I am rediscovering namespaces as context indicators, I naturally tried to do see the limits of the current system to see what I can do and what I can't.
# FooBar.nim
proc f(x: int): int =
return x + 2
# main.nim (or whatever)
from FooBar as baz import nil
# Expected:
discard f(2) # FAILS
discard FooBar.f(2) # FAILS
discard baz.f(2) # SUCCEEDS
# Surprising (to my eyes)
discard 2.baz.f # FAILS
Two things:
# module A
type
Person* = object
user*: string
age*: int
proc `$`*(p: Person): string = p.user
# module B
from A import Person
var x = Person(user: "Gustav Gans", age: 45)
# oops, bug here, calls system.$ not the $ for Person
echo x
Oops, the "namespace pollution" prevented a bug. Hmmm, maybe we shouldn't constantly fight the language. ;-)
Well, then it is useless. I am from a domain (science) where similar concepts are generally called a similar way and where clashes occur a lot because something as simple as * has a gazillion meanings depending on the context (and all of them applied to arrays of any dimensions, so no useful dispatch there).
I am not asking to make strong namespacing the default, just to improve their support to help people do their job in the best way possible (again, without hindering existing people). But just addressing shortcomings to make the language better without touching to what already exists already seems too much.
I don't want to work with a new Fortran again constantly trying to find my way in spaghetti code to get the big picture and then trying not to break anything when changing anything. Fortran has never been a problem on small code bases either.
But whatever, as you say, I won't fight.
I am not asking to make strong namespacing the default, just to improve their support to help people do their job in the best way possible (again, without hindering existing people). But just addressing shortcomings to make the language better without touching to what already exists already seems too much.
Sorry, I just wanted to point out what hasn't been pointed out already. We can have a nicer syntax for from x import nil but maybe some things should be imported anyway?
And again, as I said, I am willing to improve the situation.
Oh, and remove my account please.
I will do so if you ask again, but as peace-offering how about this proposal:
from Module import type Foo
# imports Foo and every operation that acts on this type (or returns it?)
from Module import type Foo
Then maybe more explicit, something like:
# module A
type
Person* = object
user*: string
age*: int
proc `$`*(p: Person): string = p.user
export (Person, `$`*(p: Person): string) as C
# module B
from A import C
var x = Person(user: "Gustav Gans", age: 45)
# $ for Person is called
echo x
Should be less work for the compiler, and any set of symbols can be combined, not just one type; so a module can be logically subdivided in any possibly overlapping parts, not affecting its inner structure anyhow.
Oh, and remove my account please.
That really sucks to see. I agree with your reaction though, if I saw @Araq's reply I would be discouraged as well. @Araq's demonstration is fair, but he should have mentioned his willingness to improve the situation right there in his initial post, and possibly cut out some of the condescending tone.
@Araq: Sorry to be so blunt.
@lltp: I hope you will reconsider your account removal request.
from Module import type Foo
I really like this proposal of importing a type and the related operations.
Should be less work for the compiler, and any set of symbols can be combined, not just one type; so a module can be logically subdivided in any possibly overlapping parts, not affecting its inner structure anyhow.
Nice idea but who wants to write these export groupings? I don't. And much worse, the grouping is part of the module, not controllable by importers.
then what is type(A:T1'M*B:T2'M)?
Regarding A:T1'M * B:T2'M, *(x: M, y:M): M is clearly undefined and explicit casting would be necessary anyway.
Well, just as I said, if the return type is undefined, then the final type is undefined and explicit casting would be necessary. (I am not saying that as if I just solved the problem...)
Hum, and what if 'M represented a scope/namespace instead? One keeps the exact same basic type (NDArray) but by appending 'M, that namespace is looked for first...
edit: But wait this would actually be redundant with full names and would be coming back to my first idea to keep the type constant and use specialized procedures as needed...
I'm a little disconcerted, that I cannot get your point... Exactly, why "is clearly undefined" or, maybe, what is meant by that. E.g.:
type
T1 = distinct int
T2 = distinct int
M = distinct int
proc `*`(x, y: M): M {.borrow.}
var A = 3.T1
var B = 5.T2
var C = M(A) * M(B)
assert C==M(15)
# that works, so ``*(x: M, y: M): M`` is defined...
# further assuming ``'`` syntax exists
var D = A'M * A'M
assert D==T1(15)
var E = A'M * B'M
assert E.int==15
import typetraits
echo E.type.name # ?
Maybe you just mean, that a proc, taking as arguments views ('M) of two or more different types should be prohibited?
@lltp
This is a bit too fuzzy here for my personal preference. I don't properly get what you really want. Can you give a working example project that I can execute on my computer, best would be a git(hub) repository that I can simply clone. Then name the problem that you see in there, and give an example of how you want it to work. I have the feeling. I have the feeling, that not the language here is the problem, but the way you think about the problem. And to exclude that, I would really like to see a working example form you.
As far as I am concerned, I don't want to spend my time checking that the names I chose conflict with nothing in the totality of packages in nimble and elsewhere. I want to be able to rename NativeExcelReaderPackage.open/close/write/append/metadata/etc. to xlsread.open/close/write/append/metadata/etc. if I feel like it instead of being stuck with it. I don't want to spend my time writing "Foo_init(); Foo_do_bar(); Foo_do_baz()" because, after all, these are the Foo variant of init, do_bar and do_baz...
This problem simply doesn't exist, as you describe it. close/write/append/metadata/etc you can override as much as you want, because they probably have an explicitly typed first argument, it is just the open procedure, where this typed object does not exist yet. When you don't like this, you can still use init (even though you already said you would not like that). And by the way, you can always use explicit package names for function calls.
going to dig up an old thread, I would still love to see
from module import type foo
Let's face it, the 'import nil' is really ugly, it would be a different thing if 'namespace' (for example) were a reserved name
from strutils as str import namespace
But also too verbose. Anyway, this works out of the box. For the second question, I don't thing that's possible without using macros tho.
template namespace*(module) = from module import nil
namespace sequtils
namespace strutils as str
echo str.toUpperAscii("hi")
echo sequtils.concat(@[1,2,3], @[4,5,6])
doAssert not compiles(toUpperAscii("hi"))
doAssert not compiles(concat(@[1,2,3], @[4,5,6]))
play with itIf you want to allow multiple moduleA as a, moduleB as b, .. then I think you need a tiny macro, for example:
import macros
macro imp*(imports: varargs[untyped]): untyped =
result = newStmtList()
for i in imports:
result.add quote do: from `i` import nil
imp strutils as su, parseutils as pu