Skip to content

Instantly share code, notes, and snippets.

@eiriktsarpalis
Created October 26, 2017 12:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eiriktsarpalis/ddfa80c64b2d7ba682f2a3822a1d5887 to your computer and use it in GitHub Desktop.
Save eiriktsarpalis/ddfa80c64b2d7ba682f2a3822a1d5887 to your computer and use it in GitHub Desktop.
Encoding Cancellation on top of foreach loops
// 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