7 people like it.

# Using units of measure for safe array access

A typical problem when working with arrays and indices is that it's easy to access an array with the wrong index. Units of measure in F# can be applied to integers, which makes it possible to abuse them to prevent this kind of error.

 ``` 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: ``` ``````/// An array whose index has a unit of measure type MarkedArray<[] 'K, 'T> = MarkedArray of 'T[] with member this.Content = let (MarkedArray arr) = this arr member this.First : int<'K> = LanguagePrimitives.Int32WithMeasure 0 member this.Last : int<'K> = let (MarkedArray arr) = this LanguagePrimitives.Int32WithMeasure (arr.Length - 1) member this.Item with get (i : int<'K>) = let (MarkedArray arr) = this arr.[int i] and set (i : int<'K>) (v : 'T) = let (MarkedArray arr) = this arr.[int i] <- v [] module MarkedArray = let inline set (arr : MarkedArray<'K, 'T>) idx v = arr.[idx] <- v let inline get (arr : MarkedArray<'K, 'T>) idx = arr.[idx] /// arr.[idx] <- f (arr.[idx]) let inline mutate f (arr, idx) = let v = get arr idx set arr idx (f v) module Example = type Ship = Ship type Missile = Missile [] type MissileIndex [] type ShipIndex let missiles : MarkedArray = Array.create 42 (Some Missile) |> MarkedArray let ships : MarkedArray = Array.create 4 (Some Ship) |> MarkedArray // A missile hit the ship, destroy both let applyHit shipIdx missileIdx = missiles.[missileIdx] <- None ships.[shipIdx] <- None ``````
Multiple items
type MeasureAttribute =
inherit Attribute
new : unit -> MeasureAttribute

Full name: Microsoft.FSharp.Core.MeasureAttribute

--------------------
new : unit -> MeasureAttribute
Multiple items
union case MarkedArray.MarkedArray: 'T [] -> MarkedArray<'K,'T>

--------------------
type MarkedArray<'K,'T> =
| MarkedArray of 'T []
member Content : 'T []
member First : int<'K>
member Item : i:int<'K> -> 'T with get
member Last : int<'K>
member Item : i:int<'K> -> 'T with set

Full name: Script.MarkedArray<_,_>

An array whose index has a unit of measure
val this : MarkedArray<'K,'T>
member MarkedArray.Content : 'T []

Full name: Script.MarkedArray`1.Content
val arr : 'T []
member MarkedArray.First : int<'K>

Full name: Script.MarkedArray`1.First
Multiple items
val int : value:'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------
type int = int32

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
module LanguagePrimitives

from Microsoft.FSharp.Core
val Int32WithMeasure : int -> int<'Measure>

Full name: Microsoft.FSharp.Core.LanguagePrimitives.Int32WithMeasure
member MarkedArray.Last : int<'K>

Full name: Script.MarkedArray`1.Last
property System.Array.Length: int
member MarkedArray.Item : i:int<'K> -> 'T with set

Full name: Script.MarkedArray`1.Item
val i : int<'K>
val set : elements:seq<'T> -> Set<'T> (requires comparison)

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.set
val v : 'T
Multiple items
type RequireQualifiedAccessAttribute =
inherit Attribute
new : unit -> RequireQualifiedAccessAttribute

Full name: Microsoft.FSharp.Core.RequireQualifiedAccessAttribute

--------------------
new : unit -> RequireQualifiedAccessAttribute
val set : arr:MarkedArray<'K,'T> -> idx:int<'K> -> v:'T -> unit

Full name: Script.MarkedArray.set
val arr : MarkedArray<'K,'T>
val idx : int<'K>
val get : arr:MarkedArray<'K,'T> -> idx:int<'K> -> 'T

Full name: Script.MarkedArray.get
val mutate : f:('a -> 'a) -> arr:MarkedArray<'u,'a> * idx:int<'u> -> unit

Full name: Script.MarkedArray.mutate

arr.[idx] <- f (arr.[idx])
val f : ('a -> 'a)
val arr : MarkedArray<'u,'a>
val idx : int<'u>
val v : 'a
module Example

from Script
Multiple items
union case Ship.Ship: Ship

--------------------
type Ship = | Ship

Full name: Script.Example.Ship
Multiple items
union case Missile.Missile: Missile

--------------------
type Missile = | Missile

Full name: Script.Example.Missile
[<Measure>]
type MissileIndex

Full name: Script.Example.MissileIndex
[<Measure>]
type ShipIndex

Full name: Script.Example.ShipIndex
val missiles : MarkedArray<MissileIndex,Missile option>

Full name: Script.Example.missiles
Multiple items
union case MarkedArray.MarkedArray: 'T [] -> MarkedArray<'K,'T>

--------------------
module MarkedArray

from Script

--------------------
type MarkedArray<'K,'T> =
| MarkedArray of 'T []
member Content : 'T []
member First : int<'K>
member Item : i:int<'K> -> 'T with get
member Last : int<'K>
member Item : i:int<'K> -> 'T with set

Full name: Script.MarkedArray<_,_>

An array whose index has a unit of measure
module Array

from Microsoft.FSharp.Collections
val create : count:int -> value:'T -> 'T []

Full name: Microsoft.FSharp.Collections.Array.create
union case Option.Some: Value: 'T -> Option<'T>
val ships : MarkedArray<ShipIndex,Ship option>

Full name: Script.Example.ships
val applyHit : shipIdx:int<ShipIndex> -> missileIdx:int<MissileIndex> -> unit

Full name: Script.Example.applyHit
val shipIdx : int<ShipIndex>
val missileIdx : int<MissileIndex>
union case Option.None: Option<'T>