[feature request] variable scope definition #473

Closed
stryju opened this Issue Aug 7, 2012 · 18 comments

Projects

None yet

9 participants

@stryju
stryju commented Aug 7, 2012

hey

i thought it would be a nice idea to define variable scope when using the same variable name within some declaration as global one ( behavior similar to javascript )

example:

$background-color: red;

.foo {
  @var $background-color: blue;

  .bar {
    background: $background-color; /* blue */
  }
}

.baz {
  background: $background-color; /* red */
  /* currenlty it gets overwritten by .foo definition to value of 'blue' */
}
@chriseppstein
Member

I do think we need variable shadowing.

I'd probably vote for:

$background-color: blue !local;
@chriseppstein
Member

or maybe !scoped.

@lunelson
lunelson commented Oct 3, 2012

Another idea I had recently WRT scoping, would be to allow definition of variables inside placeholder declarations, so that when you use @extend you extend not only the properties but also inherit variables scoped to that placeholder

Something like this

@mixin my-mixin($c) {
    color: $c;
}

%my-placeholder {
    $color: red;
    height: 100px;
}

.my-test {
    @extend %my-placeholder;
    @include my-mixin($color);
}
@nex3
Contributor
nex3 commented Oct 5, 2012

@lunelson I think that leads to a very confusing experience when reading the code. It makes it very mysterious where the imported variable comes from. In addition, it doesn't really make sense with the mechanics of @extend.

@pdaoust
Contributor
pdaoust commented Oct 10, 2012

@nex3 agreed re: mixins adding variables; that could get confusing. However, I do agree with previous commenters; a !local keyword would be awesome; it would be more in line with the way a lot of languages work -- JavaScript, for instance (which a lot of front-end developers would be familiar with already) allows you to shadow variables at your discretion. This slipped me up already...

@tomengstrom

Just popping in to agree with this. I think it would be essential to have scoping inside files somehow:

layout.scss:

$var: blue;
@import "view";
.class {
    color: $var;
}

view.scss:

$var: red !local;

This would result in
layout.css:

.class {
    color: blue;
}

Extend would use the scope it's in of course.

@stryju
stryju commented Mar 12, 2013

ping? :)

@millermedeiros

I agree that there should be a way to define local scope - for me file scope would even be the default behavior and add a !global flag to set it as global, but since the boat already sailed I'm OK with a flag (!local) to keep it local to the file or add some sort of wrapper that creates a new closure - similar to JavaScript IIFE:

@scope {
  // this var will only be available inside this block
  $wid: 80px;
  .lorem {
    width: $wid;
  }
}

TBH I think the current behavior (overwriting the global var) is not intuitive. I think it should only override the global var if using a flag:

$wid: 50px;
$hei: 80px;
.lorem {
  // only affect $wid inside this block
  $wid: 10px;
  // override the global value (this is rarely what you want)
  $hei: 30px !global;
}
@pdaoust
Contributor
pdaoust commented Apr 10, 2013

@millermedeiros Re: local vars overwriting global vars being unintuitive, It depends on which language you're familiar with. JavaScript and C#, for example, both allow you to overwrite a variable in a containing context by default, and they have a local scope keyword (var in both cases) that gets used in declaration (I know C# also allows you to specify the type at declaration instead, but I'm trying to keep it simple).

Ruby, on the other hand, scopes all variables to their immediate context, except variables with a special notation ($var for globals and @var for instance variables). It might be nice to use something similar -- a symbol notation for defining scope -- since SASS has its roots in Ruby and is used by a lot of Ruby developers. However, we don't have a lot of symbols that aren't already being used by something else in CSS. I like your !local flag, although it does create an issue of syntactic ambiguity. Let's say we have this:

$wid: 50px;
@scope {
    $wid: 40px;
    $wid: 30px !local;
    width: $wid;
}

What is width in the mixin? 40px or 30px? The only solution I can think of is to 'hoist' the !local flag to the first $wid assignment/declaration, in which case all instances of $wid inside the mixin would be local. This would make sense to a JavaScript developer, but as with JavaScript, the intent isn't clear.

The !global identifier would become a bit clearer:

$wid: 50px;
@scope {
    $wid: 40px;
    $wid: 30px !global; // both global and local $wid are now 30px
    width: $wid; // 30px
}

The trouble with this is that it would break a lot of SASS that people have already written, if they're expecting mixins to be able to modify global variables. Perhaps the best solution is to use some sort of naming convention in your mixins to avoid collisions, something like $_var.

It could be argued that this should be a developer practice: mixins, functions, and style blocks should never cause side-effects like changes to variables. But this may be hard to enforce when you're working with third-party libraries; who knows what sort of magic undocumented globals they might be using?

@bjmiller

FWIW, my vote is to try to emulate Ruby in some way, even if a different syntax must be used to name variables to be global-ish versus local-ish.

This is going to take a lot to think through and get right, so let's not bust on the core maintainers too hard for taking their time on it.

@pdaoust
Contributor
pdaoust commented Apr 11, 2013

@bjmiller Those observations have got my amen.

@drgrib
drgrib commented Sep 4, 2013

I want to bring this back up to highlight how important function scoping is. This situation should never happen in a language, or at the very least there should be a way to prevent it:

@function shouldntChangeStuff() {
    $value: changeAllValues;
    @return anything;
}

@mixin unsuspectingMixin() {
    $value: dontChangeThis;
    $otherValue: shouldntChangeStuff();

    // $value now contains 'changeAllValues' !!!
}

I think it is absurd to expect that only one function in an entire SASS code base will use the variable name $value to avoid conflicting with other functions. The only way I have found to get around this is by prefixing my variable names within functions (e.g. $repeat_value). It is resulting in very ugly code. Basic function scoping please!

@nex3
Contributor
nex3 commented Sep 6, 2013

@credford If you run that code, you'll see that $value remains dontChangeThis even after shouldntChangeStuff is run. Sass is lexically scoped; only variables in a lexically-visible parent scope will be overwritten. The feature request here is to add a way to shadow global variables even when they are lexically visible.

@drgrib
drgrib commented Sep 6, 2013

@nex3 Whoa. I was using version 0.0.5 of scssphp when I got this result. I just tested it with sass 3.2.7 and you are right. I need to upgrade the scssphp or just use sass. Thanks for alerting me to this.

@stryju
stryju commented Sep 26, 2013

@nex3 @chriseppstein as I understand, it's more or less implemented in 3.3?

source: [CSSconf.eu 2013] Chris Eppstein - The Mind-blowing Power of Sass 3.3

@nex3
Contributor
nex3 commented Oct 4, 2013

@stryju no, this is currently unimplemented. It's not clear whether or not it will make it into 3.3.

@nex3 nex3 added a commit that referenced this issue Oct 12, 2013
@nex3 nex3 Make the first step towards new variable semantics.
Global variable assignment will now print a deprecation warning unless
it uses the "!global" flag. In a future release, it will assign to a
local variable instead.

Closes #473
7cd0572
@nex3 nex3 closed this Oct 12, 2013
@nex3 nex3 reopened this Oct 12, 2013
@nex3
Contributor
nex3 commented Oct 12, 2013

I implemented the part of this that's planned for 3.3; I'm leaving the issue open until the full deprecation cycle is complete.

@nex3
Contributor
nex3 commented Dec 14, 2013

Duplicate of #136.

@nex3 nex3 closed this Dec 14, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment