www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 596] New: Support array, arrayliteral and struct in switch and case

reply d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=596

           Summary: Support array, arrayliteral and struct in switch and
                    case
           Product: D
           Version: unspecified
          Platform: All
        OS/Version: All
            Status: NEW
          Severity: enhancement
          Priority: P2
         Component: DMD
        AssignedTo: bugzilla digitalmars.com
        ReportedBy: lovesyao hotmail.com


struct Test{
  int x;
  int y;
  int z;
}

void main(){
  Test* t = new Test;
  const static Test c1 = {0,0,0};
  switch(*t){
    case c1://struct
      break;
    default:
      assert(0);
  }
  ubyte[] t2 = [0,1,2];
  switch(t2){
    case [0,1,2]://arrayliteral
      break;
    default:
  }
}


-- 
Nov 25 2006
next sibling parent Thomas Kuehne <thomas-dloop kuehne.cn> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

d-bugmail puremagic.com schrieb am 2006-11-25:
 http://d.puremagic.com/issues/show_bug.cgi?id=596
 void main(){
<snip>
   ubyte[] t2 = [0,1,2];
   switch(t2){
     case [0,1,2]://arrayliteral
       break;
     default:
   }
 }
Added to DStress as http://dstress.kuehne.cn/run/c/case_05_A.d http://dstress.kuehne.cn/run/c/case_05_B.d http://dstress.kuehne.cn/run/c/case_05_C.d http://dstress.kuehne.cn/run/s/switch_23_A.d http://dstress.kuehne.cn/run/s/switch_23_B.d http://dstress.kuehne.cn/run/s/switch_23_C.d Thomas -----BEGIN PGP SIGNATURE----- iD8DBQFFaIn9LK5blCcjpWoRAmSzAKCoAbyM7GpWeT91ns333aGZOpS/pQCffTPD kFNVqQQA11CBcTe8BRZfUS4= =XNA6 -----END PGP SIGNATURE-----
Nov 25 2006
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=596


Andrei Alexandrescu <andrei metalanguage.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |ASSIGNED
                 CC|                            |andrei metalanguage.com
         AssignedTo|nobody puremagic.com        |bugzilla digitalmars.com


-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Nov 26 2010
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=596


bearophile_hugs eml.cc changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |bearophile_hugs eml.cc



If structs become supported by switch, then an interesting use case is to
support Tuples of typecons too.

