Very interesting switch/case on Swift - What other languages support this?

Asked

Viewed 1,231 times

14

The Swift language presents a very interesting and very intuitive way to work with intervals using switch-case, with "partial matching" techniques, "Pattern-matching" etc, look at these examples:

let age = 33

switch(age){
case let x where x >= 0 && x <= 2:
    print("Infant")
case let x where x >= 3 && x <= 12:
    print("Child")
case let x where x >= 13 && x <= 19:
    print("Teenager")
case let x where x >= 20 && x <= 39:
    print("Adult")
case let x where x >= 40 && x <= 60:
    print("Middle aged")
case let x where x >= 61:
    print("Elderly")
default:
    print("Invalid")
}

Another very interesting example:

let string = "Dog"
switch(string){
    case "Cat","Dog":
        print("Domestic animal")
    case "Lion","Leopard","Pantera":
        print("Never touch me")
    default:
        print("By the way, don't touch... :-)")
} 

Are there other languages that enable these features? In Java I’ve never been able to make a switch case like this. I’ve also never been curious to know if it would work :-) I’ve always used a different logic approach.

Editing: Adding an incredible example of "partial matching":

let coordinates: (x: Int, y: Int, z: Int) = (3, 0, 0)
switch (coordinates) {
case (0, 0, 0): // 1
    print("Origin")
case (_, 0, 0): // 2
    print("On the x-axis.")
case (0, _, 0): // 3
    print("On the y-axis.")
case (0, 0, _): // 4
    print("On the z-axis.")
default:        // 5
    print("Somewhere in space")
}

In the above case, the program will analyze by default, since it is a point in X and the rest of the axes at zero. "On the X Axis". In Java you would not use a switch case and simply a getx of the object no matter the rest, everything with Else if, would write less and have the same result. But I thought it was different the way Swift had this freedom.

It seems to me that VB also has a similar technique.

