type-of list with a single item returns 'string' #679

Closed
myakura opened this Issue Mar 6, 2013 · 13 comments

Comments

Projects
None yet
5 participants
@myakura

myakura commented Mar 6, 2013

While these work perfectly:

>> type-of(())
"list"
>> type-of((one, two))
"list"

Why does it return string when passing a list with a single item?

>> type-of((one))
"string"
@StefanoRausch

This comment has been minimized.

Show comment
Hide comment
@StefanoRausch

StefanoRausch Mar 6, 2013

According to File: SASS_REFERENCE

Lists are just a series of other values, separated by either spaces or commas. In fact, individual values count as lists, too: they’re just lists with one item.

Unfortunately type-of does not support single item lists, even if they are explicitly defined with ( ). Hopefully it will be fixed in future.

According to File: SASS_REFERENCE

Lists are just a series of other values, separated by either spaces or commas. In fact, individual values count as lists, too: they’re just lists with one item.

Unfortunately type-of does not support single item lists, even if they are explicitly defined with ( ). Hopefully it will be fixed in future.

@myakura

This comment has been minimized.

Show comment
Hide comment
@myakura

myakura Mar 6, 2013

Unfortunately type-of does not support single item lists, even if they are explicitly defined with ( ). Hopefully it will be fixed in future.

Yeah... Curretnly I get around by doing the following, but do hope it'll be fixed...

@if type-of($list) == 'string' {
    $list: append((), $list);
}

myakura commented Mar 6, 2013

Unfortunately type-of does not support single item lists, even if they are explicitly defined with ( ). Hopefully it will be fixed in future.

Yeah... Curretnly I get around by doing the following, but do hope it'll be fixed...

@if type-of($list) == 'string' {
    $list: append((), $list);
}
@nex3

This comment has been minimized.

Show comment
Hide comment
@nex3

nex3 Mar 6, 2013

Contributor

Sass doesn't use () as list delimiter characters. (1, 2, 3) returns the list 1, 2, 3 for the same reason that (1 + 2) returns the number 3.

Contributor

nex3 commented Mar 6, 2013

Sass doesn't use () as list delimiter characters. (1, 2, 3) returns the list 1, 2, 3 for the same reason that (1 + 2) returns the number 3.

@nex3 nex3 closed this Mar 6, 2013

@StefanoRausch

This comment has been minimized.

Show comment
Hide comment
@StefanoRausch

StefanoRausch Mar 7, 2013

Isn't that inconsistent with the empty list definition $list : ();? Braces should force $list : ( 1 ); type-of( $list ) == list; and this only for single item lists. If I really want the literal than I just write $number : 1;.

Just food for thoughts.

Isn't that inconsistent with the empty list definition $list : ();? Braces should force $list : ( 1 ); type-of( $list ) == list; and this only for single item lists. If I really want the literal than I just write $number : 1;.

Just food for thoughts.

@cimmanon

This comment has been minimized.

Show comment
Hide comment
@cimmanon

cimmanon Mar 7, 2013

It is consistent if you consider that parentheses are used to determine order of precedence in expressions:

$foo: (5 + 6);
$bar: -(8 + 7);
$baz: (3 + 2) * 9;

Should any of these items automatically be cast to lists simply because they use parentheses? Keep in mind that strings can be added together as well.

cimmanon commented Mar 7, 2013

It is consistent if you consider that parentheses are used to determine order of precedence in expressions:

$foo: (5 + 6);
$bar: -(8 + 7);
$baz: (3 + 2) * 9;

Should any of these items automatically be cast to lists simply because they use parentheses? Keep in mind that strings can be added together as well.

@StefanoRausch

This comment has been minimized.

Show comment
Hide comment
@StefanoRausch

StefanoRausch Mar 7, 2013

I am with you as long as there are expressions involved. However, and as we now e.g. index does currently not recognise a single item list, even though the "manual" does speak of them.

