IvoryScript further reading

Data types: Strict and lazy

Values carry information, but not all values carry the same kind of information. The integer value forty-two and the character value 'a' are different kinds of value. A data type identifies such a kind and the values which belong to it.

IvoryScript is statically typed. Some fundamental types are:

      Int
      Double
      Char
      Bool
      String
   

The distinction established earlier between a value and an expression with a potential value is reflected directly in the type system.

The integer value:

#!42

has type:

Int

whereas the expression:

42

has type:

Exp Int

Int is a strict type. Exp Int is its corresponding lazy type. More generally, for any type a:

      a
      Exp a
   

are distinct types. A value of type Exp a is not an unfinished value of type a. It is a different value, with a potential value of type a.

The distinction can be observed directly:

      #!42       :: Int
      42         :: Exp Int

      #!1.0      :: Double
      1.0        :: Exp Double
   

It is not restricted to constants. A function application, lambda, let expression, conditional or other expression form may similarly have a lazy type. The type describes the value, not the particular syntax used to express it.

Strict and lazy values

The distinction between strict and lazy values is therefore explicit in their types. A program can contain both an integer and an integer expression at the same time:

         #!42       :: Int
         42         :: Exp Int
      

Neither type is a temporary description of the other. Int and Exp Int are distinct types. Reduction provides a relationship between them, but does not make them interchangeable.

Lazy types may themselves contain lazy types:

Exp (Exp Int)

A single reduction of a value of this type may have type Exp Int. No further reduction is implied.

Functions

Functions are values and have types in the usual way. A function accepting an integer and returning an integer may have type:

Int -> Int

A function taking two integers may have type:

Int -> Int -> Int

The arrow associates to the right, so this means:

Int -> (Int -> Int)

This corresponds naturally with currying. Applying the function to one integer may leave a function of type:

Int -> Int

The distinction between strict and lazy values applies equally to functions. A function value may have type:

Int -> Int

while an expression with such a function as its potential value has type:

Exp (Int -> Int)

Functions therefore require no special exemption from the distinction between strict and lazy values.

Type variables

Types need not always name one particular kind of value. A type variable such as:

a

stands for a type which is not yet fixed. Thus:

Exp a

describes an expression with a potential value of type a, whatever that type may be.

Similarly:

a -> a

describes a function which accepts a value of some type and returns a value of that same type. The identity function is the familiar example:

\x -> x

Its useful property does not depend upon x being an integer, character or any other particular kind of value. The relationship between the argument and result types is what matters.

Type variables allow such relationships to be expressed without unnecessarily restricting the values involved.

Thunks

IvoryScript also provides a thunk, written using lambda syntax but without a parameter:

\ -> e

A thunk is not a function awaiting an argument. It is a value wrapped in a closure. The values associated with any free variables occurring in e are captured and retained with it.

For example:

let x = 42 in \ -> x

retains the value associated with x when the thunk value first arises. Its meaning does not depend upon a later lookup of x in another environment.

This property is shared with ordinary lambdas. Both functions and thunks are closures. A function introduces one or more parameters; a thunk does not.

A thunk containing a value of type a has unary-arrow type:

-> a

This is distinct from:

Exp a

The relationship between the two is provided by:

fromThunk :: (-> a) -> Exp a

This conversion is the only special semantics associated with the thunk type. A thunk is therefore neither an ordinary function nor another notation for a lazy value. It is a value together with the captured values required to preserve its context.

Constructed types

Types may themselves be formed from other types. Exp is one example: applying it to Int forms Exp Int.

Lists provide another:

         [Int]
         [Char]
      

The element type may itself be lazy:

[Exp Int]

which describes a list of integer expressions. This is quite different from:

Exp [Int]

which describes an expression with a list of integers as its potential value.

Types can therefore describe precisely where lazy values occur within larger values. The same principle applies to tuples, functions and other constructed data types.

Types of functions over expressions

Strict and lazy types may be combined freely in function types. For example:

         Exp Int -> Int
         Int -> Exp Int
         Exp Int -> Exp Int
      

describe respectively a function from an integer expression to an integer, a function from an integer to an integer expression and a function from one integer expression to another.

These distinctions matter because an expression value and its potential value are not interchangeable merely because they are related.

How IvoryScript can nevertheless move conveniently between suitable strict and lazy types will be considered separately.

Exp therefore makes laziness explicit in the type. It is not a general property imposed upon every value in the language. Strict and lazy values can coexist and be combined within the same program, while the type system retains the distinction between them.