3

Elementary Slogan

In this chapter we will closely examine the basic constructs from which programs are built in Slogan. Like any other powerful language, Slogan allows us to combine these basic elements to build complex ones. We will also have a sneak peek at some of the means of abstractions offered by Slogan.1

3.1 Expressions

The preceding chapter showed how to use the REPL to interactively run code. The program fragments that you type in are called expressions. The Slogan interpreter responds by displaying the value of those expressions. One of the most primitive expression is a number. As there is nothing much to be done to a number in isolation, the interpreter will just give it back.


2017
// 2017
      

Numbers can be combined together using the familiar arithmetic operators to produce new values:


2017 + 1
// 2018
1000 - 300 + 3
// 703
1000 - 300 * 3
// 100
4/2
// 2
      

The arithmetic operators follow the commonly understood precedence and associativity rules. Multiplication and division takes precedence over addition and subtraction. The arithmetic operators with the same precedence associate from left to right. Parentheses are used to explicitly override precedence or group parts of an expression that should be evaluated first.


(1000 - 300) * 3
// 2100
      

There are many more operations you can perform on primitive data like numbers. These operations are all defined as functions. A function is usually invoked by calling its name followed by a list of arguments enclosed in parentheses. Examples of some useful numeric functions are given below:


sqrt(1729)
// 41.58124577258358
mod(5, 2)
// 1
abs(-1)
// 1
sin(3.14)
// .0015926529164868282
      

