Table of Contents

1 Introduction

1.1 Command Line Interpreter

1.2 Hello World

1.3 Main Wafl Concepts

1.4 Introduction to the Types

2 Program Structure

2.1 Program Is an Expression

2.2 Comments

2.3 Tuples

2.4 Local Definitions

2.5 Function Definition

2.6 Named Expression Definition

2.7 No Variables

2.8 The Order of the Definitions

2.9 Conditional Expression if

2.10 Conditional Expression switch

2.11 Recursion

2.12 Libraries

3 Programming With Functions

3.1 Strict Type Checking

3.2 Automatic Type Inference

3.3 Polymorphism

3.4 Higher Order Functions

3.5 Partial Application

3.6 Lambda Functions

3.7 Lambda Closures

3.8 Operators as Functions

3.9 Dot Operator

3.10 Explicit Computation State

3.11 Cached Functions

4 Primitive Types

4.1 Literals

4.2 Operators

4.3 Conversion Functions

4.4 Integer Functions

4.5 Float Functions

4.6 String Functions

5 List Type

5.1 List Literals

5.2 Basic List Functions

5.3 Basic List Processing

5.4 Advanced List Processing

5.5 More List Functions

5.6 Functions map and zip

5.7 Functions foldr, foldl and fold

5.8 Functions filter, find and filterMap

5.9 Functions forall and exists

5.10 Functions count and countRep

5.11 Functions sort and sortBy

5.12 Functions in and contains

5.13 Lazy Lists

6 Structured Types

6.1 Array Type

6.2 Map Type

6.3 Tuple Type

6.4 Record Type

7 Elements of Wafl Library

7.1 Program Control

7.2 File Reading

7.3 File Writing

7.4 File Operations

7.5 Directory Operations

7.6 Regex Functions

7.7 Command Line

7.8 Web and HTTP Functions

7.9 Wafl to JSON

7.10 Wafl Program Evaluation

8 Parallelization

8.1 Wafl and Parallelization

8.2 Parallel Functions

9 Core Library Reference

9.1 Main Library

9.2 Command Line Library

9.3 Web Library

9.4 XML Library

9.5 Drawing Library (SDL)

9.6 Timer Library

9.7 Edlib Library

10 Command Line Reference

10.1 Command Line Options

10.2 Configuration Files

10.3 ANSI Control Codes

11 Advanced

11.1 JIT

11.2 Wafl Binary Libraries

12 More Content…

12.1 Soon…

 

 

Last update: 29.01.2025.

Wafl

Wafl

Tutorial / 1 - Introduction

Open printable version

Wafl Tutorial

Wafl is a strongly typed functional programming language, with implicit type inference. It was originally designed as an experimental application of FP languages in web development. A few years later, however, Wafl became a simple and very fast scripting language, with a simple and efficient interface to databases.

The Wafl Tutorial introduces the language constructions, the use of the command line interpreter, elements of the core library and many examples.

Welcome to Wafl!

Saša Malkov

1 Introduction

This introductory chapter covers only the basic elements of Wafl syntax and elements of using the command line interpreter. The aim is to provide some basic information that will enable us to progress further in the following chapters.

1.1 Command Line Interpreter

For most of this tutorial, we assume that you are using the Wafl command line interpreter. The name of the command line interpreter may vary depending on the version of the installation package. It can be clWafl, clwafl or wafl. In this tutorial we assume that it is called clwafl.

To check if the interpreter is ready, open the OS command shell and type:

clwafl

If everything is set up correctly, the output should look like this:

*** Program filename missing!
*** Wafl Command Line Interpreter usage syntax:
    clwafl [<options>] <program file name> [<arguments>]
    clwafl [<options>] -code <source code> [-args <arguments>]
    clwafl -help

We assume that each program is written and saved in a file and then evaluated with the command line interpreter. To evaluate a program stored in example.wafl, enter and execute:

clwafl example.wafl

Command line interpreter options are discussed in details in Command Line Reference.

To download Wafl, visit Wafl Downloads. The current version is available as a Linux deb package and as a Windows installation program. Each installation package contains an offline version of this tutorial.

1.2 Hello World

The easiest way to calculate the character string "Hello World!" is to write a program that consists only of this character string:

"Hello World!"

Hello World!

If you write this program and save it as a new file with the name “hello.wafl”, you can evaluate it with the Wafl command line interpreter:

clwafl hello.wafl

If everything is set up correctly, the output should look like this:

Hello World!

1.3 Main Wafl Concepts

Wafl is a strongly typed and eager functional programming language with implicit type inference.

Let us discuss the first sentence in detail.

Wafl is a functional programming language. A Wafl program has the syntactic form of an expression. In imperative programming we speak of the execution of a program, but here we speak instead of the calculation or evaluation of a program. The order of program execution is not explicitly determined by the program source file, but by implicitly defined rules for the evaluation of expressions.

Wafl is not a pure functional language. There are some language features that violate the strict rules of the functional paradigm, including the command line interaction subsystem, the use of files and databases, and so on. However, the core of the language is defined in such a way that we can speak of a large pure subset. Most of the examples in this tutorial use only the pure subset of the language. Impure elements are generally limited to some elements of the core library.

There are (almost) no variables, assignments, iterations, procedures and many other elements that are common in imperative programming languages. Even if there are some types of these concepts, they are not present in the usual imperative form.

The order of evaluation is not important in most cases, but where it is important, it can be assumed that all arguments of functions and operators are evaluated from left to right.

Wafl is an eager language. This means, in short, that for most functions (and operators) all arguments are fully evaluated before the function (or operator) is evaluated. There are some common exceptions, such as logical operators and conditional expressions.

