The introduction to Lasso 9 brings in some new functionality, as well as an increased exposure to object-oriented programming (OOP). Lasso in an object-oriented language, however before version 9, it was generally received as a language that could perform either procedurally, or had the tools to get into OOP. This article is meant to clarify how OOP works, how Lasso developers have been using OOP techniques without knowing them, and clarify some of the terminology.
Parentheses syntax will be used for code demonstration.
It can be assumed that every Lasso developer has used the Lasso language in order to provide a way of getting information into a database. A table might be to hold data on cars, books, recipes, etc. OOP looks at these items as definable entities and strives to encapsulate management of these items through using variables and handlers that modify these objects. When we approach an application using OOP, we spend time defining such objects and give them the tools they need to survive on their own.
OOP is easy once you get how it works. The toughest part about OOP is understanding how it works, and keeping your head around those concepts. OOP is the art of defining our types. Data types are sets of values that we commonly use to store structured information. Examples are integers, strings, arrays, and maps. These are objects that are already defined within Lasso, so we don't need to provide a define_type
in order to use these. We use define_type
in order to define objects that we want to work with.
A Lasso type is what's known as a class in OOP. When we define types, we are providing an object's class definition. As an example, we define a "user". A user has information associated with it such as username, password, address, city, country, postal code, email, phone, etc. Those aspects of a user are known as properties or attributes. Every time we make a "user" object, there are internal places inside that are available to hold such items. Every user has a username, a password, etc.
define_type('user');
// define properties
local(
'id' = string,
'username' = string,
'password' = string,
'addresss' = string,
'country' = string,
'postalcode' = string,
'email' = string,
'phone' = string,
'fave_colour' = string
);
// define methods
define_tag('set_colour', -opt = 'colour');
if(#colour != '');
self->'fave_colour' = #colour;
else;
self->'fave_colour' = 'red';
/if;
/define_tag;
define_tag('set_pass', -req = 'pword');
if(#pword->length < 8);
return('password too short');
else;
self->'password' = #pword;
/if;
/define_tag;
define_tag('show_country');
if(self->'country' != '');
return(self->'country' + ' is where I am from, proud of it');
else;
return('I have no idea where I am from');
/if;
/define_tag;
define_tag('DBupdate');
inline($dbconn, -table = 'users', -SQL = "UPDATE users SET
address = self->'address',
country = self->'country',
postalcode = self->'postalcode',
email = self->'email',
phone = self->'phone',
fave_colour = self->'fave_colour'
WHERE id = self->'id')
error_code ? return('Uhoh, something wrong writing to database: ' + error_msg);
/inline
/define_tag;
/define_type;
// Application methods used for the 'users' class:
var('user1' = user); // A
var('user2' = user(-country = 'Canada')); // B
$user1->set_colour(-colour = 'blue'); // C
$user2->set_pass(-pword = 'moosehair'); // D
$user1->show_country; // E
$user2->show_country; // F
$user1->DBupdate; // G
First we define the type, which is our user class. The properties are defined. Then the methods. The set_colour
method is a tag that can only be used in the user type, and it sets the fave_colour
property to the 'colour' parameter used with the method. Self
is a tag that refers to the current instance of this type. More on that later. The set_pass
method is a tool to change the password. The show_country
method is to grab the country property and to display it to the user accordingly.
Part A above is where we make a user. We shove it into the $user1
variable. It's just like making any other data type, like a string or integer, but in this case we have a user, with all properties completely empty. Please note, there are ways of automatically setting properties during type instantiation. As well, there are special member tags (methods) that are private to the type which perform specific tasks. onCreate
is a tag which gets run automatically when a class is instantiated.
Part B is another variable of the user type, but this time a country parameter was provided. Any property that is supplied automatically goes into the property
local. So $user2
's country is already set to Canada.
Part C says "set the colour of $user1
to 'blue'". It uses the set_colour
method, throws the 'colour' parameter, which gets shoved into that property. This is called a "setter". While we can change properties directly, it isn't proper practice, and setters should be written. Changing and retrieving property values should always be done through member tags. In this way, you can control what properties you have "permission" to view or modify.
Part D changes the password for $user2
.
Parts E and F call the show_country
method for each user, and ask to display their countries.
Part G is where the current object is written to the database. Similar methods can be written to populate an instance of an object with fields from a record, however you will need to somehow provide which record you need. Usually this is a keyfield. A proper test for this type of updating function would mean to search the database to see if the record is already present. If not, to create the record by using an INSERT SQL command, then grabbing the keyfield value for the id attribute. Methods for INSERT, UPDATE, SELECT and DELETE will often be common in your classes.
As you can see, the direct encapsulated design of the type definition, including properties and methods (member tags) means it's all "black box" functionality. Parameters in, response back. This in turn allows the application to be much shorter and much more direct than in traditional inline procedural coding. This allows your design to get much quicker and more convenient. You will find that polishing your types is something you need to do, and you spend more time in defining types rather than in your applications. But there are some serious benefits in approaching design in this matter, because it forces you to respect your type definitions and their functionality.
As the code demonstrates, two objects were made. They were both of the "user" class. Making objects is commonly labelled the "cookie cutter" process. Every cookie cutter has a different shape, with the overall area and shape being the attributtes of the cookies that are made by each. One is shaped like a star, another shaped like an apple, another shaped like a moose. They are all different classes. When we use them on cookie dough, the actual cookies that are made, come out in the shape of the definition. The cookies are objects. I can say "the third cookie I made is a star, the first cookie I made is a moose".
An object is an instance of a class. Every object that is created has the ability to look at itself through methods. In order to perform functions on that instance, a method uses the term "self". self->change_colour('red')
is an implementation upon the current instance, which is parked inside a variable. It says "change MY colour to red". It is relevant only in the scope of the single instance in which we are speaking. Any call for a method comes from the reference to an instantiation, or from the developer's point of view (outside the type definition), an object.
In Lasso 9, we can use the decimal to represent "this": .'colour' = red
The problem that sits in front of us at the introduction of Lasso 9 is that we have been using member tags for so long that we might see them as being part of Lasso, instead of OOP. The following code is essentially a call on a string type member tag (method):
var('myVar') = 'Amsterdam'; // type instantiation
$myVar->length; // => for 9
The second line is calling a method which counts the characters in the $myVar. $myVar is obviously a string type, and in the type definition (define_type('string')) there is a method (member tag) called "length". I am guessing it's just a small function that splits up the property where the value is kept into an array, then it returns the size of the array. Not complicated, but highly useful.
When we build custom tags openly, we are essentially taking previously defined data types, and implementing them through an external method. We think it's just a custom tag, but we always have to deal with a previously defined data type.
Lasso name | OOP name |
---|---|
member tag | method |
property | property/attribute |
type | class (a class is an object template, or object definition) |
single instance of a class | object |
making an object | object instantiation (the reference to the object is THIS instance) |
I am going to walk through an example that is available on http://www.tagswap.net/twitter/. Twitter has an API which Jason Huck has taken advantage of. Essentially you can construct urls to get feedback from the Twitter API, and you have to submit parameters so that Twitter knows what you are asking about. Jason has parked all of this functionality into a type which demonstrates an object, its associated properties, and a slew of methods. In the end, it's a slick set of tricks that are very well packaged, that demonstrate how all of this can be managed.
var('t1') = twitter(-username = 'StevePiercy', -password = 'moosehair');
// $t1 is your twitter type instance, which is an object
$t1->public_timeline->first;
// this shows the first item from the public timeline of Steve's account
$t1->update('Duke takes me for a walk');
// this is how he published to the world that his dog Duke owns this guy right now
As you can see, once the type is tested, it is highly reliable and the syntax to implement the functionality associated with any type you make, is really small and precise. This is one of the benefits of OOP, and it's called encapsulation. You rely on storeys or layers of tested, segregated code.
Reusable types, like this Twitter type, can be shoved into any application by including the type definition (class) somehow. You can park all of your definitions into a folder and call them as needed. When developing, methods are called to do anything, and are quite often done using a single line representing the method name, and some named parameters.
Some methods can be designed to implement functionality that's completely relevant to the developer in order to troubleshoot. The method on line 533 is what Jason has implemented to present feedback from Twitter.
A good book I read to get me into OOP is The Object-Oriented Thought Process, Third Edition, by Matt Weisfeld (Addison-Wesley).
Other Relevant Tagswap References:
Knop is a Lasso framework that uses OOP:
Author: Richard Fortnum
Created: 18 Nov 2009
Last Modified: 16 Mar 2011
©LassoSoft Inc 2015 | Web Development by Treefrog Inc | Privacy | Legal terms and Shipping | Contact LassoSoft