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]
println["j == j2:  " + (j == j2)]
s = new set[j, j2, j2]
println[s]

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]

println["j == j2:  " + (j == j2)]



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 17533 days, 13 hours, 16 minutes ago.