However, there are also some lazy elements. The most important lazy subsystem is the processing of database queries. The query results are not retrieved completely before the query results are processed. Instead, the query result is treated as a lazy list of rows. Individual rows of the query result are retrieved as needed.

Wafl is a strongly typed programming language. Each program is completely analyzed and type-checked before evaluation. No type-related errors can occur during program evaluation. However, the Wafl syntax does not contain an explicit type specification. All types are implicitly inferred. Normally, the derived types are not displayed during the evaluation, but if a type error is detected during type checking, the corresponding derived types are reported to support problem localization.

1.4 Introduction to the Types

To understand the notation of function types, we need to familiarize ourselves with the notation of types in general. This requires at least a small introduction to the Wafl type system. For exact types the notation is relatively simple, but for polymorphic types it can be more complex.

The types are never written in Wafl programs. The notation of types is only important because it is used in the interpreter’s type-checking error messages and in the Wafl documentation (including the library reference and this tutorial).

Wafl supports implicit polymorphism. This means that each function has a strictly defined type, but is as general as possible so that it works for all types to which its definition can apply. For example, we can define a function add3:

add3(x,y) = x + y + 3;

This is a definition of the function add3. It is easy to verify that add3 can only work for integer arguments (because no implicit conversions are allowed and we have an explicitly specified integer argument: 3). We can write the type of this function as follows:

(Int * Int -> Int)

Function types are always enclosed in parenthesis. This type indicates that the function has two arguments of type Int and the result of the same type Int.

Let us now define a function add:

add(x,y) = x + y;

The function add can work for all types for which its definition expression can be evaluated correctly. This includes all types for which the binary operator ‘+’ is defined. In Wafl, these are the primitive types: Integer, Bool and String. The function add therefore has a polymorphic type that includes the exact types (Int * Int -> Int), (Float * Float -> Float) and (String * String -> String). To simplify the notation, we use the parameterized notation:

(Value['1] * Value['1] -> Value['1])

The Value represents a particular set of types (Int, Float, String) and Value['1] represents an instance of the set, where '1 is a type parameter (or type variable). Type parameters take the form of a quotation mark followed by an integer. A type parameter can represent any type that fits the context (i.e. any type from the given set), but the same type in the entire type notation. In this example, '1 is one of the types in the set Value - Int, Float or String, but the same in all three occurrences.

So, if we replace '1 with specific types, one by one, then our type (Value['1] * Value['1] -> Value['1]) virtually becomes a set of types that includes: (Int * Int -> Int), (Float * Float -> Float) and (String * String -> String).

Now, we agree that it was almost more complicated to explain this than to use the set of types. But there are sets that are too large to be written without a special polymorphic notation. For example, if we have a polymorphic list of elements, then its type is denoted as List['1], where '1 denotes the element type and can be any type that Wafl supports. Thus, List['1] represents an infinite set of types.

List['1] is an infinite set, at least theoretically. If there is a List[A] for a type A in this set, then there is also a type List[List[A]] in this set, and so on… For every natural number N there is at least one N-deep list, so our set of list types is at least as large as the set of all natural numbers, which is infinite.

Let us write a function that checks if a list is longer than a given number:

isLongerThan( list, n ) = size( list ) > n;

First, we know that size is a core library function that returns Int, so n must also be of type Int. The expression of the function definition evaluates to a Bool value, so we conclude that our function will have a type like (... * Int -> Bool). And what is the type of the first argument?

In our example, we expect a list as the first argument, but there are no any implications for its element type. Then it will be a polymorphic list List['1], and the function type will be: (List['1] * Int -> Bool).

But size works not only for lists, but also for arrays. To consider all sequence types (lists and arrays), the function type must be more general:

(Sequence['2]['1] * Int -> Bool

where '2 stands for a sequence type (array or list) and '1 for a sequence elements type.

In addition, size also works for strings. Strings often behave like sequences of characters (even though there is no new type for characters - String is used), so we have a more general type class SequenceStr that includes arrays, lists and strings:

(SequenceStr['2]['1] * Int -> Bool

If '2 is String, then '1 must of course also be String.

But that’s not all. There is a map type in Wafl, that is a collection of elements accessed via keys. Its type is Map['2]['1], where '1 is the element type and '2 is the key type. And of course the function size also works for maps. So here is the final type of our function isLongerThan:

(Indexable['1]['2]['3] * Int -> Bool)

where '1 is the type of the indexable collection (list, array, map or string), '2 is the type of the keys (for all collections except maps must be Int) and '3 is the type of the elements.

Here is a list of some of the type classes we often use in Wafl:

Type Class / Description

Prime['1]

One of the primitive types: Int, Float, Bool and String

Value['1]

One of the types: Int, Float and String.

Numeric

One of the types Int and Float.

PrimeNotInt['1]

One of the types: Float, Bool and String.

PrimeNotFloat['1]

One of the types: Int, Bool and String.

PrimeNotString['1]

One of the types: Int, Float and Bool.

Sequence['2]['1]

A sequence, where '2 specifies the sequence class (array, list) and '1 the element type.

SequenceStr['2]['1]

A sequence, where '2 specifies the sequence class (array, list or string) and '1 the element type. If '2 is String, then '1 must also be String.

Indexable['3]['2]['1]

An indexable collection, where '3 specifies the collection class (array, list, map or string), '2 the index type, and '1 the element type.

There are a few other types and type classes, but this should be enough to get you started.

Wafl Home            Downloads            Wafl Tutorial v.0.6.8 © 2021-2025 Saša Malkov            Open printable version