In fact, the arithmetic operators are all functions with special syntactic support from the language. The functions associated with the operators can be accessed by enclosing the operators in tick-quotes (`).


`*`(10, 2)
// 20
`+`(1, 2, 3, 4, 5)
// 15
      

In this section we have introduced two important concepts – expressions and values. A value is the result of an expression. To repeat the point with a simple example, 10 * 2 is not a value because a computation can still be performed on it. The result of that computation – the number 20 – is a value because no more operations can be performed on it.

3.1.1 Remembering Values

If we can associate a name with a value, it will be easier to refer to that value later. The names given to values are called variables. We name things with the let statement.2


let rectangle_width = 23
let rectangle_length = 52
let rectangle_area = rectangle_width * rectangle_length

rectangle_area
// 1196
      

The let statement is Slogan's simplest means of abstraction, for it allows to refer to results of complex computations by name.

The names that we use for variables are generally known as identifiers. Keywords, variables, and symbols are collectively called identifiers. Identifiers may be formed from letters, digits and these special characters: _, $, %, and @. Some characters are reserved by the language for special purposes, mostly to be used as operators. The characters that cannot normally be used in identifiers are: +, -, /, *, <, ', >, =, \, #, ., (, ), {, }, [, ], ,, :, ; &, ? and |.

Identifiers are case-sensitive. For example, rectangle_width and Rectangle_Width are considered to be different identifiers. There is no inherent limit to the length of identifiers.

A number of identifiers are reserved by Slogan for syntactic definition. These are known as reserved words or keywords. They cannot be used as names for variables. The reserved words in Slogan are listed below:


function
module
record
true
false
if
else
when
let
letfn
letrec
letdyn
yield
case
match
where
try
trycc
catch
finally
namespace
declare
assert
for
break
continue
      

3.1.2 Grouping Expressions Together

It is possible to have multiple expressions evaluated in sequence and have a single value returned. Such groupings of expressions are known as code blocks. The expressions in a code block are enclosed in opening and closing curly braces. ({ }). The expressions in a code block is evaluated one after the other in the order they appear and the value of the final expression will be returned as the value of the entire block.


{ showln(1 + 2)
  showln(2 * 2)
  sqrt(5 + 2) }
//> 3
//> 4
// 2.6457513110645907
      

A code block is like any other expression that evaluates to a single value. It can appear wherever ordinary expressions are legal.

The scope of variables defined in a code block are limited to that code block. They won't be visible to code outside the block. All variable definition statements must appear at the top of the code block.


let x = 100

{ let x = 200
  x + 10 }
// 210

x + 10
// 110
      

3.2 Defining Functions

The previous section described how values can be abstracted away by assigning them to variables. Function definitions are a more powerful abstraction mechanism by which an expression itself can be referenced by a name.

Imagine how you would express the conversion of temperature from Celsius to Fahrenheit. The procedure can be described in English as "multiply temperature in Celsius by 9, then divide by 5, then add 32". This description can be translated to a Slogan function as


function celsius_to_fahrenheit(temperature)
  temperature * 9 / 5 + 32
      

A function definition is introduced by the function keyword. It is followed by the function's name. The celsius_to_fahrenheit function requires the caller to specify the temperature in Celsius that needs to be converted. This value becomes an entry in the parenthesized parameter list of the function. The parameter list is followed by an expression which is the formula for converting temperature to Fahrenheit. Unlike functions in imperative languages such as C, no explicit return statement is required to return a value from the function. The value of the last expression is implicitly returned as the value of the function call.

Here are a few instances of using the temperature conversion function:

    
celsius_to_fahrenheit(180)
// 356
celsius_to_fahrenheit(30)
// 86
      

Exercise 3.1.   Define a function to express the idea of squaring, i.e, multiplying a number x by itself.

Exercise 3.2.   Write a function that takes the radius of a circle as argument and return its area. The area of a circle is computed by the formula: area = π × radius2.

3.3 Comparison and Logical Operations

Slogan represent truth or Boolean values with the identifiers true and false. They are used as values for operations that return an yes or no answer. For instance, we can check if two values are equal and get back a Boolean result:


1 == 1
// true
2 == 3
// false
      

We can also do other kinds of comparisons like checking if a value is less than or greater than another value:


1 < 2            
// true
1 < 1
// false    
1 <= 1 // less-than-or-equal-to
// true
1 > 2
// false
1 > 1
// false    
1 >= 1 // greater-than-or-equal-to
// true

1 <> 2 // not-equals
// true
not(1 == 2)
// true

// like arithmetic operators, comparison operators are also real functions
`<`(1, 2, 3, 4, 5)
// true
`<`(1, 2, 3, 4, 4)
// false
`<=`(1, 2, 3, 4, 4)
// true
      

Often we want to make a decision based on more than one Boolean value. The two logical operators - && (and) and || (or) - are used for combining Boolean values.3


1 < 2 && 2 == 2
// true
and(1 < 2, 2 == 2, 3 > 2)
// true
1 < 0 || 2 == 3 || 3 == 3
// true
or(1 < 0, 2 == 3, 3 > 4)
// false
      

3.4 Conditional Expressions

Conditional expressions allows us to make a test and perform different operations based on the result of that test. The most general conditional is the if expression.4

The syntax of if is shown below:


if (condition_expression)
  consequent_expression            
else
  alternative_expression
      

If condition_expression evaluates to true, consequent_expression is evaluated, otherwise alternative_expression is evaluated. The value of the expression that gets evaluated will become the value of the whole if expression.

Let us write a function that makes use of comparison operators and condition expressions. The function is called reciprocal and it computes 1/n for a number n when it is not equal to 0. If n is 0 reciprocal will return the string "oops!".5


function reciprocal(n)
  if (n == 0) "oops!"
  else 1/n
      

Examples of using the reciprocal function are shown below:


reciprocal(10)
// 1/10
reciprocal(1/10)
// 10
reciprocal(0)
// oops!
reciprocal(reciprocal(1/10))
// 1/10
      

Alternative_expression can be another if expression, making it possible to check for multiple conditions. This is demonstrated by the next function which calculates train fare discounts based on the age and sex of the traveler. The rules for computing the discounts are these – if the traveler is female, give a 7% discount, if the traveler is more than 60 years of age, add 12.5% to the discount. The function takes the original fare, the age of the traveler and a Boolean value that indicates whether the traveler is female or not.


function discount(fare, age, is_female)
  if (is_female && age > 60) fare * (0.07 + 0.125)
  else if (is_female) fare * 0.07
  else if (age > 60) fare * 0.125
  else 0

// Usage:
discount(1230, 61, true)  
// 239.85
discount(1230, 42, true)
// 86.10000000000001
discount(1230, 42, false)
// 0
discount(1230, 63, false)
// 153.75
      

Alternative_expression is optional. If it is omitted and consequent_expression evaluates to false, if will return false.


if (1 < 2) 100
// 100
if (1 > 2) 100
// false
      

The when expression is a cleaner alternative to if expressions without the else clause.


when (1 < 2) 100
// 100
when (1 > 2) 100
// false
      

3.4.1 The Truthfulness of Values

Though Slogan support two explicit identifiers to represent Boolean values, all values other than the identifier false is treated as true by the conditional expressions.


if (1 + 2) "ok" else "no"
// ok
      

Exercise 3.3.   Define a function that takes three numbers as arguments and returns the sum of the squares of the two larger numbers.

3.5 Repeating Yourself

Some problems require iterating over the same expressions for a given number of times. Most imperative languages provide constructs like the for and while loops for this purpose. Slogan gives you a simpler solution – if a function wants to repeatedly do some stuff, just make recursive calls to itself. If the recursive call is made at a position which is the last expression in the function, Slogan will arrange its internal affairs in such a way that the call stack won't overflow.

The next example defines a function that prints a table of Celsius to Fahrenheit conversions. It take three arguments - the lower limit of the table, the upper bound and the size by which each entry in the table differs.


function print_temperature_table(lower, upper, size)
  when (lower <= upper)
  { showln(lower, "c:", celsius_to_fahrenheit(lower), "f")
    print_temperature_table(lower + size, upper, size) }
      

This function makes use of the celsius_to_fahrenheit function we defined earlier. Print_temperature_table will call itself until the limit is reached. This recursive call is made as the last expression in the conditional block. This also happens to be the last expression executed by the function. So Slogan translates this into an iterative call, removing the previous call stacks from the call frame. This optimization allows the function to call itself as many times as it wants and no special looping construct is needed.6

Before we move to the next topic, let us once see the print_temperature_table in action:


print_temperature_table(0, 100, 20)
//> 0c:32f
   20c:68f
   40c:104f
   60c:140f
   80c:176f
   100c:212f
      

Exercise 3.4.   Read the Wikipedia article on ancient Egyptian multiplication. Use recursion to implement one of the algorithms described there.

3.6 Time Server Revisited

Now we know enough about Slogan to fix the first problem that our time server had – its inability to handle more than a single client. This can be fixed using recursion. We will put the basic behavior of the server in a function that calls itself for handling each client.


// file: time_server02.sn
            
let server = tcp_server_stream(2121)
            
function client_handler()
{ let client = read(server)
  let request = read_line(client)
  if (request == "GET TIME")
    showln(stream = client, time_to_string(now()))
  else
    showln(stream = client, "error: invalid request")
  close_stream(client)
  // call self to handle next client
  client_handler() }

// start the handler
client_handler()  
// we will never reach here
close_stream(server)
      

To try the new server, load the time_server02.sn script into the REPL:


load("time_server02")
      

Start another REPL and load the client time_client.sn script. To send multiple requests to the server, just reload the client multiple times. The new server will be able to handle these requests without requiring a restart. A significant improvement indeed!


load("time_client")
//> 2017-04-30T08:00:48
load("time_client")
//> 2017-04-30T08:00:49
      

As the server always expects another client to connect, it will never terminate. You can terminate the server process by pressing the Control+C key-combination in the shell that the server is running.


1Starting from this chapter, we will only show code snippets and their output without the slogan> prompt. You may evaluate the programs interactively at the REPL or type them into a file that is compiled and executed later. While using the REPL, remember to end all expressions with a semicolon (;), so that the interpreter can kick-in and evaluate that expression. Semicolons are normally not required to terminate expressions entered into a file. So we omit them in the code examples as well.

2A statement is often defined as code that does something and does not produce a value. Slogan do not have statements in this sense, it only have expressions. Even statements like let return a value. This value is a special object known as void. So, in the context of Slogan, a statement is an expression that returns void.

3&& takes precedence over ||.

4If is an expression because it returns the value of the expression that was conditionally evaluated. This may come as a surprise for some among the readers as most popular languages treat conditionals as statements.

5The string data type will be covered in detail in the next chapter.

6Slogan do have a built-in imperative looping construct which is similar to the for loop in C. But Slogan's for loop is more powerful and expressive, as you will discover later. It is also possible to add new looping constructs to the language using the syntactic extension facility. Despite all this, the preferred way to repeatedly execute a piece of code in Slogan is by tail-recursion!


Next | Previous | Contents