Other Dino Scripts

Scott Waldron Updated by Scott Waldron

Dino is a “script-oriented” language. What that means is that you can import scripts into the current script, either as top-level variables and functions, or as an object-like construct using an alias. You bring other scripts into the current script using the import script statement.

// base.dino

var publicVar = 0;
private var privateVar = 0;

def baseFunc(a, b) {
privateVar += a * b;
}

private def privateFunc() {
publicVar = 10;
}
// importing.dino

import script 'base.dino' as base;

def func() {
base.baseFunc(10, 10);
base.publicVar = 20;
// base.privateVar = 10; // error
}
// toplevel.dino

import script 'importing.dino' as imp;

imp.func();
// base.baseFunc(10, 10); // error, base was imported privately

Scripts can be imported with the public keyword. By default, base scripts are imported as private symbols into the current script, meaning that they will be available in the current script, but not to any subsequent scripts that import the current script. By placing the public keyword on the import statement, you make it available to other scripts that import the current script. It takes three levels of import to demonstrate this concept.

// base2.dino

def baseFunc() {
}
// importing2.dino

public import script 'base2.dino' as base;
// toplevel2.dino

import script 'importing2.dino' as imp;

base.baseFunc(); // ok, base was imported public into importing2

Imports are private by default because as a best practice, you should only be using symbols from the script that was directly imported. But the functionality is there if it is needed.

Imported Scripts as Classes

When you import a script in the normal manner (with an alias) as shown above, the script behaves much like a static class in classical object-oriented programming (OOP). Consider the following C# OOP static class:

public static class Foo
{
public static void Bar()
{
}
}

To call the member Bar on this static class, you would just write . There is no need to create instances to use the methods defined. Now here is a C# standard class:

public class AnotherFoo
{
public void BarThis()
{
}
}

Here we have a standard class. To call the BarThis method, we must create an instance of this class and call it as follows:

var af = new AnotherFoo();
af.BarThis();

While Dino is not classic OOP, it does its best as a script-oriented language to give you the same semantics. The class keyword is used in Dino as well, but in a different manner: rather than naming the code that contains the methods, Dino applies “class semantics” at import time.

// foo.dino
def bar() {
}
// another script
import script 'foo.dino' as class Foo; // note extra keyword!
var f = Foo(); // create an 'instance' of the foo.dino script
f.bar();
//Foo.bar(); // error, unless the extend keyword is added

Now let’s look once again at the “normal” aliased method. Hopefully it will become clear that the class keyword causes Dino to behave like an OOP system!

// foo.dino
def bar() {
}
// another script
import script 'foo.dino' as Foo; // note – no class keyword!
Foo.bar(); // without the class on import, behaves like a static class.
//f = Foo(); // error!

This is not simply “syntactic sugar” either – behind the scenes Dino treats these differently. You really do get a “new Foo” when you use the class keyword and then create instances with – complete with its own scope and everything.

This turns out to be very powerful, because you can “compose” new classes at runtime using the “base classes” (other scripts) combined in different ways:

// base.dino
def inBase() {
}
// derived.dino
import script 'base.dino'; // note – direct import
def inDerived() {
}
// another script
import script 'derived.dino' as class Derived;
d = Derived();
d.inBase(); // ok
d.inDerived(); // ok
// other.dino
import script 'derived.dino'; // note – direct import
def inOther() {
}

You may be wondering “how do I override the base method? And is there any way to keep the functionality of the base method after overriding?” The answer is yes on both counts:

// base.dino
def doBase() {
return 10;
}
// derived.dino – make some new members – others from Base are invisible
import script 'base.dino' as class Base;

private var base = Base(); // create an 'instance' of the base class as a private var

def doDerived() {
return 20;
}

def doBase() {
return base.doBase() + 15; // call your private 'instance'
}
// derived.dino – override only, no base class access
// note no 'class' or alias name!

import script 'base.dino';

def doDerived() {
return 20;
}

def doBase() {
return 40;
}
// derived.dino – override + continued access to base functionality
// note we add the 'extend' keyword!

import script 'base.dino' as extend class Base;

private var base = Base(); // create an 'instance' of the base class as a private var

def doDerived() {
return 20;
}

def doBase() {
return base.doBase() + 15; // call your private 'instance'
}

Note that Dino only considers the name of a function, not the signature, when overriding. So, you can override functions with extra parameters, fewer parameters, etc.

Importing Scripts in a Nutshell

A table with the four different types of script imports.

Signature

Purpose

import script 's';

Imports the other script into this one, exposing its public variables, types, and functions as if they belonged directly to this script. This is a good strategy for “class-like” scripts when you want to extend or replace the base script functions, but you don’t need to create instances of the base script.

// base
def foo() {
return 1;
}
def bar() {
return 2;
}
// other
import script 'base';
var f = foo(); // 1

import script 's' as S;

Use this when you want to import a script and call its public functions and variables, but the other script’s functions should only be called with a name qualifier, or alias. The accessibility of the other script’s members is the same as the basic import, but you must qualify the members with the alias.

The alias in this case acts as a static class.

// base
def foo() {
return 1;
}
def bar() {
return 2;
}
// other
import script 'base' as Base;
var f = Base.foo(); // 1

import script 's' as class S

Use this when you want to import the base script and be able to create instances of it, which are compiled on demand when you ask for a new instance.

The alias in this case acts as a class constructor, and you must call it as such, that is, as if calling a function (with parentheses).

// base
def foo() {
return 1;
}
def bar() {
return 2;
}
// other
import script 'base' as class Base;
var b = Base();
var f = b.foo(); // 1

import script 's' as extend class S

Use this when you want to import a script which is intended as a base class for this script, and you also want to be able to create instances of the base class. This import statement is a combination of #1 and #3.

This statement will “forward through” the public members that are defined on the base class, if this script is imported into yet another script.

Note that in “derived”, defined below, we will have a “class symbol” called Base, which we can create instances from. However, we will also have direct access to the functions defined in “base”, because its public symbols will also be directly imported into “derived”.

// base
def foo() {
return 1;
}
def bar() {
return 2;
}
// derived
import script 'base' as extend class Base;
var base = Base();

def rex() {
return 5;
}
// override foo, change signature...
def foo(num) {
return num + base.foo();
}
// we have not overridden the bar() method of base,
// and we have direct access to its static function
// without name qualification
def staticBar() {
// 'bar()' here is in the static symbol
return bar() + base.bar(); // 4
}
// other
import script 'derived' as class Derived;
var d = Derived();
var f = d.foo(8); // 9, overridden in derived
var b = d.bar(); // 2, forwarded through from base
var r = d.rex(); // 5, newly defined in derived
var sb = d.staticBar(); // 4

How Dino Finds Scripts for Import

We'll be expanding the documentation in this section soon. Please contact us if you have questions.

How did we do?

Aliasing

Contact