Wyvern Guide
Wyvern Guide
require stdout
1
stdout.print("Hello, World!")
This program already illustrates a couple of basic aspects of Wyvern. First,
Wyvern is object-oriented: stdout is an object, and we are invoking the print
method on it. For expressions, much of Wyvern's syntax is similar to Java's.
Second, system resources such as the standard output object, stdout, are
not ambiently available to programs, but must be explicitly required from the
operating system. A primary goal of Wyvern's module system is helping de-
velopers to reason about the use of resources. Thus even a simple script such
as Hello World must declare the resources it requires in order to execute. This
allows engineers to determine at a glance what kind of I/O a program might do,
and provides a basis for making a decision about whether to run this program
in a particular situation. In this case, even without looking at the actual code,
we know that this program may write to the standard output stream, but will
not access the le system or access the network.
3. Anonymous Functions
Wyvern has good support for functional programming, and anonymous functions
can be dened in Wyvern using the syntax:
(x:Int) => x + 1
We can bind the expresison above to a variable and invoke it:
(x:Int,y:Int) => x + y
or no parameters:
() => 7
Function types can be denoted with an arrow, and we can annotate a variable
with this type. If we annotate the type of the variable we are binding to the
function, we can leave out the type annotation (and even the parentheses) on
the function's argument:
2
Function types with multiple arguments can use * to separate them; e.g.
Int * Int -> Int is a function that takes two integers and returns an integer.
4. Functions in Wyvern
def factorial(n:Int):Int
(n < 2).ifTrue(
() => 1,
() => n * factorial(n-1)
)
stdout.print("factorial(15) = ")
stdout.printInt(factorial(15))
A function is dened with the def keyword, and its argument and return types
are given in Algol-like syntax. def are recursive, so
Functions dened with
we can callfactorial in the body. The example illustrates how an integer
comparison n-2 is a boolean object, on which we can invoke the ifTrue method.
This method takes two functions, one of which is evaluated in the true case and
one of which is evaluated in the false case.
Note that factorial(15) would overow in languages such as Java in which
Int means
the default integer types is represented using only 32 bits. In Wyvern,
an arbitrary precision integer.
Wyvern provides two nicer ways to write the if statement above:
def fact(n:Int):Int
if (n < 2)
1
else
n * fact(n-1)
def fact2(n:Int):Int
if (n < 2) { 1 } else { n * fact2(n-1) }
In the rst case, we put the then computation on a new, indented line, followed
by else (also indented, but not as much), and then the else computation. In
the second case, we put it all on one line of code, but put the then and else
computations in curly braces to set them o.
It turns out that both versions of the if function can be user-dened. In
the rst case, Wyvern takes all the indented code and passes it as an argument
to the if function. In the second case, Wyvern passes the code in each set of
curly braces as a separate argument to the if...else... function.
If you'd like to see how these if functions are actually user-dened, look in
3
the Wyvern standard prelude (stdlib/prelude.wyv):
type IntList
def sum():Int
The type keyword declares a new object type, called IntList in this case. The
public methods available in the type are listed below, but no method bodies
may be given as we are dening a type, not an implementation.
We can implement a constant representing the empty list and a constructor
for creating a larger list out of a smaller one as follows:
cons(3,cons(4,empty)).sum() // evalutes to 7
The new expression creates an object with the methods given. In the example
above, we just have one method, sum(), which evaluates to 0 in the case of the
empty list and sums up the integers in the list otherwise.
String literals can be written in quotes, using the same escapes as in Java.
Strings support several operations, including ==, <, >, length(), and charAt(Int).
The last of these returns a Character, which supports ==, <, and > operations. A
simple program illustrating these is in examples/introductory/strings.wyv
Wyvern will support character literals but doesn't yet.
4
7. Anonymous Functions as Objects
new
def apply(x:Int):Int = x + 1
which is an instance of the following type:
type IntToIntFn
def apply(x:Int):Int
As mentioned earlier, the type above can be abbreviated Int -> Int, as in
many other languages with good support for functional programming.
Types with mutable state can be dened, but need to be marked as resource
types (examples/introductory/cell.wyv):
val c = makeCell(5)
c.get() // evalutes to 5
c.set(3)
c.get() // evalutes to 3
Here makeCell uses a new statement to create an object with a var eld value.
var elds are assignable, so the set funtion is implemented to assign the value
eld of the receiver object this to the passed-in argument. Note that we must
initialize a var eld with an initial value. If we had not declared Cell to be
a resource type, we would get an error because the new expression creates a
stateful object that is a resource.
In the example above, Unit is used as the return type of functions that do
not return any interesting value.
For function types, -> indicates a resource type (i.e. some state is captured
by the function) whereas => indicates non-resource type.
5
9. Modules
Here the import statement takes a fully qualied name and uses this to nd the
le dening module cell. The module is actually an object that gets bound
to the name cell. We can invoke make() on cell just as if it were a method.
Types such as Cell dened in the cell module can be referred to by their
qualied names, i.e. cell.Cell. In fact, types can be dened as members of
an object as well, and the same qualied syntax can be used to refer to them.
So modules are not special semantically: they are just a convenient syntax for
dening an object. Consider what is the type of cell? The answer could be
written as follows:
type TCell
resource type Cell
def set(newValue:Int):Unit
def get():Int
def make(initVal:Int):this.Cell
Wyvern les that dene a type use a .wyt extension (for Wyvern Type), and
6
you can nd the above denition at examples/modules/TCell.wyt.
Regular modules can't have var declarations, but functors (below) can.
10. Functors
Although modules are objects, they cannot contain any state and they cannot
encapsulate system resources. How can we express designs similar to those in
other languages, in which modules do these things? We can do so by dening
functors . A functor is a module that is dened as a function: it takes zero or
more arguments and returns an object. For example, we can dene a functor
that returns a stateful Cell object (examples/modules/cellAsModule.wyv):
val m1 = cellAsModule()
val m2 = cellAsModule()
m1.set(1)
m2.set(2)
m1.get() // evalutes to 1
m2.get() // evalutes to 2
In this example you can see that we have called the functor cellAsModule twice,
and each resulting object instance has its own internal state.
Note that in the example above, we cannot access the value declaration
from outside the module. Wyvern hides all var declarations from clients. If you
want to access state, provide getters and setters, as suggested in the example.
7
11. Module Parameters
def addOne():Unit
cell.set(cell.get()+1)
import myPackage.cellAsModule
import myPackage.cellClientFunctor
13. To Add
Datatypes
8
Note that these can't have methods, nor elds other than those in the
constructors
Programs are made up of four kinds of core declarations: val, var, def, and
type, as well as expressions. The val and var declarations and the expressions
in a program are evaluated in sequence, and the variables dened earlier in the
sequence are in scope in later declarations and expressions. In contrast, def and
type declarations do not evaluate, and therefore these declaration forms can be
safely used to dene mutually recursive functions and types. Each sequence of
declarations that consists exclusively of def and type is therefore treated as a
mutually recursive block, so that the denition or type dened in each of the
declarations is in scope in all the other declarations.
To understand why we allow recursive def and type declarations but do not
allow this for val and var declarations, consider the following example:
bar.get()
When we try to initialize the bar value, we call foo(), which in turn invokes
baz(). However, baz() reads the bar variable, which is what we are dening, so
there is no well-dened result. Languages such as Java handle this by initializing
bar to null at rst and then writing a permanent value to it after the initializer
executes. However, in order to avoid null pointer errors, Wyvern does not
allow null as a value. Languages such as Haskell would use a special black
hole value and signal a run-time error if the black hole is ever used, as in the
bar.get statement at the end. We avoid this semantics as it adds complexity
and means the program can fail at run time. Of course, innite loops can
still exist in Wyvern, but they come from recursive functions, never recursively
dened values.
The Wyvern standard library les are in subdirectories ofstdlib. For example,
earlier we used theIfTSL library, dened in stdlib/wyvern/IfTSL.wyv. When
importing les from the standard library, leave out stdlib; i.e. in the example
above, just use import wyvern.IfTSL
9
Platform-specic denitions are in the platform stdlib,
subdirectory of
in a sub-subdirectory named after the platform (e.g. java). For example,
stdout is dened in stdlib/platform/java/stdout.wyv for the java plat-
form; there is an analogous denition for the python platform too. The deni-
tion of stdout for java uses some Java helper code dened in the Java class
wyvern.stdlib.support.Stdio. These libraries should be imported without
using stdlib.platform.java.
An example of a utility library that provides a small part of a regular ex-
pression package is in wyvern/util/matching/regex.wyv. The design approx-
imately follows the corresponding Scala library.
An example of a data structure library is wyvern/collections/list.wyv.
Also see wyvern/option.wyv.
All of the above examples are tested by the Wyvern regression test suite that
is run as part of ant test when building Wyvern.
10