I am really only focusing on single item lists, which - and please do correct me, if I am wrong - for the time being can not directly be created. These could be used as a starting point to populate it with additional values. Sure, one can / has to write $list : join( (), $value ); … just more typing, but I am digressing …

I am with you as long as there are expressions involved. However, and as we now e.g. index does currently not recognise a single item list, even though the "manual" does speak of them.

I am really only focusing on single item lists, which - and please do correct me, if I am wrong - for the time being can not directly be created. These could be used as a starting point to populate it with additional values. Sure, one can / has to write $list : join( (), $value ); … just more typing, but I am digressing …

@nex3

This comment has been minimized.

Show comment
Hide comment
@nex3

nex3 Mar 7, 2013

Contributor

It would be very confusing if (3) and (1 + 2) had different values. I'm not saying that the current system isn't confusing at all, but I think it's minimally surprising given the constraints under which we're working.

In almost all cases, individual values are treated as lists with a single item. type-of is one exception, although since all list functions treat individual values as single-item lists using type-of to detect lists shouldn't be necessary. index and zip also used to be exceptions, but that was a bug that has since been fixed (see #668). The only remaining place where this is an issue is a single-item list containing another list, for which we may add a special function in the future.

Contributor

nex3 commented Mar 7, 2013

It would be very confusing if (3) and (1 + 2) had different values. I'm not saying that the current system isn't confusing at all, but I think it's minimally surprising given the constraints under which we're working.

In almost all cases, individual values are treated as lists with a single item. type-of is one exception, although since all list functions treat individual values as single-item lists using type-of to detect lists shouldn't be necessary. index and zip also used to be exceptions, but that was a bug that has since been fixed (see #668). The only remaining place where this is an issue is a single-item list containing another list, for which we may add a special function in the future.

@StefanoRausch

This comment has been minimized.

Show comment
Hide comment
@StefanoRausch

StefanoRausch Mar 8, 2013

Excellent and well spoken - it is really just a minor "issue", if we can call it that way ;) So the loosing ends are being tied up and all is good.

Excellent and well spoken - it is really just a minor "issue", if we can call it that way ;) So the loosing ends are being tied up and all is good.

@robwierzbowski

This comment has been minimized.

Show comment
Hide comment
@robwierzbowski

robwierzbowski Mar 8, 2013

Contributor

I just finished coding some complex list logic in Sass, and I think the current behavior works well. My experience:

Single items work great with Sass list functions:

@debug type-of('this'); // String
@debug join('this', 8, 'comma'); // List: 'this', 8
@debug append('this', 8, 'comma'); // List: 'this', 8 
@debug length('this'); // 1
@debug nth('this', 1); // 'this'

So manipulating single items as if they were lists or list parts is no problem.

If single list items stay typed as lists you'd have to unwrap them before performing any logic or operations on them (e.g., > and < comparison, math, concatenation). The current code (which I used quite a bit):

@if type-of($var) != "list" {
  ... 
}

would have to become

@if length($var) == 1 {
  $var: nth($var, 1);
  ...
}

Not the end of the world, but clunky, verbose and less enjoyable all around.

Strong typing seems a bit much for Sass. In general Sass is programming language lite — just a bit of logic, a few functions, see what you can build. And I love that about it.

@myakura, I'm sure I missed situations where single list items are useful. What was your use case?

If you're curious, here's the extension I was working on, and here's a partial with a lot of list handling and manipulation.

Contributor

robwierzbowski commented Mar 8, 2013

I just finished coding some complex list logic in Sass, and I think the current behavior works well. My experience:

Single items work great with Sass list functions:

@debug type-of('this'); // String
@debug join('this', 8, 'comma'); // List: 'this', 8
@debug append('this', 8, 'comma'); // List: 'this', 8 
@debug length('this'); // 1
@debug nth('this', 1); // 'this'

So manipulating single items as if they were lists or list parts is no problem.

