Download or view 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)]
Download or view 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 20167 days, 3 hours, 0 minutes ago.