Table of Contents
There would be no object-oriented language without constructors and
naturally MN8 has them. The purpose of the constructors is quite obvious,
empty concepts are not very useful and using the same sequences to fill an
concept instance with data every time we want to use it is a waste of time.
In order to prevent this waste of time MN8 has constructors that have the
sole purpose to initialize concepts with diverse values at
instantiation.
All constructors are in fact regular methods with two restrictions: the
name of the method is create and have no return type.
In case a concept has no constructor(s) a default one is always created
by the MN8 interpreter, one with no parameters because at every typeof the
default constructor is called.
Let's see an example:
#---- Person.mn8 ----
define Person [
@firstName
@lastName
address
: create ( $firstName typeof String, $lastName typeof String, $address typeof String ) [
@firstName = $firstName
@lastName = $lastName
/address/ = $address
]
static : main ($args typeof Series) [
$me = Person.create( "Remus", "Pereni", "Some address!")
Error.println ($me.getConceptType)
print $me.toXML
]
]
|
|
By executing the Person concept will get:
Person
<Person lastName="Pereni" firstName="Remus">
<address>Some address!</address>
</Person>
|
|
There is only one weird thing in the above example, that Error.print
line. Both Windows (2000, XP) and Unix flavors have two output streams an
out and an err, this streams can be separated and redirected each into
diverse devices. We used both streams in the example so we don't mix the
XML output with a string, which would make any redirection of the result
invalid XML.
We could redirect the two outputs as follows:
mn8 Person.mn8 1> res.txt 2>res.err
|
|
Which means that the first stream (which is the output stream) goes to
res.txt file and the second stream (the error stream) goes to the res.err
file.
We can have any number of constructors we like with the only restriction
that should be only one constructor with the same signature. The signature
of a method is a string containing the name of the method followed by the
types of all it's parameters. The signature of the above constructor would
be: create:String,String,String
. We can use the signature also for
retrieving the methods or constructors of a concept using reflection. To get
the above constructor we only have to do:
$me.getConceptConstructor("create:String,String,String")
If we want that a piece of code or initialization to happened any time a
variable is instantiated from that type using TYPEOF we only have to
override the default create constructor:
#---- Person.mn8 ----
define Person [
@firstName
@lastName
address
: create [
/address/ = "not disclosed"
Error.println(" Hey I'm in the default constructor of: " + .getConceptType)
]
static : main ( $args typeof Series ) [
$me typeof Person
print $me.toXML
]
]
|
|
with the obvious result of:
Hey I'm in the default constructor of: Person
<Person lastName="" firstName="">
<address>not disclosed</address>
</Person>
|
|
If we would have to call another constructor in the parent class, one
with parameters (like the one we declared earlier which accepts a first
name, last name and address we could do that by calling instead of
super.create
, super.create($fName, $lName, "not disclosed")
.
Most MN8 concepts already have their most important operators
implemented, operators like +, -, ==, <, > for concepts like Integer and
String, still there will be cases where it will make a lot of sense to
implement divers operators for many different custom concept types.
The operations you can define or/and override are: +, ++, +=, -, --
, -=, *, *=, /, /=, ==, !=, !, <, >, >=, <=
.
Let's say we have a person concept that can have multiple emails. In case
we make the email information another concept then we can define the person
+ email operation as an easy way to add emails to a person concept. This
would look pretty much as follows:
#---- EmailAddress.mn8 ----
define EmailAddress [
@type
@address
]
#---- Person.mn8 ----
define Person [
@firstName
@lastName
emailAddresses [
email* typeof EmailAddress
]
:+ ( $newAddress typeof EmailAddress) [
/emailAddresses/email.createNewEntry
/emailAddresses/email.lastEntry@type = $newAddress@type
/emailAddresses/email.lastEntry@address = $newAddress@address
return this
] typeof Person
static : main ( $args typeof Series ) [
$me typeof Person
$mail1 typeof EmailAddress
$mail1@type = "primary"
$mail1@address = "remus@nolimits.ro"
$mail2 typeof EmailAddress
$mail2@type = "yahoo"
$mail2@address = "rpereni@yahoo.com"
$me = $me + $mail1
$me = $me + $mail2
print $me.toXML
]
]
|
|
The result of the execution of the Person concept will be:
<Person lastName="" firstName="">
<emailAddresses>
<email address="remus@nolimits.ro" type="primary"></email>
<email address="rpereni@yahoo.com" type="yahoo"></email>
</emailAddresses>
</Person>
|
|
Overriding or declaring operators for new types are also very useful for
example in declaring special behaviors, for instance, in determining
equality. Let's say we have the good old person concept and we don't like
the way MN8 does compare two instances of a person concept. We would like
that two person instances to be considered as the same if the first and last
name, of the two person instances, are the same regardless of the other
person related information, like email address.
#---- Person.mn8 ----
define Person [
@firstName
@lastName
email
# simple constructor to help us initialize the concept instances
: create ( $fName typeof String, $lName typeof String, $email typeof String ) [
@firstName = $fName
@lastName = $lName
/email/ = $email
]
# we override the default equality operator
: == ( $person typeof Person ) [
if @firstName.toLowerCase == $person@firstName.toLowerCase and \
@lastName.toLowerCase == $person@lastName.toLowerCase then [
return true
]
return false
] typeof Logical
static : main ( $args typeof Series ) [
$me = Person.create( "Remus", "Pereni", "remus@nolimits.ro")
$stillMe = Person.create("remus", "pereni", "rpereni@yahoo.com")
$sorin = Person.create("Sorin", "Iluti", "sorin@nolimits.ro")
print $me == $stillMe
print $me == $sorin
]
]
|
|
By executing this concept we will get as result:
If you look in the equal operator overloading method, at the first if the
line ends after the and with an backslash (\), because end of lines are
important, when we have a long line and don't want to get to far to the
right we can use the backslash operator to signalize to the MN8 interpreter
that the next line is a continuation of the current line.
The synopsis of an MN8 method is:
(STATIC)? : method_name (( $param1Name TYPEOF Param1Type, $param2Name TYPEOF Param2Type, … ) )? [ eol
## method body
] (TYPEOF ReturnType)? eol
|
|
Really simple, isn't it?
To clear any confusion let's see another Person example in which we use some methods.
#---- Person.mn8 ----
define Person [
@firstName
@lastName
birthDates [
@birthYear typeof Integer
@birthMonth typeof Integer
@birthDay
]
: getAge [
return Integer.create(Date.getToday.getDate("yyyy")) - /birthDates@birthYear
] typeof Integer
static : homePageLastUpdated( $url typeof String, $justForFun typeof String) [
$page from $url
if $page.getHeaders.containsKey("Date") then [
print "Home page last updated: " + $page.getHeaders.getValue("Date")
]
]
static : main ($args typeof Series) [
$me typeof Person
$me/birthDates@birthYear = 1974
print "Age: " + $me.getAge
Person.homePageLastUpdated("http://neuro.nolimits.ro", "xxx")
]
]
|
|
Executing the above concept you we'll get probably a completely different
result that the one I've got and presented now, but still I think the
example made the point.
The result:
Age: 28
Home page last updated: Tue, 22 Oct 2002 21:23:26 GMT
|
|
In the above example you could see two methods getAge
and
homePageLastUpdated
, one static the other not, one returning a value and
finally one with parameters and the other without.