Monday, July 9, 2012

Ceylon - Basics by Example



Update 1: Porting code examples to Ceylon M5 - syntax changes on assignent operator, module definition and support for String Interpolation.

Extending my Basics by Example series to a new language. today's version of the post written in Ceylon.

You can copy and paste the code below in Eclipse and start playing and learning with it. This little "working" program will teach you the basics of the Programming Language.

There are some "comments" on the code added just to tell you what are or how are some features called. In case you want to review the theory, you can read my previous post, where I give a definition of each of the concepts mentioned on the code. You can find it here: http://carlosqt.blogspot.com/2010/08/new-series-languages-basics-by-example.html 


Greetings Program - Verbose
module com.series.basics '1.0.0' {
    import java.base '7';
}
// Ceylon Basics  
import java.util { Calendar, GregorianCalendar, Scanner }  
import java.lang { System { sin = \Iin } }  
  
// (Main) Constructor/Initializer  
// Instead of method and constructor overloading,   
// Ceylon supports parameters with default values and sequenced ("varargs") parameters.  
shared class Greet(_name="", _message="", _loopMessage=0) {  
    // Fields or Attributes    
    variable String _name;  
    variable String _message;  
    variable Integer _loopMessage;  
    // Getters and Setters  
    shared String name {  
        return _name;  
    }  
    assign name {  
        _name = this.capitalize(name);  
    }  
    shared String message {  
        return _message;  
    }  
    assign message {  
        _message = this.capitalize(message);  
    }  
    shared Integer loopMessage {  
        return _loopMessage;  
    }  
    assign loopMessage {  
        _loopMessage = loopMessage;  
    }  
    // Overloaded/Secondary Constructor    
    // No overloaded constructors/initializers  
    // Method 1    
    String capitalize(String val) {    
          // "if-then-else" statement    
        if (val.size > 0) {  
            return val[0..0].uppercased + val[1...];  
        }    
        else {  
            return "";  
        }  
     }  
    // Method 2    
    shared void salute() {    
        // "for" statement    
        for(i in 1..this._loopMessage) {    
            print("``this._message`` ``this._name``!");    
        }    
    }    
    // Overloaded Method    
    // No Overloaded Methods Support. New methods instead.    
    // Method 2.1    
    shared void salute21(String message, String name, Integer loopMessage) {    
        // "while" statement    
        variable Integer i = 1;    
        while (i <= loopMessage) {    
            print("``this.capitalize(message)`` ``this.capitalize(name)``!");    
            i++;    
        }    
    }    
    // Overloaded Method    
    // No Overloaded Methods Support. New methods instead.    
    // Method 2.2    
    shared void salute22(String name) {  
        Calendar dtNow = GregorianCalendar();  
        variable Integer hh = dtNow.get(dtNow.\iHOUR_OF_DAY);  
        // Ceylon doc: The type of the switched expression must be an enumerated type.   
        // You can't switch on a String or Integer. (Use if instead.)  
        // if-else-if statement  
        if (hh in 6..11) { this._message = "good morning,"; }   
        else if (hh in 12..17) { this._message = "good afternoon,"; }   
        else if (hh in 18..22) { this._message = "good evening,"; }   
        else if (hh == 23 || hh in 0..5) { this._message = "good night,"; }   
        else { this._message = "huh?"; }  
        print("``this.capitalize(this._message)`` ``this.capitalize(name)``!");  
    }  
  
}  
  
doc "Run the module `com.series.basics`."
shared void run() {  
    // Define variable object of type Greet and Instantiate. Call Constructor      
    value g = Greet();  
    // Call Setter  
    g.message = "hello";    
    g.name = "world";    
    g.loopMessage = 5;    
    // Call Method 2    
    g.salute();  
    // Call Overloaded Method 2.1 and Getter    
    g.salute21(g.message, "Ceylon", g.loopMessage);    
    // Call Overloaded Method 2.2    
    g.salute22("carlos");    
  
    print("Press any key to exit...");  
    Scanner s = Scanner(sin);  
    String line = s.nextLine();  
    s.close();  
}  

Greetings Program - Minimal
module com.series.basics '1.0.0' {
    import java.base '7';
}
// Ceylon Basics  
import java.util { Calendar, GregorianCalendar, Scanner }  
import java.lang { System { sin = \Iin } }  
  