If single list items stay typed as lists you'd have to unwrap them before performing any logic or operations on them (e.g., > and < comparison, math, concatenation). The current code (which I used quite a bit):

@if type-of($var) != "list" {
  ... 
}

would have to become

@if length($var) == 1 {
  $var: nth($var, 1);
  ...
}

Not the end of the world, but clunky, verbose and less enjoyable all around.

Strong typing seems a bit much for Sass. In general Sass is programming language lite — just a bit of logic, a few functions, see what you can build. And I love that about it.

@myakura, I'm sure I missed situations where single list items are useful. What was your use case?

If you're curious, here's the extension I was working on, and here's a partial with a lot of list handling and manipulation.

@StefanoRausch

This comment has been minimized.

Show comment
Hide comment
@StefanoRausch

StefanoRausch Mar 9, 2013

Interesting. BTW, in _parse.scss::@function bp-unpack-list($bp-list) you're stating that Sass doesn't allow recursive functions?

Here is my function I am currently using in one of my projects:

@function flatten-list( $list )
{
    $normalised : ();

    @if $list != null {
        @if type-of( $list ) == list {
            @each $item in $list {
                @if $item == null {
                    $item : "null";
                }

                $normalised : join( $normalised, flatten-list( $item ), comma );
            }
        }
    }

    @else {
        $list : "null";
    }

    @return if( $normalised == (), $list, $normalised );
}

and it works like a charm ;)

Interesting. BTW, in _parse.scss::@function bp-unpack-list($bp-list) you're stating that Sass doesn't allow recursive functions?

Here is my function I am currently using in one of my projects:

@function flatten-list( $list )
{
    $normalised : ();

    @if $list != null {
        @if type-of( $list ) == list {
            @each $item in $list {
                @if $item == null {
                    $item : "null";
                }

                $normalised : join( $normalised, flatten-list( $item ), comma );
            }
        }
    }

    @else {
        $list : "null";
    }

    @return if( $normalised == (), $list, $normalised );
}

and it works like a charm ;)

@nex3

This comment has been minimized.

Show comment
Hide comment
@nex3

nex3 Mar 9, 2013

Contributor

The current behavior isn't going to change. The main problem with it is that it's very difficult to represent a single-item list containing another list. There is no value of $var such that nth($var, 1) returns 1, 2, 3, which is a problem. We're likely to solve that by adding an additional function, though, rather than changing the existing semantics.

Contributor

nex3 commented Mar 9, 2013

The current behavior isn't going to change. The main problem with it is that it's very difficult to represent a single-item list containing another list. There is no value of $var such that nth($var, 1) returns 1, 2, 3, which is a problem. We're likely to solve that by adding an additional function, though, rather than changing the existing semantics.

@robwierzbowski

This comment has been minimized.

Show comment
Hide comment
@robwierzbowski

robwierzbowski Mar 9, 2013

Contributor

Off topic @StefanoRausch:

My function needed to return one deep nested lists, not entirely flattened (e.g., from (one two) ((three four) (five six)) to (one two), (three four), (five six). Thanks for reading the code, and if you want to chat more hit me up on the repo issue queue or twitter.

Contributor

robwierzbowski commented Mar 9, 2013

Off topic @StefanoRausch:

My function needed to return one deep nested lists, not entirely flattened (e.g., from (one two) ((three four) (five six)) to (one two), (three four), (five six). Thanks for reading the code, and if you want to chat more hit me up on the repo issue queue or twitter.

@robwierzbowski

This comment has been minimized.

Show comment
Hide comment
@robwierzbowski

robwierzbowski Mar 9, 2013

Contributor

@nex3, that's the case I was missing. I got around that exact situation by using and testing for comma separated lists at points in my code, but it was a bit hackey.

Contributor

robwierzbowski commented Mar 9, 2013

@nex3, that's the case I was missing. I got around that exact situation by using and testing for comma separated lists at points in my code, but it was a bit hackey.

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