Side effects

Even if the functional programming style prefers writing pure functions without side-effects, sometimes side-effects are the reason for running the function in the first place.

This section lists the most important functions with side-effects that are defined in the SCL standard library.

Printing

print :: Show a => a -> <Proc> () (Prelude)

Prints the given value in the console.

printString :: String -> <Proc> () (Prelude)

Prints the given string to the console.

printError :: String -> <Proc> () (Prelude)

Prints an error message to the console.

printingToFile :: String -> <b> a -> <b> a (Prelude)

printingToFile "fileName" expression executes the expression so that all its console prints are written to the file given as a first parameter. The file will be written with UTF-8 encoding.

References

ref :: a -> <Proc> Ref a (Prelude)

Creates a new reference with the given initial value.

getRef :: Ref a -> <Proc> a (Prelude)

Returns the current value of the reference.

(:=) :: Ref a -> a -> <Proc> () (Prelude)

Sets a new value for the reference.

Mutable arrays

data T a (ArrayList)

Type of lists.

new :: () -> <Proc> ArrayList.T a (ArrayList)

Constructs a new list.

add :: ArrayList.T a -> a -> <Proc> () (ArrayList)

Adds an element to the list.

remove :: ArrayList.T a -> Integer -> <Proc> a (ArrayList)

Removes the i:th element of the list and returns it. Indexing starts from zero.

get :: ArrayList.T a -> Integer -> <Proc> a (ArrayList)

Gets the i:th element of the list. Indexing starts from zero.

length :: ArrayList.T a -> <Proc> Integer (ArrayList)

The current length of the list.

contains :: ArrayList.T a -> a -> <Proc> Boolean (ArrayList)

Returns True if the list contains the given element.

iter :: (a -> <b> ()) -> ArrayList.T a -> <Proc,b> () (ArrayList)

Iterates through the list. The elements added during the iteration are also iterated.

The length is re-read before every step, so appending to the list from inside the function extends the iteration instead of failing or being ignored. That makes this usable as a worklist loop, and also means the iteration does not terminate if the function always appends. mapInPlace behaves differently: it reads the length once up front.

Example:

> l = ArrayList.fromList [1, 2]
> ArrayList.iter (\x -> if x < 4 then ArrayList.add l (x + 2) else ()) l
> ArrayList.freeze l
[1, 2, 3, 4, 5]

The 3 and 4 appended while iterating 1 and 2 were themselves visited, which is how 5 came to be added.

for :: ArrayList.T a -> (a -> <b> ()) -> <Proc,b> () (ArrayList)

iter with the parameters swapped: the list comes first. See iter for the semantics.

mapInPlace :: (a -> <b> a) -> ArrayList.T a -> <Proc,b> ArrayList.T a (ArrayList)

Replaces every element of the list by the result of applying the element to the given function.

The given list is mutated and the very same list is returned; the result is not a copy, so the return value can be ignored.

Unlike iter, the length is read once before the loop starts. Elements appended during the traversal are therefore not visited.

Examples:

> l = ArrayList.fromList [1, 2, 3]
> ArrayList.freeze (ArrayList.mapInPlace (\x -> x * 10) l)
[10, 20, 30]

Only the two original elements are mapped here, even though the list grows to four:

> l = ArrayList.fromList [1, 2]
> ArrayList.freeze (ArrayList.mapInPlace (\x -> do ArrayList.add l 99 ; x * 10) l)
[10, 20, 99, 99]
popUntilEmpty :: ArrayList.T a -> (a -> <b> ()) -> <Proc,b> () (ArrayList)

Pops the last element of the list until list becomes empty.

Elements are consumed from the end of the list, so this is a LIFO worklist drain, not a front-to-back traversal: the function is called with the elements in reverse order and the list is left empty. Because the last element is re-read on every step, the function may push more work onto the list.

Note the argument order, which is the reverse of iter: the list comes first and the function second, like for.

Example:

> l = ArrayList.fromList [1, 2, 3]
> ArrayList.popUntilEmpty l (\x -> print x)
3
2
1
> ArrayList.length l
0

Mutable maps and sets

In addition to mutable arrays, SCL provides mutable map and set structures:

data T a b (MMap)

A mutable map from keys of type a to values of type b.

data T a (MSet)

A mutable set of values of type a.

Both support the usual insert/lookup/remove operations. When the mutable phase is complete, convert to an immutable value with freeze.

Escaping side-effects

runProc :: <Proc,b> a -> <b> a (Builtin)

Warning: runProc suppresses the <Proc> effect, making a side-effecting computation appear pure to the type system. This is dangerous in production code because the compiler may optimise away or reorder calls that it believes have no observable effects. Only use runProc in tightly controlled situations where you are certain reordering cannot happen.

Transactions and effect conversion

syncRead and syncWrite (from Simantics/DB) consume the <ReadGraph> and <WriteGraph> effects respectively, converting them to <Proc>. This is typically used when you need to perform database operations from within a <Proc> context. See 2.05 Semantic graph for details.

Effects propagate upward: if a function calls another function with effect <E>, the caller must also declare <E> in its type signature (unless it explicitly consumes the effect with a combinator like syncRead).

Monad sequencing with mdo

When working with monadic types (lists, Maybe, sequences), mdo provides a clean sequencing syntax without explicit >>= chains. See 1.15 Functors and monads for a full explanation.