(A possible enhancement is to support class references too (comparing the
dynamic type), but similar switches on objects is considered a bad practice in
OO code. So maybe it's better to not support class references).

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Nov 26 2010
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=596




A possible improvement for the switch on structs is to support wildcards in
some way:

struct Foo { int x, y; }
void main() {
    auto f = Foo(1, 2);
    int f;
    switch (f) {
        case Foo(1, _): break; // all structs where x==1, y == don't care
        default: break;
    }
}

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jan 07 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=596






 struct Foo { int x, y; }
 void main() {
     auto f = Foo(1, 2);
     int f;
     switch (f) {
         case Foo(1, _): break; // all structs where x==1, y == don't care
         default: break;
     }
 }
The idea of wildcards is very useful, it turns the D switch into something useful to partially match Tuples too. This is a very commonly used operation in functional languages. But probably the "_" (underscore) is not right as wildcard, because while it's nice and clear, it's a legal variable name. Possible alternatives are a single &, or or %: case Foo(1, &): break; case Foo(1, ): break; case Foo(1, %): break; Or maybe just "void", that is longer, but adds no new tokens and is more clear than an arbitrary symbol: import std.typecons: Tuple; alias Tuple!(int, "x", int, "y") Foo; void main() { auto f = Foo(1, 2); switch (f) { case Foo(1, void): break; // any Foo with x==1, don't care y default: break; } } -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jan 30 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=596




If structs too gets supported by switch, then BigInts too become allowed:


import std.bigint;
void main() {
    auto x = BigInt(3);
    switch (x) {
        case BigInt(0): break;
        default: break;
    }
}

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Mar 21 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=596




Regarding tuple unpacking see also issue 6365

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jul 23 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=596




One more useful use is with OOP (this is GUI code):


import core.stdc.stdio;
class Control {}
class Slider : Control {}
class Button : Control {}
void main() {
    Control c = new Button;
    switch (typeof(c)) {
        case Slider: printf("A"); break;
        case Button: printf("B"); break;
        default: // probably a Control
    }
}



That is similar to this (but faster):

import core.stdc.stdio;
class Control {}
class Slider : Control {}
class Button : Control {}
void main() {
    Control c = new Button;
         if (cast(Slider)c)  { printf("A"); }
    else if (cast(Button)c)  { printf("B"); }
}

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Aug 15 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=596




Supporting something like this will be very useful (this is done very commonly
in functional languages):


import std.variant: Algebraic;
alias Algebraic!(int, float) A;
void main() {
    A a = 1.5;
    final switch (a.type()) {
        case typeid(int): ...
        case typeid(float): ...        
    }
}

But if possible I suggest to introduce a compiler optimization specific for
this usage, to avoid actually allocating and using TypeInfo class instances.

The advantage of using a final switch instead of a sequence of if statements:
- The code is more readable, the various cases are shown in a more ordered way;
- The "final" of switch makes sure all types of the Algebraic are taken into
account;
- The compiler has ways to optimize this code better (sometimes avoiding many
runtime tests).

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Sep 28 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=596




To match classes Scala uses the standard method "unapply":

http://www.scala-lang.org/node/112


object Twice {                              
  def apply(x: Int): Int = x * 2
  def unapply(z: Int): Option[Int] = if (z%2 == 0) Some(z/2) else None
}

object TwiceTest extends Application {
  val x = Twice(21)
  x match { case Twice(n) => Console.println(n) } // prints 21
}

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Feb 19 2013
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=596




When final switch supports structs, writing a fizzBuzz
(http://imranontech.com/2007/01/24/using-fizzbuzz-to-find-developers-who-grok-coding/
) gets simpler and safer:


import std.stdio, std.typecons;
void main() {
    foreach (immutable i; 1 .. 101) {
        final switch (tuple(i % 3 == 0, i % 5 == 0)) {
            case tuple(false, false): writeln(num);        break;
            case tuple(false,  true): writeln("Buzz");     break;
            case tuple( true, false): writeln("Fizz");     break;
            case tuple( true,  true): writeln("FizzBuzz"); break;
        }
    }
}

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Mar 03 2013
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=596


Simen Kjaeraas <simen.kjaras gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |simen.kjaras gmail.com



10:16:01 PST ---
The problem of your proposed pattern matching is that there is not necessarily
a simple correlation between constructor parameters and runtime values. For
instance, for this struct declaration:

struct Foo {
    int n;
    this( int a, int b ) {
        n = a * b;
    }
}

, Foo( 12, void ) just doesn't make any sense. For tuples however, this works:

struct DontCare {
    bool opEquals( T )( T other ) const {
        return true;
    }
}

enum DontCare dontCare = DontCare( );

unittest {
    import std.typecons : Tuple, tuple;

    static class A {}

    auto a = tuple( 12, "foo" );
    assert( a == tuple( 12, dontCare ) );
    assert( a == tuple( dontCare, "foo" ) );
    assert( a != tuple( 42, dontCare ) );
    assert( a != tuple( dontCare, "bar" ) );
    assert( tuple( 12, dontCare ) == a );
    assert( tuple( dontCare, "foo" ) == a );
    assert( tuple( 42, dontCare ) != a );
    assert( tuple( dontCare, "bar" ) != a );
    auto aa = new A( );
    auto b = tuple( 1.5f, aa );
    assert( tuple( 1.5f, dontCare ) == b );
    assert( tuple( dontCare, aa ) == b );
    assert( tuple( 3.5f, dontCare ) != b );
    assert( tuple( dontCare, cast(A)null ) != b );
    assert( b == tuple( 1.5f, dontCare ) );
    assert( b == tuple( dontCare, aa ) );
    assert( b != tuple( 3.5f, dontCare ) );
    assert( b != tuple( dontCare, cast(A)null ) );
}


This would allow switch statements like this:

switch (myTuple) {
    case tuple( 3, dontCare ): // A
        break;
    case tuple( dontCare, "foo" ): // B
        break;
}

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Mar 03 2013
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=596





 The problem of your proposed pattern matching is that there is not necessarily
 a simple correlation between constructor parameters and runtime values. For
 instance, for this struct declaration:
 
 struct Foo {
     int n;
     this( int a, int b ) {
         n = a * b;
     }
 }
 
 , Foo( 12, void ) just doesn't make any sense. For tuples however, this works:
Arrays don't have a constructor, so they don't have this problem in a switch. Regarding structs, the problem has some solutions (both are needed): 1) Don't accept structs that have one or more constructors, and special-case std.typecons.Tuple in the compiler so tuples are accepted (the compiler assumes their constructor is just a series of field assignments). 2) If you want to support the general case of structs that have a constructor, then such structs must have a standard method like "unapply", that is used by the switch itself. This is the solution used by Scala language, it's shown in the Comment 9: http://d.puremagic.com/issues/show_bug.cgi?id=596#c9 -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Mar 03 2013
prev sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=596




It's useful to switch on struct values:


import std.bigint;
void main() {
    auto x = BigInt(3);
    switch (x) {
        case BigInt(0): break;
        default: break;
    }
}


Other examples of Phobos structs that is useful to switch on are Nullable,
Algebraic, etc.

Switching on structs is more easy if the struct has no ctor. So it's a POD
(despite having some other method).

To support the general case of structs that have a constructor such structs
need a standard method named like "unapply", that is used by the switch itself.
This is the solution used by Scala language:

http://www.scala-lang.org/node/112


This example is in Scala language:


object Twice {                              
  def apply(x: Int): Int = x * 2
  def unapply(z: Int): Option[Int] = if (z%2 == 0) Some(z/2) else None
}

object TwiceTest extends Application {
  val x = Twice(21)
  x match { case Twice(n) => Console.println(n) } // prints 21
}



It's equivalent to the D code:


import std.stdio;
import std.typecons: Nullable;

struct Twice {
    int static opCall(int x) {
        return x * 2;
    }

    Nullable!int unapply(int z) {
        if (z % 2 == 0)
            return typeof(return)(z / 2);
        else
            return typeof(return).init;
    }
}

void main() {
    immutable int x = Twice(21);
    assert(x == 42);

    switch (x) {
        case Twice(n):
            writeln(n); // prints 21
            break;
        default:
    }
}



A different example:


import std.stdio;
import std.typecons: Nullable;

struct Foo {
    int x;

    this(int x_) {
        this.x = x_ * 2;
    }

    Nullable!int unapply(Foo f1) const {
        return typeof(return)(f1.x / 2);
    }
}

void main() {
    immutable Foo f2 = Foo(10);
    assert(f1.x == 20);

    switch (f2) {
        case Foo(5):
            writeln("First case: 5");
            break;
        case Foo(n):
            writeln(n); // Prints: 10
            break;
        default:
    }
}



A third example:

import std.stdio;

struct Even {
    bool unapply(int x) {
        return x % 2 == 0;
    }
}

void main() {
    int x = 17;

    switch (x) {
        case Even():
            writeln("even");
            break;
        default:
            writeln("odd");
    }
}


unapply() is allowed to return a bool or a Nullable (including a Nullable of a
tuple).

For more info:

http://lamp.epfl.ch/~emir/written/MatchingObjectsWithPatterns-TR.pdf

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Mar 29 2013