classtest.frink

View or download classtest.frink in plain text format


// This class demonstrates object-oriented programming in Frink.

// Classes are defined using the "class" keyword followed by the name of
// the class, and then the variables and methods of the class defined within
// curly braces.
class Sphere
{
   // Define a class-level variable which is shared by all instances of the
   // class.
   //
   // This is indicated by the keyword "class var" before the variable
   // definition.
   //
   // (This is not really necessary; Frink defines pi as a global unit by
   //  default; it's merely for illustrative purposes.)
   //
   // You do not need an instance of the class to access this variable.  You
   // can, for instance, write
   //
   //   Sphere.pi
   //
   // anywhere in your code to access
   // this variable.
   //
   // This provides a safe way to make "global" variables.
   //
   // (In C++ and Java, this would be one use of the "static" keyword.)
   class var pi = 3.14159

   // Define a class-level method, indicated by the "class" modifier before
   // the method declaration.  Class-level methods can be called without
   // having an instance of the class.  For example, from anywhere, you can
   // call:
   //
   //    Sphere.getPi[]
   // 
   // Otherwise, the definition of methods
   // in Frink is exactly as the same as defining functions.
   // (In C++ and Java, this would be another use of the "static" keyword.)
   class getPi[] := pi
   
   // Define an instance variable without constraints; requires "var" keyword
   // because otherwise it's not clear that this is a variable declaration.
   // Undefined variables default to the special "undef" value
   var name

   // Define some instance variables with constraints and default values
   // The "var" keyword is optional here because the "is" constraint implies
   // that it's a variable declaration.
   // The constraint will ensure that any value assigned to this variable
   // must have dimensions of mass.
   var mass is mass = 1 ton

   // And another definition.  Note that an initial value is required for
   // this definition and the one above because the constraint ensures that
   // the value *always* has a value with units of mass.
   var radius is length = 1 meter

   // This is just a test for formatters
   // var dummy\u1234 = "\u1235hi"
   
   // Define a constructor, which is indicated by the reserved keyword "new"
   // as the name of the function.  Note that this usage differs from other
   // languages, say C++ or Java, which require the name of the constructor
   // to be the same as the name of the class.  I think that the Java/C++ way
   // violates the programming principle of "specify each piece of data in one
   // place only."
   //
   // Constructors are automatically class-level methods.
   // One or more constructors may be defined in a class.
   // If any constructor is defined, the object must be created with an
   // argument list that matches the formal argument list specified by one
   // of the constructors.
   new[objName] :=
   {
      name = objName
   }

   /* Define another constructor without defaults. */
   new[objName is string, objMass, rad] :=
   {
      name = objName
      mass = objMass
      radius = rad
   }

   // Define a method that takes one argument.  This method just doubles its
   // argument.  Methods look just like function definitions.
   double[y] := y * 2

   // A zero-argument method to calculate the volume of this object.
   // Also note that referring to the class-level variable pi doesn't require
   // use of an object name.
   volume[] := 4/3 pi radius^3

   // Returns the density of this object.
   // This tests calling another method.  Note that the method call doesn't
   // require an explicit object since we're referring to this object.
   density[] := mass / volume[]

   // This demonstrates a multi-line method.
   getName[] :=
   {
      if (name)
         return name
      else
         return "This object has no name."
   }

   // Prints the "this" pointer.
   printThis[] :=
   {
      println[this]
   }

   // Nested call
   nested[] :=
   {
      println["pi on " + getName[] + " is " + getPi[]]
   }

   // Nested call
   nested2[] :=
   {
      println["pi on " + name + " is " + pi]
   }

   // Try to make a unit reference sphere
   class var unitSphere = new Sphere["unit"]
   class var doubleUnitSphere = new Sphere["doubleUnit", 2 tons, 2 m]
}


// The class definition is complete.  The following contains uses of the class.

// Test implicit class-level construction first.  Note that we haven't
// construced an instance of the class, and don't need to to access the
// class-level methods

// Call a class-level method.  Class-level methods are called using the
// class name followed by a period and the method call (which looks just like
// a function call)
println["The universal value of pi, by the function Sphere.getPi[] is: " + Sphere.getPi[]]

// Get the value of a class-level variable.
// Class-level variables are accessed using the name of the class, followed
// by a period, and then the name of the variable.
println["Sphere.pi is " + Sphere.pi]
println[]

// Constraint test.  Declare a variable called "a" which is an instance
// of Sphere and create a new instance.
// New instances are created using the new keyword, followed by the
// name of the class.
a is Sphere = new Sphere["Earth", earthmass, earthradius]

// Access some fields of the a object.
println[a]
println["The name of object a is: " + a.getName[]]
println["Mass is " + a.mass]
println["Radius is " + a.radius]

// Call some methods on the a object.
println["The name of object a is: " + a.getName[] ]
println[a.double[1000] + " should be 2000"]
println["Density is " + (a.density[] -> "g/cm^3") ]
println["Volume is " + a.volume[]]
println[]

// Create another object called "b"
b = new Sphere["Object b"]

// Show that they both share class-level variables.
println["a.pi is " + a.pi]
println["b.pi is " + b.pi]

// Frink allows you to dump the value of an object.
// Try dumping a whole object
println["\nObject dump of object a: "]
println[a]

// Frink allows you to dump the value of an object.
// Try dumping a whole object
println["\nObject dump of object b: "]
println[b]

// Dump the Metaclass object.  This is a special object that contains the
// class-level variables and methods of the class.
println["Metaclass object dump for Sphere:"]
println[Sphere]

// Try a Metaclass-style construction (which looks like a call to the special
// method "new" on the Metaclass object.)
j = Sphere.new["Jupiter", jupitermass, jupiterradius]
println[j]

// Try a purely string-based construction.  This takes the name of a class
// (which can be specified as a string at runtime) and constructs an instance
// of it.
mars = "Sphere".new["Mars", marsmass, marsradius]
println[mars]

mars.printThis[]

mars.nested[]
mars.nested2[]

println["\nClass methods on Sphere are:"]
println[sort[methods[Sphere]]]

println["\n\nMethods on Jupiter object are:"]
println[sort[methods[j]]]

// Attempt class deep copy
j2 = deepCopy[j]
j2.name = "Jupiter 2"
j2.mass = earthmass
println["j2 is:"]
println[j2]
//println[sort[methods[j2]]]

println["jupiter is:"]
println[j]

println["j2.this is " + j2.this]
println["j.this is " + j.this]


View or download classtest.frink in plain text format


This is a program written in the programming language Frink.
For more information, view the Frink Documentation or see More Sample Frink Programs.

Alan Eliasen was born 17449 days, 1 hours, 9 minutes ago.