# Three-dimensional vectors

This example shows how we might define a module type for programming with vectors in normal 3D space.

`module type vspace_3d = {`

We keep the type of vector components abstract, because it will be
useful to have the same interface for vectors with components that
are `f32`

, `f64`

, or something even more exotic.

`type scalar `

However, the vector type itself is opaque. It must be a record
with `x`

/`y`

/`z`

fields.

`type vector = {x: scalar, y: scalar, z: scalar} `

Vectors must support the usual binary operations.

```
val +: vector -> vector -> vector
val -: vector -> vector -> vector
val *: vector -> vector -> vector
val /: vector -> vector -> vector
```

As well as dot products and cross products.

```
val dot: vector -> vector -> scalar
val cross: vector -> vector -> vector
```

Vectors can be scaled by a scalar.

`val scale: scalar -> vector -> vector `

We can also take the length of a vector, as well as normalise a vector to have unit length.

```
val length: vector -> scalar
val normalise: vector -> vector
```

Finally, vectors can be rotated around the three axes.

```
val rot_x : (radians: scalar) -> vector -> vector
val rot_y : (radians: scalar) -> vector -> vector
val rot_z : (radians: scalar) -> vector -> vector
}
```

So for which scalars can this module type be implemented? We describe this with yet another module type.

```
module type scalar = {
type t
val +: t -> t -> t
val -: t -> t -> t
val *: t -> t -> t
val /: t -> t -> t
val f64: f64 -> t
val sqrt : t -> t
val cos : t -> t
val sin : t -> t
}
```

Because of Futhark’s structural type system, the built-in modules
`f32`

and `f64`

already satisfy this interface.

We can now define a parametric module for constructing vector spaces:

```
module mk_vspace_3d(P: scalar): vspace_3d with scalar = P.t = {
type scalar = P.t
type vector = {x: scalar, y: scalar, z: scalar}
def zero = {x = P.f64 0, y = P.f64 0, z = P.f64 0}
def one = P.f64 1
```

Most of the definitions are straight out of a textbook, and so we won’t be providing much commentary.

We start out by defining two helper functions for doing operations on the components of a vector.

```
def map (f: scalar -> scalar) (v : vector) =
{x = f v.x, y = f v.y, z = f v.z}
def map2 (f: scalar -> scalar -> scalar) (a : vector) (b : vector) =
{x = f a.x b.x, y = f a.y b.y, z = f a.z b.z}
```

This allows us to conveniently define vector arithmetic and scaling.

```
def (+) = map2 (P.+)
def (-) = map2 (P.-)
def (*) = map2 (P.*)
def (/) = map2 (P./)
def scale (s: scalar) = map (s P.*)
```

The remaining operations are defined explicitly.

```
def dot (a: vector) (b: vector) =
P.(a.x*b.x + a.y*b.y + a.z*b.z)
def cross ({x=ax,y=ay,z=az}: vector)
({x=bx,y=by,z=bz}: vector): vector =
P.({x=ay*bz-az*by, y=az*bx-ax*bz, z=ax*by-ay*bx})
def length v = P.sqrt (dot v v)
def normalise (v: vector): vector =
let l = length v
in scale (one P./ l) v
def rot_x (theta: scalar) ({x,y,z} : vector) =
let cos_theta = P.cos theta
let sin_theta = P.sin theta
in { x
, y = P.(cos_theta * y - sin_theta * z)
, z = P.(sin_theta * y + cos_theta * z)}
def rot_y (theta: scalar) ({x,y,z} : vector) =
let cos_theta = P.cos theta
let sin_theta = P.sin theta
in { x = P.(cos_theta * x - sin_theta * z)
, y
, z = P.(sin_theta * x + cos_theta * z)}
def rot_z (theta: scalar) ({x,y,z} : vector) =
let cos_theta = P.cos theta
let sin_theta = P.sin theta
in { x = P.(cos_theta * x - sin_theta * y)
, y = P.(sin_theta * x + cos_theta * y)
, z} }
```

Instantiating the module:

`module f64_3d = mk_vspace_3d f64`

And trying it out:

```
> f64_3d.cross {x=1,y=2,z=3} {x=4,y=5,z=6}
{x = -3.0f64, y = 6.0f64, z = -3.0f64}
```

# See also

The vector package.