open System open Microsoft.FSharp.Collections open Microsoft.FSharp.Core.Printf open System.Collections.Generic open System.Collections.Concurrent open Fusion [] module Core = type Transducer<'a,'b> = /// partial application is the key to the functionality of a transducer /// transform should take a step function as its argument without and acc /// or input and then used by a fold or an unfold abstract transform<'r> : stepfn: ('r->'b->'r) -> acc:'r -> input:'a -> 'r // TODO Add another member to transducers that transforms the postfn on // a foldpost // OR another abstract type STateful transducer, that implements transducer /// When transducers are composed using this function t1 /// will execute before t2 let inline compose (t1:Transducer<'a,'b>) (t2:Transducer<'b,'c>) = { new Transducer<'a,'c> with member __.transform stepfn acc input = ( t2.transform >> t1.transform ) stepfn acc input } /// Comp should be used if transducers are being composed in a /// pipeline with `|>` The transducers will execute from top to /// bottom let inline comp (t1:Transducer<_,_>) (t2:Transducer<_,_>) = compose t2 t1 /// Forward composition operator /// When transducers are composed using this operator the left transducer /// will execute first let inline (|>>) xf1 xf2 = compose xf1 xf2 /// When transducers are composed using this operator the right transducer /// will execute first let inline (<<|) xf1 xf2 = comp xf1 xf2 type FoldArgs<'a,'b> = abstract StepFn : ('b->'a->'b) abstract Acc : 'b let inline (|&>) (xf:Transducer<_,_>)(fld:FoldArgs<_,_>) = { new FoldArgs<_,_> with member __.StepFn = xf.transform fld.StepFn member __.Acc = [] } let inline (<&|) (fld:FoldArgs<_,_>)(xf:Transducer<_,_>) = { new FoldArgs<_,_> with member __.StepFn = xf.transform fld.StepFn member __.Acc = [] } let inline consfn xs x = x::xs let foldList = { new FoldArgs<_,_> with member __.StepFn = consfn member __.Acc = [] } type UnfoldArgs<'a,'b,'c,'d> = abstract StepFn : ('c->'d->'c) abstract Acc : 'c abstract Pred : ('a->bool) abstract MapElm : ('a->'b) abstract Inc : ('a->'a) let inline (|~>) (xf:Transducer<_,_>)(uf:UnfoldArgs<_,_,_,_>) = { new UnfoldArgs<_,_,_,_> with member __.StepFn = xf.transform uf.StepFn member __.Acc = [] member __.Pred = uf.Pred member __.MapElm = uf.MapElm member __.Inc = uf.Inc } let inline (<~|) (uf:UnfoldArgs<_,_,_,_>)(xf:Transducer<_,_>) = { new UnfoldArgs<_,_,_,_> with member __.StepFn = xf.transform uf.StepFn member __.Acc = [] member __.Pred = uf.Pred member __.MapElm = uf.MapElm member __.Inc = uf.Inc } let inline concat = { new Transducer<_,_> with member __.transform stepfn acc input = Folds.foldl stepfn acc input } let inline collect (func:'a -> #seq<'c>) = { new Transducer<_,_> with member __.transform stepfn acc input = Folds.foldl stepfn acc (func input) } let inline map (func:'a->'b) = { new Transducer<_,_> with member __.transform (stepfn:'c->'b->'c) (acc:'c) (input:'a) = stepfn acc ( func input ) } let inline filter pred = { new Transducer<_,_> with member __.transform stepfn acc input = match pred input with | true -> stepfn acc input | false -> acc } let filterMap (pred:'a -> bool) mapfn = { new Transducer<_,_> with member __.transform stepfn acc input = match pred input with | true -> stepfn acc (mapfn input) | false -> acc } let filterMapAlt pred funcTrue funcFalse = { new Transducer<_,_> with member __.transform stepfn acc input = match pred input with | true -> stepfn acc ( funcTrue input ) | false -> stepfn acc ( funcFalse input ) } let mapWhen2 pred1 pred2 mapfn1 mapfn2 = { new Transducer<_,_> with member __.transform stepfn acc input = match pred1 input, pred2 input with | true, _ -> stepfn acc ( mapfn1 input ) | _ ,true -> stepfn acc ( mapfn2 input ) | _ , _ -> acc } let choose() = { new Transducer<_,_> with member __.transform stepfn acc input = match Option.isSome input with | true -> stepfn acc input.Value | false -> acc } /// take throws an invalid argument exception on negative input values let take num = let count = ref 0 { new Transducer<_,_> with member __.transform stepfn acc input = match num with | x when x < 0 -> invalidArg "num" (sprintf "args for take must be postive, value passed in was %d" num ) | 0 -> acc | _ -> match !count < num with | true -> incr count stepfn acc input | false -> acc } /// negative input value is the same as "take 0" let takeSafe num = let count = ref 0 { new Transducer<_,_> with member __.transform stepfn acc input = match num with | x when x <= 0 -> acc | _ -> match !count < num with | true -> incr count stepfn acc input | false -> acc } let takeWhile (pred:'a -> bool) = let taking = ref true { new Transducer<_,_> with member __.transform stepfn acc input = taking := pred input match !taking with | true -> stepfn acc input | false -> acc } let skip num = let count = ref 0 { new Transducer<_,_> with member __.transform stepfn acc input = match num with | x when x < 0 -> invalidArg "num" (sprintf "args for skip must be postive, value passed in was %d" num ) | 0 -> stepfn acc input | _ -> match !count >= num with | true -> stepfn acc input | false -> incr count acc } let skipSafe num = let count = ref 0 { new Transducer<_,_> with member __.transform stepfn acc input = match num with | x when x <= 0 -> stepfn acc input | _ -> match !count >= num with | true -> stepfn acc input | false -> incr count acc } let skipWhile (pred:'a -> bool) = let skipping = ref true { new Transducer<_,_> with member __.transform stepfn acc input = skipping := pred input match !skipping with | true -> acc | false -> stepfn acc input } let inline slice start finish = let start' = start-1 let takeNum = if finish < start' then 0 else finish - start' let sc = ref 0 // skip count let tc = ref 0 // take count { new Transducer<_,_> with member __.transform stepfn acc input = match !sc < start' with | true -> incr sc; acc | false -> match !tc < takeNum with | true -> incr tc; stepfn acc input | false -> acc } let index() = let counter = ref 0 let inc (cnt:int ref) input = let idx = !cnt incr cnt idx,input { new Transducer<_,_> with member __.transform stepfn acc input = stepfn acc (inc counter input) } /// Stateful version of index that can execute across a transduction let indexFrom (start:int) = let counter = ref start let inc (cnt:int ref) input = let idx = !cnt incr cnt idx,input { new Transducer<_,_> with member __.transform stepfn acc input = stepfn acc (inc counter input) } /// Stateful version of distinct that can operate across a reduction /// Should only be used inside of a function that returns a transducer // distinctS needs to return a transducer due to the value restriction let inline distinct() = let exists = HashSet() let dedupe (input:'a) = exists.Add (hash input) { new Transducer<_,_> with member __.transform stepfn acc input = match dedupe input with | true -> stepfn acc input | false -> acc } /// Should only be used inside of a function that returns a transducer let distinctBy (proj:'a -> 'key) = let exists = HashSet<'key>() let dedupe (input:'a) = exists.Add (proj input) { new Transducer<_,_> with member __.transform stepfn acc input = match dedupe input with | true -> stepfn acc input | false -> acc } /// Should only be used inside of a function that returns a transducer let distinctFrom (other:HashSet) = let exists = HashSet(other) let dedupe (input:'a) = exists.Add (hash input) { new Transducer<_,_> with member __.transform stepfn acc input = match dedupe input with | true -> stepfn acc input | false -> acc } // TODO - Implement the metamorphism version of Quicksort /// Create an empty hashset for storing hashes for distinction comparison let idSet() = HashSet() let inline logf (msg) = { new Transducer<_,_> with member __.transform stepfn acc input = stepfn acc (printf msg input; input) } let inline logfn (msg) = { new Transducer<_,_> with member __.transform stepfn acc input = stepfn acc (printfn msg input; input) } // Ninja Logging Operators let inline ( !?! ) t1 = (logf ("\n| %A |")) |>> t1 |>> ( logf( "==> %A " )) let inline ( |?> ) t1 t2 = t1 |>> ( t2 |>> ( logf( "==> %A " ))) let inline ( %A ")) <<| t1 [] module CoreExtensions = type Transducer<'b,'c> with member self.Fold stepfn acc = Folds.foldl (self.transform stepfn) acc member self.Unfold stepfn (acc:'c) pred mapElm inc : 'a -> 'c = Folds.unfold (self.transform stepfn) acc pred mapElm inc