Created
October 26, 2017 12:01
-
-
Save eiriktsarpalis/ddfa80c64b2d7ba682f2a3822a1d5887 to your computer and use it in GitHub Desktop.
Encoding Cancellation on top of foreach loops
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// One of the common annoyances when writing imperative code in F# is that the | |
// expression-based nature of the language makes it effectively impossible to | |
// break or return in the midst of a for-loop. This is an attempt to encode | |
// lightweight cancellation on top of native for loops. As usual, this is intended | |
// as a PoC and should not be considered seriously for production code. | |
module Seq = | |
open System.Collections | |
open System.Collections.Generic | |
type Cancellation = unit -> unit | |
type private CancellableEnumerator<'T> (seq : seq<'T>) = | |
let inner= seq.GetEnumerator() | |
let mutable isCanceled = false | |
let cancel : Cancellation = fun () -> isCanceled <- true | |
interface IEnumerator<struct('T * Cancellation)> with | |
member __.Current: struct('T * Cancellation) = struct(inner.Current, cancel) | |
member __.Current: obj = box(struct(inner.Current, cancel)) | |
member __.MoveNext(): bool = not isCanceled && inner.MoveNext() | |
member __.Reset(): unit = inner.Reset(); isCanceled <- false | |
member __.Dispose() = inner.Dispose(); isCanceled <- true | |
let cancellable (seq : seq<'T>) : seq<struct('T * Cancellation)> = | |
{ new IEnumerable<struct('T * Cancellation)> with | |
member __.GetEnumerator(): IEnumerator = | |
new CancellableEnumerator<'T>(seq) :> _ | |
member __.GetEnumerator(): IEnumerator<struct('T * Cancellation)> = | |
new CancellableEnumerator<'T>(seq) :> _ } | |
// Example | |
// regular, enumerator-based approach | |
let forall (f : 'T -> bool) (ts: seq<'T>) = | |
let mutable forall = true | |
use enum = ts.GetEnumerator() | |
while forall && enum.MoveNext() do | |
forall <- f enum.Current | |
forall | |
// for loop with cancellation | |
let forall' (f : 'T -> bool) (ts: seq<'T>) = | |
let mutable forall = true | |
for struct(t,cancel) in Seq.cancellable tsdo | |
if not(f t) then | |
forall <- false | |
cancel() | |
forall | |
forall (fun i -> printfn "%d" i ; i < 5) [1 .. 10] | |
forall' (fun i -> printfn "%d" i ; i < 5) [1 .. 10] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment