Very interesting Switch/case in Swift - What other languages support this?

The Swift language presents a very interesting and very intuitive way to work with intervals using switch-case, with techniques of "partial matching", "pattern-matching" etc., see just 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 have never been able to make a switch case like this. I was also never curious if it would work : -) I always used an approach of different logic.

Editing: Adding an amazing 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 parse by default, given that it is a point in X and the rest of the axes in zero. "On the X axis". In Java would not use a switch case and simply a getx of the object not interesting the rest, all with else if, would write less and have the same result. But I thought Swift's way of having that freedom was different.

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

Hugs everyone.

Author: Mateus, 2016-06-30

4 answers

All languages that allow pattern matching :Q you know the term of that now.

I don't know if I'm risking to say that all functional languages support this mechanism. Some in one way, some in another, some more limited. For better or for worse none is used intensively in the market. Some examples in Wikipedia .

Most modern languages, even non-functional, are supporting the engine because it is useful. Some languages are evolving to support, is the case with C # . Which will make, along with Swift, the only languages with expressive use (Swift still needs to eat beans, but gets there fast) that support this for some time. As far as I know, Java, C/C++ / Obj C, PHP, Python, Perl, Ruby, js, Moon, Delphi, VB.NET, etc. they're not planning this anytime soon. But it can change, stay tuned. In fact I have seen something being said in C++ and well above in Java.

Rust is a aspiring language you have.

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

 15
Author: Maniero, 2020-02-03 21:39:06

In Python there was intense discussion of this in forums discussing the future of the language earlier this year - but the inclusion of a Specialized Command equivalent to switch/case was declined by the community (again).

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

As for matching - it is not part of the syntax of the language, it can be embedded in any third-party package and used as a normal function (just like regular expressions in Python). Again, readability wins. If a third-party matching package appears that is actually a "de facto standard" it may be incorporated into the default library of the language. (Ranges have been supported in Python for a long time)

 10
Author: jsbueno, 2016-06-30 13:47:11

Warning: This answer is a complement.

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

  • little benefit to the level of complexity of implementing and maintaining-thinking about compatibility with future releases and previous releases.
  • more advanced features that visibly impact performance tend to be used incorrectly 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 original switch, it is unnecessary to check the lower range if the age is always >= 0.

However, even if it was necessary to perform complex logic of any kind, it would be enough to change the match method of the above enum. The big plus is that the "search" method would not change, since it is decoupled from the implementation.

Multiple values

Comparing all values is always inefficient. Java postponed switch to strings for this reason. It is much simpler and more efficient to use a previously prepared map, by 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 query:

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

Coordinates

In Java you need a class and if it is immutable, better yet, 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 claiming that languages like Java are perfect. There are, yes, many improvements that are missing in it and others language.

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

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

 8
Author: utluiz, 2016-07-01 03:34:26

Complementing, by way of curiosity.

Comparing with the examples of the question, I demonstrate how it would look in a language like PHP. Note that virtually 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

Does not change at all. 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 stripos () function. Just swap for equivalent function in JS.

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 thing is to use array and compare the 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 examples above is Boolean true.

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

 7
Author: Daniel Omine, 2016-07-01 04:08:31