Saturday, October 30, 2010

Fantom - Basics by Example



Continue with the Basics by Example; today's version of the post written in Fantom Enjoy!

You can copy and paste the code below in your favorite IDE/Editor 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
// Fantom Basics
internal class Greet  
{
    // Fields or Attributes
    private Str? message  
    private Str? name 
    private Int? loopMessage 
    // Properties, Getters and Setters
    public Str Message
    { 
      get { return this.&message }
      set { this.&message = this.capitalize(it) }
    }
    public Str Name
    { 
      get { return this.&name }
      set { this.&name = this.capitalize(it) }
    }
    public Int LoopMessage
    { 
      get { return this.&loopMessage }
      set { this.&loopMessage = it }
    }
    // Constructor
    public new make()
    {   
        this.message = ""
        this.name = ""
        this.loopMessage = 0
    }  
    // Overloaded Constructor
    // No Overloaded Constructor support as designed
    // Method 1
    private Str capitalize(Str val)
    {
        // "if-then-else" statement  
        if (val.size() >= 1) {  
            return val.capitalize()
        }  
        else  {  
            return ""
        }  
    }
    // Method 2
    public Void salute() 
    {  
        // "for" statement
        for (i:=1; i<=this.loopMessage; i++) 
        {  
            echo("$this.message $this.name!")  
        }  
    }  
    // Overloaded Method
    // No Overloaded Methods Support in Fantom. News methods instead.
    // Method 2.1  
    public Void salute21(Str message, Str name, Int loopMessage) 
    {  
        // "while" statement  
        Int i := 0
        while(i < loopMessage) 
        {  
            echo("${this.capitalize(message)} ${this.capitalize(name)}!")            
            i++
        }  
    }  
    // Method 2.2
    public Void salute22(Str name) 
    {
        // "switch/case" statement  
        DateTime dtNow := DateTime.now();
        switch(dtNow.hour)
        {
            case 6: case 7: case 8: case 9: case 10: case 11:  
                this.message = "good morning," 
            case 12: case 13: case 14: case 15: case 16: case 17:  
                this.message = "good afternoon,"
            case 18: case 19: case 20: case 21: case 22:  
                this.message = "good evening," 
            case 23: case 0: case 1: case 2: case 3: case 4: case 5:  
                this.message = "good night,"
            default:
                this.message = "huh?" 
        }
        echo("${this.capitalize(this.message)} ${this.capitalize(name)}!")            
    }
}  

// Console Program
public class FanGreetProgram  
{  
    public static Void main()  
    {  
        // Define variable object of type Greet and Instantiate. Call Constructor
        Greet g := Greet.make()
        // Call Setters
        g.Message = "hello"
        g.Name = "world"
        g.LoopMessage = 5
        // Call Method 2    
        g.salute()  
        // Overloaded Method 2.1 and Getters    
        g.salute21(g.Message, "fantom", g.LoopMessage)   
        // Overloaded Method 2.2    
        g.salute22("carlos")  

        // Stop and exit    
        echo("Press any key to exit...")
        console := Env.cur
        userInput := console.in.readLine
    }  
}

Greetings Program - Minimal
// Fantom Basics
class Greet  
{
    // Fields or Attributes
    private Str? message  
    private Str? name 
    private Int? loopMessage 
    // Properties, Getters and Setters
    Str Message
    { 
      get { return &message }
      set { &message = capitalize(it) }
    }
    Str Name
    { 
      get { return &name }
      set { &name = capitalize(it) }
    }
    Int LoopMessage
    { 
      get { return &loopMessage }
      set { &loopMessage = it }
    }
    // Constructor
    new make()
    {   
        message = ""
        name = ""
        loopMessage = 0
    }  
    // Overloaded Constructor
    // No Overloaded Constructor support as designed
    // Method 1
    private Str capitalize(Str val)
    {
        // "if-then-else" statement  
        if (val.size >= 1) {  
            return val.capitalize
        }  
        else  {  
            return ""
        }  
    }
    // Method 2
    Void salute() 
    {  
        // "for" statement
        for (i:=1; i<=loopMessage; i++) 
        {  
            echo("$message $name!")  
        }  
    }  
    // Overloaded Method
    // No Overloaded Methods Support in Fantom. News methods instead.
    // Method 2.1  
    Void salute21(Str message, Str name, Int loopMessage) 
    {  
        // "while" statement  
        Int i := 0
        while(i < loopMessage) 
        {              
            echo("${capitalize(message)} ${capitalize(name)}!")
            i++  
        }  
    }  
    // Method 2.2
    Void salute22(Str name) 
    {
        // "switch/case" statement  
        DateTime dtNow := DateTime.now;
        switch(dtNow.hour)
        {
            case 6: case 7: case 8: case 9: case 10: case 11:  
                message = "good morning," 
            case 12: case 13: case 14: case 15: case 16: case 17:  
                message = "good afternoon,"
            case 18: case 19: case 20: case 21: case 22:  
                message = "good evening," 
            case 23: case 0: case 1: case 2: case 3: case 4: case 5:  
                message = "good night,"
            default:
                message = "huh?" 
        }
        echo("${capitalize(message)} ${capitalize(name)}!")
    }
}  

