Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Silent namespace and mixins unlocking #2093

Closed
SomMeri opened this issue Jul 5, 2014 · 7 comments
Closed

Silent namespace and mixins unlocking #2093

SomMeri opened this issue Jul 5, 2014 · 7 comments

Comments

@SomMeri
Copy link
Member

SomMeri commented Jul 5, 2014

Should silent namespace (e.g. namespace declared with parentheses ()) behave the same way as normal ruleset namespaces? I can not find silent namespace in docs, so maybe they should not exist at all?

Ruleset-namespace:

.unlocker() { 
  .mixin() { declaration: value;  } 
}
#namespace { //difference
  .unlocker(); 
}
#test {
  #namespace > .mixin(); //unlocked mixin is available here
}

compiles into:

#test {  declaration: value; }

Silent namespace:

.unlocker() { 
  .mixin() { 
    declaration: value;
  } 
}
#namespace() { //difference
  .unlocker();
}
#test {
  #namespace > .mixin(); //unlocked mixin is not available
}

returns an error:

NameError: #namespace > .mixin is undefined in test.less on line 12, column 3:
11 #test {
12   #namespace > .mixin(); //unlocked mixin is not available
13 }
@lukeapage
Copy link
Member

A silent namespace is just a mixin containing a mixin definition is it not?
That particular form has evolved by accident afaik and I'm not sure much was ever decided. I think you and max probably know and have discussed the most about it.

@seven-phases-max
Copy link
Member

Yes, I would also remind and stress the fact that "namespace" is just an abstraction. "Silent namespace" is just an ordinal parametric mixin, same way as a "non-silent" namespace is just an ordinal non-parametric one :). I.e. we should not invent any special "namespace" rules beside the core "invoke namespace child" syntax, and all the rest, i.e. variables and mixins visibility, scope and unlocking, should follow "standard" "mixin nested within a mixin" stuff.
And any issues (there're probably a few) also should be resolved as a part of these "standard" "mixin nested within a mixin" rules.

P.S. I can't find similar issue (with . instead of #) but I'm pretty sure there's. I'll try search harder :)

@seven-phases-max
Copy link
Member

After some thinking, I guess this is an expected behaviour. Non-parametric #namespace is "evaluated" (with all of its contents incl. .unlocker();) at the point of its definition so it is known there's .mixin inside. Contrary the parametric one remains a black box (none of its content is evaluated) until we actually "call" it so at the point of #namespace > .mixin(); (which does not invoke #namespace itself and thus does not invoke .unlocker) it's truly no .mixin should exist.


P.S. Ah, found it: it's closely related to #1525, and it also has been discussed in #1812. So btw., #1525 looks like something to be fixed right after #2180 (if we want it to).


Btw. and just in case, all this does not render "parametric namespaces" to be useless as they may seem to be... They still remain pretty interesting and potentially quite valuable entity (especially counting weve got this for free :) if used as in (1) or (2). It's just more close to (classical OOP) classes/objects rather than namespaces.

@SomMeri
Copy link
Member Author

SomMeri commented Sep 7, 2014

@seven-phases-max I think that your last comment amounts to "it works as currently implemented therefore it works correctly" :).

I did not knew silent namespaces are accidental. I did not questioned less.js a year ago. I used to assume that less.js is bug free and everything works as designed. I even ported couple of suspicious "features" into less4j you later fixed as bugs. Silent namespaces were mentioned somewhere in some issue comment, worked, therefore they must have been a feature designed from the start.

@seven-phases-max
Copy link
Member

@SomMeri

"it works as currently implemented therefore it works correctly"

Yes, sort of... it sounds like so, but actually it is the opposite: "it works correctly because it's implemented correctly" (even if by accident), or in other words: it works correctly because it is supposed to work so (and not the other way (imho of course)). In summary: internals of a parametric mixin should not be considered until this mixin is actually invoked. Hence #namespace > .mixin(); can't call .mixin(); because neither #namespace() nor .unlocker() were called and thus there's no .mixin unlocked there. Technically we could probably consider to treat #namespace > part as #namespace() (so that anything inside #namespace is evaluated before > .mixin() and it becomes visible) but this would be quite heavy I think.

The following example is actually equal in terms of visibility (to illustrate the "heavy" part):

#ns() {
    @x: 1;
    .mixin() {
        1: @x;
    }
}

.z {
    #ns > .mixin();
}

For this to work we would need to either expose @x right into .z before .mixin expansion (so #ns > .mixin(); becomes equal to #ns; .mixin();) or to create some temporary scope (with @x in it) and then call .mixin inside there. And both variants are most likely to introduce new side-effects/issues.

So I'd rather support #ns() > .mixin(); syntax for such "heavy" evaluation (even if it contradicts with with plain .mixin;/.mixin(); syntax where parens are optional. Those optional parens proved themselves to be root of various problems so I would rather stop propagating them further (like we already did with DR where only @var(); is valid).

@seven-phases-max
Copy link
Member

Hmm, and thinking of it more in context of the @x example it becomes even more curious. If I'm not mistaken the same snippet for non-parametric #ns makes @x visible inside .mixin just by exposing #ns contents right into .mixin itself (hmm, in the code it's a.k.a. "attaching frames", right?). So we could use the same for parametric namespace when it comes to the @x example but this won't solve the initial .unlocker snippet: we can't expose .unlocker() into the .mixin because it is the .unlocker who expose the .mixin into #ns :) Honesty I'm not already sure I did not make any logical mistakes before this point so I guess I hold up here.

@stale
Copy link

stale bot commented Nov 14, 2017

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants