np is different
Duck + Objects - Classes
Enter and execute code right from the convenience of your own browser. Click on the blue arrows to run any piece of example code.
Lists
Lists are containers for anything in np, and you can stuff things into them sequentially (like an array)
⇥new myList = (: #Fluffy #Scruffy #Ginger ) print( myList )
will print out:
(: "Fluffy" "Scruffy" "Ginger" )
or by key (like a hash map):
⇥new myList = (: contents = #cats ) print( myList )
(: contents="cats" )
or you can mix them:
⇥new catCollection = (: contents = #cats #Fluffy #Scruffy #Ginger ) print( catCollection )
(: "Fluffy" "Scruffy" "Ginger" contents="cats" )
You can get data out of lists in several ways.
⇥-- accessing named entries with dot notation: print( 'This contains mostly:' catCollection.contents ) -- accessing named entries with bracket notation print( "Yes, it's full of" catCollection[#contents] ) -- accessing list items with bracket notation print( 'First cat:' catCollection[1] )
This contains mostly: cats Yes, it's full of cats First cat: Fluffy
But one of the most important ways to use list items is to iterate over them:
⇥catCollection:items { catName | print( '*' catName ) }
* Fluffy * Scruffy * Ginger
So what's going on there? Like all lists, catCollection
has a built-in behavior called items
, which takes a function
as an argument. In this example, the function gets called once for ever
sequential list item and outputs that item.
Lists come with all sorts of pre-defined behaviors, like for example
the join
function:
print( catCollection:join(' and ') );
Fluffy and Scruffy and Ginger
Separation of Payload and Behavior
Most object-oriented languages lump methods and fields together in
an object. This can create problems if the two conflict. In np, everything
you can stuff inside a list is considered to be data. For example, you
could very well have a named entry called "each" without overwriting the
each
behavior.
This works in part because the behavior operator :
is different from
the operators you use to access the data contained in an object.
catCollection.each = 'furry' catCollection:each(print)
Fluffy 1 1 Scruffy 2 2 Ginger 3 3 cats contents 4 furry each 5
By the way, note how the example above shows that the each
behavior
calls the receiving function print
with 3 parameters: the list entry,
its key, and an item counter - and standard print
dutifully prints them
all in a row.
New Behavior
So how can we teach objects new behaviors? Well, deep down behaviors are stored
as named entries in lists, too. For example, let's give catCollection a meow
behavior:
catCollection:bind(: meow = { cats | print( cats:size() 'cats meow.' ) } ) -- let's call meow: catCollection:meow()
3 cats meow.
This demonstrates how the catCollection object has no formal class, but
instead responds to events that are defined in its bind
list.
By calling bind() without parameters, we can get that list of behaviors
for any object:
print( list.keys(catCollection:bind()) )
(: "sort" "maxIndex" "join" "pop" "expand" "reduce" "map" "keys" "create" "copy" "clear" "iReverse" "size" "containsKeys" "keyCount" "toString" "get" "iSort" "iConcat" "concat" "find" "max" "insert" "items" "add" "iAdd" "bind" "min" "can" "condense" "implements" "reverse" "each" "set" )
You might notice something particular here. The explicit use of the
library function list.keys() should raise eyebrows.
Why can't we do catCollection:bind():keys()
instead?
Because objects can also have no defined behaviors at all, such
is the case with the built-in behaviors for example.
If you call myObject:bind(: )
on something, you too can
strip it completely naked.
Mixing Instead of Inheriting
Since there is no class or prototype system in np, there is no inheritance. Nevertheless, np has some powerful mechanisms that allow you to manipulate object capabilities in interesting ways.
For example, you can add two event lists together to give an object the capabilities of both:
⇥catCollection:bind( list << (: meow = { cats | print( cats:size() 'cats meow.' ) } )) -- it now responds to meow() catCollection:meow() -- and to items() catCollection:items(print)
In this example, the catCollection object gets a new event list
comprised of the combined list
library and another
list that contains the meow
behavior.
You can check whether an object will respond to a given
event by using the can
behavior:
if( catCollection:can(#hazCheeseburger) ) print( 'We can haz!' ) else print( 'Out of cheeseburgers!' )