// Console Program
class FanGreetProgram  
{  
    static Void main()  
    {  
        // Define variable object of type Greet and Instantiate. Call Constructor
        Greet g := Greet()
        // Call Setters
        g.Message = "hello"
        g.Name = "world"
        g.LoopMessage = 5
        // Call Method 2    
        g.salute
        // Overloaded Method 2.1 and Getters    
        g.salute21(g.Message, "fantom", g.LoopMessage)   
        // Overloaded Method 2.2    
        g.salute22("carlos")  

        // Stop and exit    
        echo("Press any key to exit...")
        console := Env.cur
        userInput := console.in.readLine
    }  
}


And the Output is:


















Emulating Overloaded Constructor in Fantom
Fantom does not support Overloading Constructors by design, but you can some how "emulate" it by calling the constructor with an initializers list as shown here below.
class Greet  
{
    // defining public fields
    Str? message  
    Str? name 
    Int? loopMessage
    // Constructor
    new make()    
    {   
        echo("greet.make")
        message = "empty_message"
        name = "empty_name"
        loopMessage = 0
    }          
    // Method 2  
    Void Salute()  
    {  
        echo("$this.loopMessage $this.message $this.name!")
    }  
}  

class Program  
{  
    static Void main()  
    {  
        // calling constructor (printing greet.make)
        Greet g := Greet()        
        g.Salute()
        // calling constructor (printing greet.make) and explicitly assigning values to each field
        // this can be done if the fields are defined as public 
        // or you will get a Private field xxx not accessible
        Greet g2 := Greet{ message="hello"; name="world"; loopMessage=5 }
        g2.Salute()
    }  
}

And the Output is:

3 comments:

  1. Great example - good to see Fantom getting more exposure!!

    A couple of suggestions on things that are simpler and IMHO make the example much cleaner (although I appreciate you're trying to show the core functionality across a range of languages and not getting too far into the idioms of each):


    Your properties can be rewritten as:

    // Slot storage is implicit in the definition, and since they're initialised
    // in the constructor, they can be non-nullable (Str not Str?)

    Str message {
    set { &message = Capitalize(it) }
    }
    Str name {
    set { &name = Capitalize(it) }
    }
    Int loopMessage


    Your capitalize function can be made a bit more succinct:

    // Optional () on isEmpty - since it takes no args

    private Str Capitalize(Str val) {
    if (val.isEmpty) {
    return Str.defVal
    } else {
    return val.capitalize()
    }
    }


    Or if you make the names non-nullable as mentioned above:

    // No () on capitalize - again no args, so not needed
    // Also no "return" - if there's a single statement,it's optional.
    private Str Capitalize(Str val) {
    val.capitalize
    }

    (or just remove completely and inline the calls)


    Your Salute() method could be rewritten with Int.times and closure (appreciate you're trying to illustrate the for() loop):

    Void Salute() {
    loopMessage.times { echo("$message $name!")}
    }

    Or with Range.each and a closure:

    Void Salute() {
    (1..loopMessage).each { echo("$message $name!")}
    }


    Your Salute21 method could also use string interpolation with the Capitalize function:

    Void Salute21(Str message, Str name, Int loopMessage) {
    Int i := 0
    while(i < loopMessage) {
    echo("${Capitalize(message)} ${Capitalize(name)}!")
    i++
    }
    }

    ReplyDelete
  2. Also, the method names should start with a lower case letter, not an uppercase one.

    ReplyDelete
  3. @ M

    Thanks for your comments and code samples M, really appreciated that.

    I will update the code examples as suggested. Concerning loops i will show those forms that you are proposing in future posts.

    @ JodaStephen,

    Thanks for the comment. I will update that as well.

    ReplyDelete