So it's not a story of "abandoning", more like an insane C idiosyncrasy that wasn't ever used anywhere else.
So it's not a story of "abandoning", more like an insane C idiosyncrasy that wasn't ever used anywhere else.
They do not work well C++ though because it puts the implementation into the header for some reason.
ELF files based on the C type system can't provide such information on object files, and binary libraries.
In the modules world it can read it from the symbol table on the BMI, like in any other module based language.
It is simply a language design flaw of C++ that class implementation (not just templates) ended up in the headers.
import x
class Foo:
def bar(self):
import y # break circular import
y.something()
Would be nice to be able to import `Foo` without pulling in also `y`, or moving `y` inline.Can be solved in different ways, but you see inline imports everywhere.
Almost every other module-based language does not have issues with circular dependencies. Python, in theory, could follow their lead, but they won't.
EDIT: I wasn't as clear as I could be. The issue again isn't modules, but scripting languages that allow top-level statements at all. Intermixing types and method declarations with executable code makes circular decencies an issue. Compiled languages don't allow top-level statements, so they don't have the dependency resolution problem.
That still happens.
It takes a lot of magic trickery to make cyclical require/imports work for JavaScript and a lot of times they don't/can't.
Realistically, that's just what happens when a language allows top-level statements, as they are executed when a file is loaded. As such, scripting languages tend to fall victim to the problem, but compiled ones don't.
I'll update my comment.
So if you just need to call that binding within, say, a function or a method call, its typically fine to do so assuming that by the time that function or method call is executed, both modules will have completed their initializations.
const foo = require("foo"); // starts out as empty object
exports.bar = function() {
return foo.fn();
}let x = require('foo').x
of course, cannot do that. It's a small difference but it does make it easier to fall into the happy path in more cases.
Lots of languages allow initialization code (e.g. Java static initializer).
But in the case of scripting languages, usually the classes and functions themselves are initialization code....i.e. everything is initialization code.
I recall having seen a circular dependency compiler error in Go.
But from a developer user experience standpoint, what is the difference between having to write things twice for this reason:
// header.h
int foo();
// library.c
int foo() { return 42; }
Versus writing things twice for this reason:
// interface.cs
interface IBar { int foo(); }
// library.cs
class Bar : IBar { int foo() { return 42; } }
Granted in C it was for dumb reasons whereas in a modern language it's for better (?) reasons, but: You're still writing things twice!
Not in Oberon.
Expose the types, and the public procedures. Users only need to see the spec during compilation.
Hence name mangling, the only way to add some linking type safety, while using a bare bones UNIX linker that only knows C and Assembly.