doug-moen / openscad2

better abstraction mechanisms for OpenSCAD
Boost Software License 1.0
25 stars 3 forks source link

collapsing namespaces #23

Open Ma-XX-oN opened 7 years ago

Ma-XX-oN commented 7 years ago

I really like what I'm seeing here in this new version specification. However, I do have some issues with the collapsing of the namespaces. I feel that it isn't necessary to collapse the 3 namespaces, and I personally prefer that there are 3 namespaces.

The only reason to have a unified namespace (as you have said) is to be able to reference them. I would propose a selector instead, to indicate what namespace you are going to reference.

I.e. If I was referencing a module, then use @(module)module_name, function @(function)function_name, a definition's contents, just definition_name. So in p2=function(x,y) pow(x,2), p2 would be in the definition namespace, referencing a lambda. However, if you wanted to reference an already existing function, it would look like this:

function p2(x) pow(x, 2);
p_2=@(function)p2;

So p2 would be in the function namespace, but p_2 would be in the definition namespace.

To call the function p2, it would be as normal, as in p2(3), but to dereference a definition, a dereference operator would be applied to the definition name, as in @p_2(3). Note that what you are dereferencing is implied by context. If the context is that a module is expected, then the definition must be a module (object), if it is a value, then it must be a function.

This would keep a much higher compatibility with OpenSCAD1's language, and make learning OpenSCAD2 a more incremental progression.

doug-moen commented 7 years ago

Yes, I've considered this type of design as well.

The syntax I came up with was v$abc for a variable, f$abc for a function, and m$abc for a module. These are not legal identifiers right now, so it's backward compatible. And it's compact.

The advantage is for the developer: this makes it easier to implement function values, with a smaller set of changes, without collapsing the namespaces. The disadvantage is for users: it is ugly and overly complicated.

Ma-XX-oN commented 7 years ago

Well, it isn't really necessary to have a way to specify the 'variable' namespace as that would be default. The parentheses that I used could actually be omitted now that I think about it, so long as there is whitespace between the keyword and the name.

I'm not necessarily a fan of compact over more expressive, but the f$abc/m$abc is, IMO, still more expressive as this would be more explicit that the user is passing something other than a simple 'variable', at least from the top level call. I personally think that aesthetically, it is actually better, because of that.

I have many functions that are equivalent module namespace items, such as cube, sphere, etc and is the reason for my like of the separate namespaces.

It really bothers me that older, tried and true libraries will be lost due to this incompatibility, requiring a major overhaul and a new debugging cycle.

doug-moen commented 7 years ago

The "OpenSCAD2" proposal has a few problems, and one of them is the inability to work with OpenSCAD libraries that define the same name in different namespaces. The v$, f$, m$ prefixes are my current idea for addressing that problem.

However, I also think that 3 namespaces are a bad design and an evolutionary dead end. A next generation OpenSCAD design would do well to break free of this design and support a single namespace, because that's the way to achieve a simpler and more powerful language. So I view the system of identifier prefixes as an ugly kludge for backwards compatibility, and nothing more.

Ma-XX-oN commented 7 years ago

To each their own. There are a few languages that do this, perl being the most notable one.

I still don't know why you need the v$ prefix. To my mind, it is unnecessary as they already exist in that namespace.

doug-moen commented 7 years ago

OpenSCAD2 has a single namespace, but it needs to be compatible with libraries written in the older dialect, which has 3 namespaces. Suppose you write some OpenSCAD2 code:

M = import("mylib.scad");

So, M is written in the old dialect, and defines both a variable and a function with the same name foo. How do we access these two distinct objects from OpenSCAD2 when they have the same name? The answer is to use namespace prefixes: M.v$foo and M.f$foo.

Ma-XX-oN commented 7 years ago

As I said before, not really a fan of having OpenSCAD2 using 1 namespace and am trying to emphasize this point.

A for referencing the two different namespaces, you could use M.foo and M.f$foo. Variables wouldn't need the decoration as it is implied.

t-paul commented 7 years ago

I think it's better to go for a simple, concise and obvious syntax. One nice thing is that OpenSCAD is also usable by non-programmers, adding some obscure syntax by default seems not very useful. The current syntax is pretty much ad-hoc, OpenSCAD2 can improve there a lot by unifying concepts and leaving the obscure prefixes only for cases where needed for backward compatibility.

doug-moen commented 7 years ago

@Ma-XX-oN said: "for referencing the two different namespaces, you could use M.foo and M.f$foo. Variables wouldn't need the decoration as it is implied."

That doesn't work reliably.

Suppose that M is an OpenSCAD script (the 3-namespace dialect) containing a function foo. There is no variable foo or module foo, so M.foo unambiguously refers to the function. You write some code in OpenSCAD2 that references M.foo.

Now suppose that M is updated with new definitions, and there is now a variable called foo. Your OpenSCAD2 code that references M.foo is now ambiguous, because the interpreter doesn't know if you want to reference the variable or the function.

What should happen is that you get an error, and you have to update your code to use M.f$foo. But if OpenSCAD2 defaults to referencing the variable in case of ambiguity, then the code will silently change its meaning from referencing the function foo to referencing the variable foo, and that is a bad thing.

Ma-XX-oN commented 7 years ago

There are also others in the community who prefer the separate namespace structure.

How about this: unless a the symbol exists in more than one namespace, disambiguating would not be necessary, and an error would be displayed when trying to reference an ambiguous symbol name in an ambiguous context, when it doesn't have a decorator.

That would allow for the ability to use 3 different namespaces while looking like it is one.

If there's concern that some new person may not understand that, then use a keyword or function call that would specify that the current file uses 1 or 3 namespaces. It can be defaulted to 1 but can be set to 3. If 1, then using the same name for different contexts would be considered an error, but if 3, there is no error.

