Other data structures

Beyond lists and tuples, SCL provides several specialized data structures for different performance and mutability requirements.

Vector

Vector is a dense array backed by a Java array. It is more efficient than [a] (linked list) for numeric data and random access by index:

import "Vector"

v :: Vector Double
v = vector [1.0, 2.0, 3.0, 4.0, 5.0]

> v ! 2
3.0

> length v
5

Vector a and [a] are interconvertible:

vector       :: [a] -> Vector a
vectorToList :: Vector a -> [a]

Use Vector when:

  • You need O(1) indexed access by position.
  • The data is numeric and will be accessed many times.
  • You are interfacing with Java APIs that expect arrays.

Map

Map.T k v is a functional (immutable) hash map from keys of type k to values of type v. Import the module qualified to avoid name clashes:

import "Map" as Map

m :: Map.T String Integer
m = Map.fromList [("a", 1), ("b", 2), ("c", 3)]

> Map.get m "b"
Just 2

> Map.get m "z"
Nothing

Key operations:

Map.empty    :: Map.T k v
Map.singleton :: k -> v -> Map.T k v
Map.fromList :: [(k,v)] -> Map.T k v
Map.toList   :: Map.T k v -> [(k,v)]
Map.get      :: Map.T k v -> k -> Maybe v
Map.put      :: Map.T k v -> k -> v -> Map.T k v
Map.remove   :: Map.T k v -> k -> Map.T k v
Map.size     :: Map.T k v -> Integer
Map.keys     :: Map.T k v -> [k]
Map.values   :: Map.T k v -> [v]

Set

Set.T a is a functional (immutable) hash set of elements of type a:

import "Set" as Set

s :: Set.T String
s = Set.fromList ["apple", "banana", "cherry"]

> Set.member s "banana"
True

> Set.member s "grape"
False

Key operations:

Set.empty     :: Set.T a
Set.fromList  :: [a] -> Set.T a
Set.toList    :: Set.T a -> [a]
Set.member    :: Set.T a -> a -> Boolean
Set.add       :: Set.T a -> a -> Set.T a
Set.remove    :: Set.T a -> a -> Set.T a
Set.size      :: Set.T a -> Integer
Set.union     :: Set.T a -> Set.T a -> Set.T a
Set.intersect :: Set.T a -> Set.T a -> Set.T a

Mutable data structures

For algorithms that naturally require mutation, SCL provides mutable companions to the immutable types.

MList (mutable list)

import "MList"

buildList :: <Proc> [Integer]
buildList = do
    ml = MList.create ()
    for [1..10] $ \i ->
        MList.add ml (i * i)
    MList.freeze ml    // converts to immutable [Integer]

MMap (mutable map)

import "MMap"

countWords :: [String] -> <Proc> Map.T String Integer
countWords words = do
    mm = MMap.create ()
    for words $ \w -> do
        current = fromMaybe 0 (MMap.get mm w)
        MMap.put mm w (current + 1)
    MMap.freeze mm

MSet (mutable set)

import "MSet"

uniqueItems :: [String] -> <Proc> Set.T String
uniqueItems items = do
    ms = MSet.create ()
    for items $ \item ->
        MSet.add ms item
    MSet.freeze ms

freeze converts the mutable structure to its corresponding immutable type. After freezing, the mutable structure should no longer be used.

Ref — mutable references

Ref a is a single mutable cell holding a value of type a. See 1.08 Side effects for details.

import "Prelude"

counter :: <Proc> Integer
counter = do
    c = ref 0
    for [1..10] $ \_ ->
        c := getRef c + 1
    getRef c

Coercion

The Coercion module provides type-safe coercions between compatible types:

import "Coercion"

coerce :: a -> b    // only valid when a and b are coercible

Built-in coercions cover numeric conversions, String/[Character], and newtype wrappers. User-defined coercions can be added via type class instances. Use coerce only when you are certain the types are representationally compatible; misuse causes runtime errors.