// (Main) Constructor/Initializer  
// Instead of method and constructor overloading,   
// Ceylon supports parameters with default values and sequenced ("varargs") parameters.  
class Greet(_name="", _message="", _loopMessage=0) {  
    // Fields or Attributes    
    variable String _name;  
    variable String _message;  
    variable Integer _loopMessage;  
    // Getters and Setters  
    shared String name {  
        return _name;  
    }  
    assign name {  
        _name = capitalize(name);  
    }  
    shared String message {  
        return _message;  
    }  
    assign message {  
        _message = capitalize(message);  
    }  
    shared Integer loopMessage {  
        return _loopMessage;  
    }  
    assign loopMessage {  
        _loopMessage = loopMessage;  
    }  
    // Overloaded/Secondary Constructor    
    // No overloaded constructors/initializers  
    // Method 1    
    String capitalize(String val) {    
          // "if-then-else" statement    
        if (val.size > 0) {  
            return val[0..0].uppercased + val[1...];  
        }    
        else {  
            return "";  
        }  
     }  
    // Method 2    
    shared void salute() {    
        // "for" statement    
        for(i in 1.._loopMessage) {    
            print("``_message`` ``_name``!");    
        }    
    }    
    // Overloaded Method    
    // No Overloaded Methods Support. New methods instead.    
    // Method 2.1    
    shared void salute21(String message, String name, Integer loopMessage) {    
        // "while" statement    
        variable Integer i = 1;    
        while (i <= loopMessage) {    
            print("``capitalize(message)`` ``capitalize(name)``!");    
            i++;    
        }    
    }    
    // Overloaded Method    
    // No Overloaded Methods Support. New methods instead.    
    // Method 2.2    
    shared void salute22(String name) {  
        Calendar dtNow = GregorianCalendar();  
        variable Integer hh = dtNow.get(dtNow.\iHOUR_OF_DAY);  
        // Ceylon documentation: The type of the switched expression must be an enumerated type.   
        // You can't switch on a String or Integer. (Use if instead.)  
        // if-else-if statement  
        if (hh in 6..11) { _message = "good morning,"; }   
        else if (hh in 12..17) { _message = "good afternoon,"; }   
        else if (hh in 18..22) { _message = "good evening,"; }   
        else if (hh == 23 || hh in 0..5) { _message = "good night,"; }   
        else { _message = "huh?"; }  
        print("``capitalize(_message)`` ``capitalize(name)``!");  
    }  
  
}  
  
void run() {  
    // Define variable object of type Greet and Instantiate. Call Constructor      
    value g = Greet();  
    // Call Setter  
    g.message = "hello";    
    g.name = "world";    
    g.loopMessage = 5;    
    // Call Method 2    
    g.salute();  
    // Call Overloaded Method 2.1 and Getter    
    g.salute21(g.message, "Ceylon", g.loopMessage);    
    // Call Overloaded Method 2.2    
    g.salute22("carlos");    
  
    print("Press any key to exit...");  
    value s = Scanner(sin);  
    s.nextLine();  
    s.close();  
}  


And the Output is:



Note: the following is extracted from Ceylon documentation:
"you don't need to write getters and setters unless you're doing something special with the value you're getting or setting.
Don't ever write code like this in Ceylon:

variable String _name;
shared String name { return _name; }
assign name { _name=name; }

It's not necessary, and there's never any benefit to it."

That is exactly what I'm doing with the setters and loopMessage's getter, but remember that the code is like that just because I'm showing the syntax constructs, more importantly, this helps to compare with other languages. The code above is not intended to teach "good practices" nor "idiomatic" code.

Auto-Implemented Properties in Ceylon

In Ceylon there are no such auto-implemented properties that generate automatically a Java bean like getter/setter, instead you need to use class attributes directly using the shared annotation to make them public.


class UsingAttributesToSetGet() {  
    // Attributes   
    shared variable String name = "";  
    shared variable String message = "";  
    shared variable Integer loopMessage = 0;  
    // Methods    
    shared void salute() {    
        print("``message`` ``name`` ``loopMessage.string``!");   
    }    
}

void run() {  
    value g = UsingAttributesToSetGet();  
    // Set the attribute    
    g.message = "hello";    
    g.name = "world";    
    g.loopMessage = 5;    
    // print them out    
    g.salute();    
    // and print them again using the attribute    
    print("``g.message`` ``g.name`` ``g.loopMessage.string``!");
}  

And the output is:


2 comments:

  1. Ceylon has a new initializer syntax that helps eliminate a lot of that code that you wrote. Instead of having n, m and lm as initializer parameters and then having to declare separate attributes just to make them shared and variable, now you can do this:

    class Greet(name="", message="", loopMessage=0) {
    shared variable String name;
    shared variable String message;
    shared variable String loopMessage;

    ...
    }

    And then you can remove the getters for name and message, as well as getter and setter for loopMessage.

    You can also use default parameters in methods, not only in the initializers, so you don't really need salute() and salute21(), you can do that with only 1 method that takes 3 parameters (like salute21) with defaults:

    shared void salute21(String m=message, String n=name, Integer count=loopMessage) {...}

    The only place where you really need to do something like you did is with the salute22() method, because it does something completely different from the other 2. In practice (if this were code for a real application) it should have a different name, like timedSalute() or something.

    BTW when you have default parameters you can skip those arguments, but if you have several default parameters (like the initializer for your Greet class), if you want to pass just certain values you have two choices: if you use a positional arguments invocation then you can only skip the rightmost defaulted parameters. For example you can call Greet("A") and "A" will be passed as the name, or Greet("A", "B") and "A" will be passed as the name and "B" as the message. If you want to create a Greet instance passing just the Integer parameter, you can use named argument invocation:

    value g = Greet { count=10; };

    That will use the defaults for name and message, and 10 for the loopMessage parameter.

    ReplyDelete
    Replies
    1. Ah, I really missed that one.
      Thanks for your remarks. I will update the code to use the new initializers syntax on the class and attributes declaration.

      Regarding getters and setters I understand that I shouldn't really use them since they are not necessary, same thing with the overloaded methods, I should have used optional parameters instead, however, the purpose of this post is to compare the same syntax constructs on several languages following the program structure mentioned here: http://carlosqt.blogspot.be/2010/08/new-series-languages-basics-by-example.html

      And finally: value g = Greet { count=10; };
      That's a nice feature, pretty handy indeed.

      Thanks again for your remarks, I appreciated.

      Regards

      Delete