Ma-XX-oN commented 7 years ago

@doug-moen, that's an interesting scenario, but it would be caught as soon as one attempted to use the contents of the definition foo, unless foo was a definition of a reference to a function. In which case, this might very well be intentional.

doug-moen commented 7 years ago

@Ma-XX-oN said: How about this: unless a the symbol exists in more than one namespace, disambiguating would not be necessary, and an error would be displayed when trying to reference an ambiguous symbol name in an ambiguous context, when it doesn't have a decorator.

I'll assume you are proposing this as a modification of the existing OpenSCAD language.

I'm not sure what the benefit is, since this isn't backwards compatible with OpenSCAD. For example, if you define a variable and a function foo in the same scope, and call foo(x), then that's ambiguous (since the variable foo might contain a function).

doug-moen commented 7 years ago

@Ma-XX-oN said: "use a keyword or function call that would specify that the current file uses 1 or 3 namespaces. It can be defaulted to 1 but can be set to 3. If 1, then using the same name for different contexts would be considered an error, but if 3, there is no error."

Yeah, that works. In terms of backwards compatibility, it requires an extra line of code to be added to old OpenSCAD scripts that make use of the 3 namespaces.

doug-moen commented 7 years ago

@Ma-XX-oN, I'm not sure what your beef is with the proposed OpenSCAD2 backwards compatibility scheme. Existing OpenSCAD scripts that make use of 3 namespaces continue to work without any changes, but if you take advantage of the new syntax, then you can write code that puts variables and functions into the same namespace. No need for a "a keyword or function call that would specify that the current file uses 1 or 3 namespaces".

Ma-XX-oN commented 7 years ago

@doug-moen said: I'll assume you are proposing this as a modification of the existing OpenSCAD language.

As a modification to your OpenSCAD2 proposal. Personally, I'd prefer the 3 namespace as default and have the 1 as an option, so as to keep backwards compatibility. I only proposed the contrary as a meet halfway proposal.

@doug-moen said: I'm not sure what your beef is with the proposed OpenSCAD2 backwards compatibility scheme.

My "beef" is that you are kneecapping older scripts for not conforming to a single namespace scheme. Yes, they will work under your compatibility scheme, but if someone wants to do a small mod to an existing library to take advantage of the new functionality, it is no longer is small mod.

I think that this project has a lot of potential, but I fear that this will be viewed as a major letdown to the established community.

doug-moen commented 7 years ago

@Ma-XX-oN "My "beef" is that you are kneecapping older scripts for not conforming to a single namespace scheme."

That's a reasonable point. I would propose solving this using the v$, f$, m$ namespace prefixes.


An important goal of OpenSCAD2 is to support programming using a single namespace. A significant part of the community wants this; the requirement isn't going away. To support this and also support backward compatibility, OpenSCAD2 has 2 dialects, a single namespace dialect and a 3-namespace dialect. The "dialect" is a global property of a script. In the 3-namespace dialect, namespace prefixes would permit 3-namespace scripts to use functions as values, among other things.

In the single namespace dialect, the scoping rules change. For example, in the 1-namespace dialect, a local variable definition shadows a global function definition of the same name, and vice versa, whereas in the 3-namespace dialect, this shadowing doesn't occur. There's no way to support the single-namespace goal, and also support backwards compatibility, without having 2 dialects.


In my previous message, by "OpenSCAD2 backwards compatibility scheme", what I really meant was the mechanism that OpenSCAD2 uses to determine which dialect is in use. There is more than one way to do this, eg

The specific approach being explored here is that we determine the dialect based on the syntax of function and module definitions. If you use old-style syntax for function and module definitions,

    function f(x) = x + 1;
    module box(s) cube(s);

then you are using the 3-namespace dialect. If you use new-style syntax for function and module definitions, you are using the 1-namespace dialect. Since OpenSCAD2 makes shapes into first class values that can be passed as arguments and returned as results, modules can be replaced by functions in 1-namespace mode, so I've proposed to unify functions and modules, which both share the same new-style definitional syntax:

   f(x) = x + 1;
   box(x) = cube(s);

Because we have a developer shortage, I wouldn't try to implement OpenSCAD2 all in one go. I'd split the implementation up into phases. I wouldn't try to unify functions and modules in the first phase, because it's too big a change. The main job of the first phase is to implement the single-namespace dialect. For pragmatic reasons, I would introduce a single-namespace module definition syntax in the first phase that looks like this:

   module box(x) = cube(s);

It's identical to the current module definition syntax, with the addition of an = sign.


There's no technical reason why the 3-namespace dialect should have any limitations on what can be expressed, relative to the 1-namespace dialect. I didn't address this when I wrote the proposal up last year because it wasn't important to me, and because I never received feedback from anybody with different ideas. I'm getting that feedback now, and I thank you for it.

In order for the 3-namespace dialect to have full expressive power, we need to have 3 distinct decorators for identifiers, one for each namespace. A single decorator (@) won't do the job. The choice of decorator syntax is arbitrary, but I'll continue to use v$, f$, m$ prefixes in this proposal.

Here are some use cases that would be supported with the 3 namespace decorators.

/// define a module, in the module namespace, by computing it from an expression
m$foo = some_expression_that_returns_a_module_value();

/// define a function, in the function namespace, by computing it from an expression
f$foo = some_expression_that_returns_a_function_value();

/// in a top-level statement, invoke a module that's stored in a variable
rotate(90) v$mymodule(x);

/// in a top-level statement, call a function that returns a shape
rotate(90) f$shapefun(x);

/// in an expression, call a function that's stored in a variable
x = v$myfun(0);

/// in an expression, call a module (returning a shape value)
if (intersects(m$cube(a), m$children(0)) ...