Hugs to all.

  • 2

    In the second case, in Java would look like this: case "Cat": case "Dog": {lógica} break;.

  • @True! In the second case is possible even, you’re right! In the first case it’s a bit interesting right! I can complement with other examples as well. I added an example of "partial matching"

  • To my knowledge, this type of matching is first emerged in functional programming languages (ML, Haskell, Ocaml, and the like). It is especially useful when combined with algebraic data types (tagged).

  • @Matthew I know the question is long overdue but the use of the Let Where case is totally unnecessary if you don’t use the variable x in your cases. In this case you should use ranges in your cases and in the latter case if it is Swift 4 you can use partial range operato if you do not want to put an age limit. For example switch age {&#xA;case 0...2:&#xA; print("Infant")&#xA;case 3...12:&#xA; print("Child")&#xA;case 13...19:&#xA; print("Teenager")&#xA;case 20...39:&#xA; print("Adult")&#xA;case 40...60:&#xA; print("Middle aged")&#xA;case 61...:&#xA; print("Elderly")&#xA;default:&#xA; print("Invalid")&#xA;}

  • Another thing none of the answers even addressed the use of keyword fallthrough. A good example is this answer that I put on the site in English https://stackoverflow.com/a/31782490/2303865

  • Also missing show use of switch with enumerations in Swift where it is not necessary to add default case (about being exhaustive) Check out Nate Cook’s response to this https://stackoverflow.com/a/26686733/2303865

Show 1 more comment

4 answers

15


All languages that allow Pattern matching :P You know the term now.

I’m not sure I’m taking the risk that all functional languages support this mechanism. Some in one way, others in another, some more limited. For good or for evil none is used intensively in the market. Some examples on Wikipedia.

Most modern languages, even non-functional languages, are supporting the mechanism because it is useful. Some languages are evolving to support, is the case of C#. Along with Swift, they were the only expressive language to support this for some time. Some languages are studying adoption or even taking first steps, to have a similar mechanism, one way or another, with more or less quality. This includes Java, PHP, Python, just to name the main ones I know.

Of course, other languages of lesser use already have the mechanism, among them Rust.

As far as I know C/C++/Objective C, Perl, Ruby, JS, Lua, Delphi, VB.NET, etc. are not planning it so soon. But you can change, stay tuned. Some have more library mechanisms that help, but it is not in the language.

In any language it is possible to simulate this, but it will not be very convenient and may affect performance.

See more in What is the Pattern Matching?.

  • thank you so much for answering. Nice to know that C# will offer this feature. As I said in my question it is clear that logic could be expressed without specific use of the switch. But with switch case seemed much less verbose, including no use of break.

  • There is an idea rolling to put this in Java. See: http://openjdk.java.net/jeps/305 and http://cr.openjdk.java.net/~briangoetz/Amber/

10

In Python there was an intense discussion about this in the forums where we discuss the future of language at the beginning of the year - but the inclusion of a specialized command equivalent to switch/case was declined by the community (again).

That is why it is understood that the if/elif/else do Python caters to everything you can do with a switch/case and even more expressiveness (since expressions may not only be matching, but include other tests as well). The only "cost" is having to repeat the variable tested in each occurrence of elif - but this is also accompanied by greater readability.

As to the matching - if it is not part of the language syntax, it can be embedded in any third package and used as a normal function (as with regular expressions in Python). Again, it beats legibility. If a third matching package appears that is actually a "de facto pattern" it may be embedded into the standard language library. (Ranges have been supported in Python for a long time)

  • thank you so much for sharing your experience!

8

Warning: this reply is a complement.

Supporting certain specific types of syntax - such as this - are almost always considered low priority for several languages for some reasons, among them:

  • Little benefit to the level of complexity of implementing and maintaining - thinking of compatibility with future versions and earlier versions.
  • More advanced features that visibly have an impact on performance tend to be misused by many users, so some languages prefer to stick to what is simpler.
  • There are other better ways to do the same thing.

Examples in Java

Age range

Given an Enum:

enum LifeStage {
    Infant(2), Child(12), Teenager(19), Adult(39), MiddleAged(60), Elderly(Integer.MAX_VALUE);
    private final int limite;
    LifeStage(int limite) {
        this.limite = limite;
    }
    public boolean match(int age) {
        return age <= limite;
    }
}

The code to find the description for age falls to a line using functional programming:

int age = 33;
Optional<LifeStage> stage = Arrays.stream(LifeStage.values()).filter(v -> v.match(age)).findFirst();
System.out.println(stage.get());

In the switch original, it is unnecessary to check the lower range if the age is always >= 0.

However, even if it were necessary to execute a complex logic of any kind, it would be enough to change the method match of Enum above. The great advantage is that the "search" method would not change as it is decoupled from the implementation.

Multiple values

Comparing all values is always inefficient. Java postponed the switch for strings for this reason. It is much simpler and more efficient to use a previously prepared map, for example:

Map<String, String> animalCategories = new HashMap<>();
String da = "Domestic Animal";
animalCategories.put("Cat", da);
animalCategories.put("Dog", da);
String ntm = "Never touch me";
animalCategories.put("Lion", ntm);
animalCategories.put("Leopard", ntm);
animalCategories.put("Pantera", ntm);

And then just consult:

String dog = "Dog";
String category = animalCategories.get(dog);
System.out.println(category);

Coordinates

In Java you need a class and if it is immutable, even better, just define the "description" of the coordinate type in the constructor, and you can use a ternary composition for this:

class Coord {
    private final int x, y, z;
    private final String desc;

    Coord(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
        desc = x == 0 && y == 0 & z ==0 ? "Origin" :
                x != 0 && y == 0 & z ==0 ? "On the x-axis." :
                x == 0 && y != 0 & z ==0 ? "On the y-axis." :
                x == 0 && y == 0 & z !=0 ? "On the z-axis" :
                "Somewhere in space";
    }
    public String getDesc() {
        return desc;
    }
}

And to use:

Coord c = new Coord(3, 0, 0);
System.out.println(c.desc);

Considerations

I’m not saying that languages like Java are perfect. There are, yes, many improvements that are missing in it and in other languages.

On the other hand, what needs to be clear in the "war of languages" is that having the largest set of functionalities or the largest variety of syntactic constructions is not a relevant factor in its absolute value.

Some authors argue that a new functionality is only worth it if the benefit obtained by its use is greater than the overhead matters by its use.

  • many thanks for sharing a very cool, complete and well explanatory and detailed reply! My congratulations master!

7

Complementing, as a matter of curiosity.

Comparing to the examples of the question, I demonstrate what it would look like in a language like PHP. Note that almost the same syntax applies in Javascript, Actionscript, and many others with similar syntax.

I will put only one condition for each to reduce the codes

Example 1

SWIFT

let age = 33

switch(age){
case let x where x >= 0 && x <= 2:
    print("Infant")
default:
    print("Invalid")
}

PHP

$age = 33;
switch (true) {
    case ($age >= 0 && $age <= 2):
        echo 'infant';
        break;
    default:
        echo 'invalid';
        break;
}

Javascript

var age = 33;
switch (true) {
    case (age >= 0 && age <= 2):
        console.log('infant');
        break;
    default:
        console.log('invalid');
        break;
}

Example 2

SWIFT

let string = "Dog"
switch(string){
    case "Cat","Dog":
        print("Domestic animal")
    case "Lion","Leopard","Pantera":
        print("Never touch me")
    default:
        print("By the way, don't touch... :-)")
} 

PHP

It does not change anything. It follows the same logic as the example:

$str = 'Dog';
switch (true) {
    case stripos('Cat, Dog', $str):
        echo 'Domestic animal';
        break;
    default:
        echo 'By the way, don't touch... :-)';
        break;
}

Javascript

Identical to PHP except for the strips() function. Simply switch to an equivalent JS function.

Example 3

SWIFT

let coordinates: (x: Int, y: Int, z: Int) = (3, 0, 0)
switch (coordinates) {
case (0, 0, 0): // 1
.......

Finally, it follows the same logic as the other two examples above.

In this case, in PHP, the most obvious is to use array and compare values.

$arr = array(0, 0 , 0);
switch (true) {
    case (funcao_para_comparar($arr, array(0, 0, 0))):
    ...... faz as coisas que tem que fazer ,etc..


function funcao_para_comparar($arr1, $arr2) {
  ... faz as firulas aqui e pá.
}

The important thing is that the condition returns the expected value in the switch() parameter, which in the above examples is boolean true.

Since the codes are repetitive, I find it unnecessary to write more of the same.

  • "Do the wrinkles here and shovel". Best example Ever. :)

  • 1

    Daniel, thanks for sharing your comparative experience with PHP and JS! Really cool! Thanks for the master contribution!

Browser other questions tagged

You are not signed in. Login or sign up in order to post.