> I assumed that the module system mostly didn't interact with other language features, so I could kick it down the road for now.
The main other language feature it interacts with is types. And with compile-time type checking. Especially in the "separate compilation" scenario. Especially in the "incremental compilation" scenario. It was a hot research topic back in the 90s, culminating with SML's idea of translucent modules. It's still a research topic today.
Exporting/importing values and variables, on the other hand, is entirely straightforward in comparison. The only thing you really have to think about is whether "const" means "constant during one program execution" (i.e. whether doing "x := 3" is legal or not) or "constant during the whole development cycle" (i.e. whether you can inline it's current declared value into the places where it's used or not).
nigeltao · 17h ago
For Wuffs, top level declarations start with either pub or pri (and both keywords have the same width, in a monospace font).
pub status "#blah"
pub struct foo(etc etc)
pri func foo.bar(etc etc)
Since code is also auto-formatted, you can do things like "show me a structural overview of a package's source code" with a simple grep:
rg -N ^p std/jpeg/*.wuffs
If you want just the exported API, change p to pub:
rg -N ^pub std/jpeg/*.wuffs
mdaniel · 17h ago
The cited claim about python mangling dunders is 100% not my experience
> If a class member starts with two leading underscores, then it really is private. The language will name mangle it to make it inaccessible.
Only names which start with double underscores AND do not end with double underscores.
mdaniel · 16h ago
Doesn't the "__hello" def qualify for your description? It shows up in both dir() outputs, although admittedly it is "name mangled" with the outer class name, so maybe that's what they meant
I guess this could qualify for a TIL because I didn't know Python treated leading and trailing dunders any different and that they were its implementation of "protected" and "private" only by convention
arjvik · 15h ago
The name mangling is what is being referred to. Specifically, you can access it as Sekrit()._Sekrit__hello(), but that's a pretty obvious boundary-break compared to .__hello().
Members ending in a double underscore as well aren't mangled, because this interferes with syntax sugar methods like __add__ (addition operator overloading).
pdpi · 18h ago
Something the article mentions in passing is Java’s “package private” being the default.
For the longest time, I believed that this was dumb, and that the default should’ve been private. Over time, my style has changed a fair bit, and these days I tend to think of packages, rather than classes, as the more important unit of code, and I find that package private was probably the right choice.
jevndev · 19h ago
I’ve stumbled into this problem before while drafting a language I want to make*. A lot of the design philosophy is “symbols for language features” and as such import/export is handled by `<~`and `~>`. An example of an exported function:
```
<~ foo := (a: int) { a - 1 }
```
Then at the import site:
```
~> foo
```
* some day it’ll totally for real make it off the page and into an interpreter I’m sure :,)
metayrnc · 20h ago
I like the final approach. What about
-def sayHi()
Or
def- sayHi()
I feel like having a minus communicates the intend of taking the declaration out of the public exports of a module.
amonks · 19h ago
There's some prior art here from Clojure, where defn- creates private definitions and defn public ones:
In Clojure this isn't syntax per-se: defn- and defn are both normal identifiers and are defined in the standard library, but, still, I think it's useful precedent for helping us understand how other people have thought about the minus character.
munificent · 19h ago
That's clever way to think of "-". :) I'll think about that.
kzemek · 19h ago
It might be from me being so used to it, but I do like Elixir’s `def`/`defp` second best to Rust’s `pub`
nagaiaida · 18h ago
personally, i like that raku goes the other way, with exported bits of the interface explicitly tagged using `is export` (which also allows for the creation of selectably importable subsets of the module through keyed export/import with `is export(:batteries)`/`use TheModule :batteries`, e.g. for a more featureful interface with a cost not every user of the module wants to pay).
it feels more natural to me to explicitly manage what gets exported and how at a different level than the keyword used to define something. i don't dislike rust's solution per se, but if you're someone like me who still instinctually does start-of-line relative searches for definitions, suddenly `fn` and `pub fn` are separate namespaces (possibly without clear indication which has the definition i'm looking for)
lizmat · 10h ago
Actually, a module can implement any export heuristics by supplying an EXPORT subroutine, which takes positional arguments from the `use` statement, and is expected to return a Map with the items that should be exported. For example:
sub EXPORT() { Map.new: "&frobnicate" => &sum }
would import the core's "sum" routine, but call it "frobnicate" in the imported scope.
Note that the EXPORT sub can also be a multi, if you'd like different behaviour for different arguments.
nagaiaida · 9h ago
neat! i've never needed more than i could get away with by just sneaking the base stuff into the mandatory exports and keying the rest off a single arg, but that'll be handy when i do.
trashburger · 17h ago
Put it in metadata. ;) Image-based languages can associate metadata with live objects, which is how stuff like category info and visibility is provided. It doesn't affect runtime, of course, but it can give you squiggly lines in the live environment editor.
aappleby · 17h ago
I'll add another option from my toy language experiments:
Names visible outside their scope begin with a dot.
foo = {
a = 3;
.b = a + 1;
};
print(foo.a) // error
print(foo.b) // '4'
zabzonk · 15h ago
If anyone can explain what he thinks C++ is doing in the "Modifiers section", I would be interested. As far as I can see, he doesn't understand C++ (or C). And why is "Eldritch" started with a capital?
listeria · 14h ago
he's talking about how in C++ you write the modifier once, and all subsequent declarations have the same access specifier, i.e:
class A {
public:
int a;
float b;
private:
...
};
zabzonk · 14h ago
Ok, thanks - but surely this is the most sensible and readable way of doing things? Otherwise, why not make every member begin with "class A"? There probably is some language that requires this, I guess :-) Isn't language design wonderful.
The main other language feature it interacts with is types. And with compile-time type checking. Especially in the "separate compilation" scenario. Especially in the "incremental compilation" scenario. It was a hot research topic back in the 90s, culminating with SML's idea of translucent modules. It's still a research topic today.
Exporting/importing values and variables, on the other hand, is entirely straightforward in comparison. The only thing you really have to think about is whether "const" means "constant during one program execution" (i.e. whether doing "x := 3" is legal or not) or "constant during the whole development cycle" (i.e. whether you can inline it's current declared value into the places where it's used or not).
> If a class member starts with two leading underscores, then it really is private. The language will name mangle it to make it inaccessible.
and, just on the off chance they really meant "class member" I tried that, tooI guess this could qualify for a TIL because I didn't know Python treated leading and trailing dunders any different and that they were its implementation of "protected" and "private" only by convention
Members ending in a double underscore as well aren't mangled, because this interferes with syntax sugar methods like __add__ (addition operator overloading).
For the longest time, I believed that this was dumb, and that the default should’ve been private. Over time, my style has changed a fair bit, and these days I tend to think of packages, rather than classes, as the more important unit of code, and I find that package private was probably the right choice.
``` <~ foo := (a: int) { a - 1 } ```
Then at the import site:
``` ~> foo ```
* some day it’ll totally for real make it off the page and into an interpreter I’m sure :,)
-def sayHi()
Or
def- sayHi()
I feel like having a minus communicates the intend of taking the declaration out of the public exports of a module.
https://clojuredocs.org/clojure.core/defn-
In Clojure this isn't syntax per-se: defn- and defn are both normal identifiers and are defined in the standard library, but, still, I think it's useful precedent for helping us understand how other people have thought about the minus character.
it feels more natural to me to explicitly manage what gets exported and how at a different level than the keyword used to define something. i don't dislike rust's solution per se, but if you're someone like me who still instinctually does start-of-line relative searches for definitions, suddenly `fn` and `pub fn` are separate namespaces (possibly without clear indication which has the definition i'm looking for)
Note that the EXPORT sub can also be a multi, if you'd like different behaviour for different arguments.
Names visible outside their scope begin with a dot.