4 people like it.

Emulating higher level typing using inline functions and member constraints

This pattern shows that by using a few static member constraints, one can create an assortment of data processing functions that emulate higher level typing than is possible in F#.

  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: 
 51: 
 52: 
 53: 
 54: 
 55: 
 56: 
 57: 
 58: 
 59: 
 60: 
 61: 
 62: 
 63: 
 64: 
 65: 
 66: 
 67: 
 68: 
 69: 
 70: 
 71: 
 72: 
 73: 
 74: 
 75: 
 76: 
 77: 
 78: 
 79: 
 80: 
 81: 
 82: 
 83: 
 84: 
 85: 
 86: 
 87: 
 88: 
 89: 
 90: 
 91: 
 92: 
 93: 
 94: 
 95: 
 96: 
 97: 
 98: 
 99: 
100: 
101: 
102: 
103: 
104: 
105: 
106: 
107: 
108: 
109: 
110: 
111: 
112: 
113: 
114: 
115: 
116: 
117: 
118: 
119: 
120: 
121: 
122: 
123: 
124: 
125: 
126: 
127: 
128: 
129: 
130: 
131: 
132: 
133: 
134: 
135: 
136: 
137: 
138: 
139: 
140: 
141: 
142: 
143: 
144: 
145: 
146: 
let inline iter (f : 'a -> unit) (x : 's) = (^s : (member Iter : ('a -> unit) -> unit) x,f)
let inline iterBack (f : 'a -> unit) (x : 's) = (^s : (member IterBack : ('a -> unit) -> unit) x,f)

let inline empty ()               = (^s : (static member Empty : 's) ())
let inline (+>) item col          = (^s : (member AddFirst : 'a -> 's) col,item)
let inline (<+) col item          = (^s : (member AddLast : 'a -> 's) col,item)
let inline (<+>) col1 col2        = (^s : (static member Concat : 's -> 's -> 's) col1,col2)
let inline (++>) items col        = (^s : (member AddManyFirst : #seq<_> -> 's) col,items)
let inline (<++) col items        = (^s : (member AddManyLast : #seq<_> -> 's) col,items)

//++ Advanced Operators
//The following operators deal with adding or removing items from a data structure.

///Returns the first elements of the data structure
let inline takef n col            = (^s : (member TakeFirst : int -> ^s) col,n)
///Returns the last elements of the data structure
let inline takel n col            = (^s : (member TakeLast : int -> ^s) col,n)
///Splits the data structure at the specified index.
let inline split n col            = (^s : (member Split : int -> ^s * ^s) col,n)

///Gets the first element of the data structure
let inline first col              = (^s : (member First : 'a) col)
///Gets the last element of the data structure
let inline last col               = (^s : (member Last : 'a) col)
///Gets the element with the specified index from the data structure
let inline get i col              = (^s : (member Item : int -> ^a) col,i)
///Sets the element with the specified index from the data structure
let inline set i v col            = (^s : (member Set : ^k * 'a -> 's) col,i,v)
///Removes the first element from the data structure.
let inline dropf col              = (^s : (member DropFirst : ^s) col)
///Removes the last element from the data structure.
let inline dropl col              = (^s : (member DropLast : ^s) col)

let inline fromSeq (sq : #seq<_>) = (^s : (static member FromSeq : seq<'a> -> 's) sq)
///Reverses the data structure.
let inline reverse col            = (^s : (member Reverse : ^s) col)
///Gets the length of the data structure.
let inline length col             = (^s : (member Length : int) col)
//++ Derived Operators
// These operators use the above operators to apply functions on data structure, filter, etc.

let inline splice index inner outer = 
    let part1,part2 = outer |> split index
    let part1 = part1 <+> inner
    let closed = part1 <+> part2
    closed

let inline droplMany n target = 
    let len = target |> length
    let first = target |> takef (len - n)
    first

let inline dropfMany n target = 
    let len = target |> length
    let last = target |> takel (len - n)
    last

let inline insertManyAt index (sq : seq<'a>) (target : ^s) = 
    let ins  = (sq |> fromSeq)
    target |> splice index ins

let inline removeAt index target = 
    let part1,part2 = target |> split index
    let part1 = part1 |> dropl
    part1 <+> part2

let inline removeManyAt index count target= 
    let part1,part2 = target |> split index
    let part1 = part1 |> droplMany count
    part1 <+> part2

let inline insertAt index item target = 
    let part1,part2 = target |> split index
    let part1 = part1 <+ item
    let closed = part1 <+> part2
    closed

///Applies the specified transformation on the data structure.
let inline map (f : 'a -> 'b) col = 
    let refRoot = ref (empty() : 'b)
    let mapIter x = 
        let x = x |> f
        refRoot := (!refRoot <+ x)
    col |> iter mapIter
    !refRoot

///Filters the data structure using the specified predicate.
let inline filter (f : 'a -> bool) (col : 's) : 's =
    let refRoot = ref (empty() : ^s)
    let filterIter (x : 'a) = 
        if f x then refRoot := !refRoot <+ x 
    col |> iter filterIter
    !refRoot

///Applies a a function on the data structure, keeping all elements which return Some, and dropping all those that return None.
let inline choose (f : _ -> _ option) col = 
    let refRoot = ref (empty() : 'b)
    let chooseIter x = 
        match f x with
        | None -> ()
        | Some y -> refRoot := !refRoot + y
    col |> iter chooseIter
    !refRoot

///Constructs a linked list from the elements of this data structure
let inline toList col = 
    let refList = ref []
    let listIter x = 
        refList := x :: !refList
    col |> iter listIter
    !refList

///Folds over the data structure using the specified function.
let inline fold (foldr : _-> _ -> _) (start : _) col = 
    let refState = ref start
    let foldIter x = 
        refState := foldr (refState.Value) x
    col |> iter foldIter
    !refState

///Folds over the data structure backwards, using the specified function.
let inline foldBack (foldr :_ -> _ -> _) (start :_) col = 
    let refState = ref start
    let foldbIter x = 
        refState := foldr (refState.Value) x 
    col |> iterBack foldbIter
    !refState

///Returns true if the specified predicate is true for all items in the data structure.
let inline forall (pred : _ -> bool) col = 
    col |> fold (fun cur item -> cur && pred(item)) true

///Returns true if the specified predicate is true for any item in the data structure.
let inline forany (pred : _ -> bool) col = 
    col |> fold (fun cur item -> cur || pred(item)) false

///Counts the number of items for which the specified predicate is true.
let inline count (pred : _ -> bool) col = 
    col |> fold (fun cur item -> if pred(item) then cur + 1 else cur) 0

let inline toArray col = 
    let l = col |> length
    let arr = Array.zeroCreate l
    for i,item in Seq.zip {0 .. l} col do
        arr.[i] <- item
    arr
val iter : f:('a -> unit) -> x:'s -> unit (requires member Iter)

Full name: Script.iter
val f : ('a -> unit)
type unit = Unit

Full name: Microsoft.FSharp.Core.unit
val x : 's (requires member Iter)
val iterBack : f:('a -> unit) -> x:'s -> unit (requires member IterBack)

Full name: Script.iterBack
val x : 's (requires member IterBack)
val empty : unit -> 's (requires member get_Empty)

Full name: Script.empty
val item : 'a
val col : 's (requires member AddFirst)
val col : 's (requires member AddLast)
val col1 : 's (requires member Concat)
val col2 : 's (requires member Concat)
val items : #seq<'b>
val col : 's (requires member AddManyFirst and 'a :> seq<'b>)
Multiple items
val seq : sequence:seq<'T> -> seq<'T>

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

--------------------
type seq<'T> = System.Collections.Generic.IEnumerable<'T>

Full name: Microsoft.FSharp.Collections.seq<_>
val col : 's (requires member AddManyLast and 'a :> seq<'b>)
val takef : n:int -> col:'s -> 's (requires member TakeFirst)

Full name: Script.takef


Returns the first elements of the data structure
val n : int
val col : 's (requires member TakeFirst)
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<_>
val takel : n:int -> col:'s -> 's (requires member TakeLast)

Full name: Script.takel


Returns the last elements of the data structure
val col : 's (requires member TakeLast)
val split : n:int -> col:'s -> 's * 's (requires member Split)

Full name: Script.split


Splits the data structure at the specified index.
val col : 's (requires member Split)
val first : col:'s -> 'a (requires member get_First)

Full name: Script.first


Gets the first element of the data structure
val col : 's (requires member get_First)
val last : col:'s -> 'a (requires member get_Last)

Full name: Script.last


Gets the last element of the data structure
val col : 's (requires member get_Last)
val get : i:int -> col:'s -> 'a (requires member Item)

Full name: Script.get


Gets the element with the specified index from the data structure
val i : int
val col : 's (requires member Item)
val set : i:'k -> v:'a -> col:'s -> 's (requires member Set)

Full name: Script.set


Sets the element with the specified index from the data structure
val i : 'k
val v : 'a
val col : 's (requires member Set)
Multiple items
module Set

from Microsoft.FSharp.Collections

--------------------
type Set<'T (requires comparison)> =
  interface IComparable
  interface IEnumerable
  interface IEnumerable<'T>
  interface ICollection<'T>
  new : elements:seq<'T> -> Set<'T>
  member Add : value:'T -> Set<'T>
  member Contains : value:'T -> bool
  override Equals : obj -> bool
  member IsProperSubsetOf : otherSet:Set<'T> -> bool
  member IsProperSupersetOf : otherSet:Set<'T> -> bool
  ...

Full name: Microsoft.FSharp.Collections.Set<_>

--------------------
new : elements:seq<'T> -> Set<'T>
val dropf : col:'s -> 's (requires member get_DropFirst)

Full name: Script.dropf


Removes the first element from the data structure.
val col : 's (requires member get_DropFirst)
val dropl : col:'s -> 's (requires member get_DropLast)

Full name: Script.dropl


Removes the last element from the data structure.
val col : 's (requires member get_DropLast)
val fromSeq : sq:#seq<'a0> -> 's (requires member FromSeq)

Full name: Script.fromSeq
val sq : #seq<'a0>
val reverse : col:'s -> 's (requires member get_Reverse)

Full name: Script.reverse


Reverses the data structure.
val col : 's (requires member get_Reverse)
val length : col:'s -> int (requires member get_Length)

Full name: Script.length


Gets the length of the data structure.
val col : 's (requires member get_Length)
val splice : index:int -> inner:'a -> outer:'a -> 'a (requires member Concat and member Split)

Full name: Script.splice
val index : int
val inner : 'a (requires member Concat and member Split)
val outer : 'a (requires member Concat and member Split)
val part1 : 'a (requires member Concat and member Split)
val part2 : 'a (requires member Concat and member Split)
val closed : 'a (requires member Concat and member Split)
val droplMany : n:int -> target:'a -> 'a (requires member TakeFirst and member get_Length)

Full name: Script.droplMany
val target : 'a (requires member TakeFirst and member get_Length)
val len : int
val first : 'a (requires member TakeFirst and member get_Length)
val dropfMany : n:int -> target:'a -> 'a (requires member TakeLast and member get_Length)

Full name: Script.dropfMany
val target : 'a (requires member TakeLast and member get_Length)
val last : 'a (requires member TakeLast and member get_Length)
val insertManyAt : index:int -> sq:seq<'a> -> target:'s -> 's (requires member Concat and member Split and member FromSeq)

Full name: Script.insertManyAt
val sq : seq<'a>
val target : 's (requires member Concat and member Split and member FromSeq)
val ins : 's (requires member Concat and member Split and member FromSeq)
val removeAt : index:int -> target:'a -> 'a (requires member get_DropLast and member Split and member Concat)

Full name: Script.removeAt
val target : 'a (requires member get_DropLast and member Split and member Concat)
val part1 : 'a (requires member get_DropLast and member Split and member Concat)
val part2 : 'a (requires member get_DropLast and member Split and member Concat)
val removeManyAt : index:int -> count:int -> target:'a -> 'a (requires member TakeFirst and member get_Length and member Split and member Concat)

Full name: Script.removeManyAt
val count : int
val target : 'a (requires member TakeFirst and member get_Length and member Split and member Concat)
val part1 : 'a (requires member TakeFirst and member get_Length and member Split and member Concat)
val part2 : 'a (requires member TakeFirst and member get_Length and member Split and member Concat)
val insertAt : index:int -> item:'a -> target:'b -> 'b (requires member Split and member AddLast and member Concat)

Full name: Script.insertAt
val target : 'b (requires member Split and member AddLast and member Concat)
val part1 : 'b (requires member Split and member AddLast and member Concat)
val part2 : 'b (requires member Split and member AddLast and member Concat)
val closed : 'b (requires member Split and member AddLast and member Concat)
val map : f:('a -> 'b) -> col:'a0 -> 'b (requires member get_Empty and member AddLast and member Iter)

Full name: Script.map


Applies the specified transformation on the data structure.
val f : ('a -> 'b) (requires member get_Empty and member AddLast)
val col : 'a (requires member Iter)
val refRoot : 'b ref (requires member get_Empty and member AddLast)
Multiple items
val ref : value:'T -> 'T ref

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

--------------------
type 'T ref = Ref<'T>

Full name: Microsoft.FSharp.Core.ref<_>
val mapIter : ('a -> unit)
val x : 'a
val x : 'b (requires member get_Empty and member AddLast)
val filter : f:('a -> bool) -> col:'s -> 's (requires member get_Empty and member AddLast and member Iter)

Full name: Script.filter


Filters the data structure using the specified predicate.
val f : ('a -> bool)
type bool = System.Boolean

Full name: Microsoft.FSharp.Core.bool
val col : 's (requires member get_Empty and member AddLast and member Iter)
val refRoot : 's ref (requires member get_Empty and member AddLast and member Iter)
val filterIter : ('a -> unit)
val choose : f:('a -> 'b option) -> col:'c -> 'b0 (requires member ( + ) and member get_Empty and member Iter)

Full name: Script.choose


Applies a a function on the data structure, keeping all elements which return Some, and dropping all those that return None.
val f : ('a -> 'b option) (requires member ( + ) and member get_Empty)
type 'T option = Option<'T>

Full name: Microsoft.FSharp.Core.option<_>
val col : 'c (requires member Iter)
val refRoot : 'b ref (requires member get_Empty and member ( + ))
val chooseIter : ('a -> unit)
union case Option.None: Option<'T>
union case Option.Some: Value: 'T -> Option<'T>
val y : 'b (requires member ( + ) and member get_Empty)
val toList : col:'a -> 'b list (requires member Iter)

Full name: Script.toList


Constructs a linked list from the elements of this data structure
val refList : 'b list ref
val listIter : ('b -> unit)
val x : 'b
val fold : foldr:('a -> 'b -> 'a) -> start:'a -> col:'c -> 'a (requires member Iter)

Full name: Script.fold


Folds over the data structure using the specified function.
val foldr : ('a -> 'b -> 'a)
val start : 'a
val refState : 'a ref
val foldIter : ('b -> unit)
property Ref.Value: 'a
val foldBack : foldr:('a -> 'b -> 'a) -> start:'a -> col:'c -> 'a (requires member IterBack)

Full name: Script.foldBack


Folds over the data structure backwards, using the specified function.
val col : 'c (requires member IterBack)
val foldbIter : ('b -> unit)
val forall : pred:('a -> bool) -> col:'b -> bool (requires member Iter)

Full name: Script.forall


Returns true if the specified predicate is true for all items in the data structure.
val pred : ('a -> bool)
val col : 'b (requires member Iter)
val cur : bool
val forany : pred:('a -> bool) -> col:'b -> bool (requires member Iter)

Full name: Script.forany


Returns true if the specified predicate is true for any item in the data structure.
val count : pred:('a -> bool) -> col:'b -> int (requires member Iter)

Full name: Script.count


Counts the number of items for which the specified predicate is true.
val cur : int
val toArray : col:'a -> 'b [] (requires member get_Length and 'a :> seq<'b>)

Full name: Script.toArray
val col : 'a (requires member get_Length and 'a :> seq<'b>)
val l : int
val arr : 'b []
module Array

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

Full name: Microsoft.FSharp.Collections.Array.zeroCreate
val item : 'b
module Seq

from Microsoft.FSharp.Collections
val zip : source1:seq<'T1> -> source2:seq<'T2> -> seq<'T1 * 'T2>

Full name: Microsoft.FSharp.Collections.Seq.zip
Raw view Test code New version

More information

Link:http://fssnip.net/hd
Posted:11 years ago
Author:Greg Ros
Tags: typeclass , inline , member constraints , data structures , polymorphism