FSharpEventAddons 1.0.1

dotnet add package FSharpEventAddons --version 1.0.1
                    
NuGet\Install-Package FSharpEventAddons -Version 1.0.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="FSharpEventAddons" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="FSharpEventAddons" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="FSharpEventAddons" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add FSharpEventAddons --version 1.0.1
                    
#r "nuget: FSharpEventAddons, 1.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package FSharpEventAddons@1.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=FSharpEventAddons&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=FSharpEventAddons&version=1.0.1
                    
Install as a Cake Tool

FSharpEventAddons

A functional programming-inspired event scheduling library for F# with priority, delayed, and periodic event schedulers. (current version 1.0.1; already stable)

If you're using F# event models for the first time, or want to refresh your knowledge, please see the Beginner's Guide section.

Requirements

  • .NET 8.0 or higher
  • F# projects or F# scripts

Features

  • PriorityEventScheduler: Execute events based on priority (higher number = higher priority)
  • DelayedEventScheduler: Schedule events to execute after specified delays
  • PeriodicEventScheduler: Execute events at regular intervals with unique identifier management
  • CompositeEventScheduler: Unified scheduler combining all three types for complex workflows (NEW in version 1.0.1)
  • Thread-safe: All operations are thread-safe using proper locking
  • Functional API design: Provides functional programming patterns while using mutable state internally for performance
  • Honest documentation: Clear about implementation trade-offs between functional purity and practical performance

Installation

Install the package using NuGet:

Install-Package FSharpEventAddons

Or via .NET CLI:

dotnet add package FSharpEventAddons

If you're using F# scripts, you can reference the library directly:

#r "nuget: FSharpEventAddons"

Quick Start

Import this library to your F# project:

open FSharpEventAddons

Priority-based Event Scheduling

let scheduler = PriorityEventScheduler()
scheduler.Schedule((fun () -> printfn "High priority event"), Some 10)
scheduler.Schedule((fun () -> printfn "Low priority event"), Some 1)
scheduler.Schedule((fun () -> printfn "Default priority event"), None)
printfn "Priority scheduler event count: %d" (scheduler.EventCount) // 3 events
scheduler.Execute() // Executes in priority order

Delayed Event Scheduling

let scheduler = DelayedEventScheduler()
scheduler.ScheduleDelayed((fun () -> printfn "Delayed event"), 1.0) // 1 second delay
// Call ExecuteExpired() periodically to check for expired events

Periodic Event Scheduling

let scheduler = PeriodicEventScheduler()
let eventId = scheduler.SchedulePeriodic((fun () -> printfn "Periodic event"), 0.5) // Every 0.5 seconds (approx. 500 ms)
// Call ExecuteDue() periodically to execute due events

Composite Event Scheduling (NEW in 1.0.1)

let compositeScheduler = CompositeEventScheduler()

// Schedule different types of events
compositeScheduler.SchedulePriority((fun () -> printfn "High priority event"), 10)
compositeScheduler.ScheduleDelayed((fun () -> printfn "Delayed event"), 2.5) // After 2.5 seconds
let periodicId = compositeScheduler.SchedulePeriodic((fun () -> printfn "Periodic event"), 1.0) // Every second

// Execute all events in unified workflow
printfn "Total events: %d" (compositeScheduler.TotalEventCount)
let (priorityCount, delayedCount, periodicCount) = compositeScheduler.GetEventCounts()
printfn "Priority: %d, Delayed: %d, Periodic: %d" priorityCount delayedCount periodicCount

// Execute all pending events
compositeScheduler.ExecuteAll()

API Reference

PriorityEventScheduler

  • Schedule(action: unit -> unit, priority: int option) : unit - Schedule an event with priority; None for default priority of 0
  • Execute() : unit - Execute all pending events in priority order
  • EventCount: int - Get current count of pending events
  • Clear() : unit - Clear all pending events

DelayedEventScheduler

  • ScheduleDelayed(action: unit -> unit, delaySec: float) : unit - Schedule event with delay in seconds (must be greater than 0)
  • ExecuteExpired() : unit - Execute events whose delay has expired
  • EventCount: int - Get current count of delayed events
  • SecondsUntilNextEvent: float option - Time until next scheduled event in seconds
  • Clear() : unit - Clear all delayed events

PeriodicEventScheduler

  • SchedulePeriodic(action: unit -> unit, intervalSec: float) : Guid - Schedule periodic event with seconds interval (must be greater than 0), and returns a unique identifier for the event
  • ExecuteDue() : unit - Execute due events whose interval has elapsed
  • EventCount: int - Get current count of periodic events
  • RemovePeriodic(eventId: Guid) : unit - Remove specific periodic event
  • Clear() : unit - Clear all periodic events

CompositeEventScheduler (NEW in 1.0.1)

  • SchedulePriority(action: unit -> unit, priority: int option) : unit - Schedule a priority event with optional priority parameter
  • ScheduleDelayed(action: unit -> unit, delaySec: float) : unit - Schedule a delayed event with seconds delay
  • SchedulePeriodic(action: unit -> unit, intervalSec: float) : Guid - Schedule a periodic event with seconds interval, returns unique identifier
  • ExecuteAll() : unit - Execute all pending events across all schedulers (priority → delayed → periodic)
  • ExecutePriority() : unit - Execute only priority events
  • ExecuteDelayed() : unit - Execute only expired delayed events
  • ExecutePeriodic() : unit - Execute only due periodic events
  • TotalEventCount: int - Get total count of all pending events
  • GetEventCounts() : (int * int * int) - Get individual counts for priority, delayed, and periodic events
  • SecondsUntilNextDelayedEvent: float option - Time until next delayed event in seconds
  • RemovePeriodic(eventId: Guid) : unit - Remove specific periodic event
  • ClearAll() : unit - Clear all events from all schedulers
  • ClearPriority() : unit - Clear only priority events
  • ClearDelayed() : unit - Clear only delayed events
  • ClearPeriodic() : unit - Clear only periodic events

F# Event Models: A Beginner's Guide

Understanding F# Event Handling

F# provides several built-in event handling mechanisms, but they differ from traditional object-oriented event systems:

1. F# Event Type
let event = Event<int>()
let observable = event.Publish

observable.Add(fun x -> printfn "Event received: %d" x)
event.Trigger(42)
2. Observable Pattern
let numbers = [1..10]
numbers 
|> List.toSeq 
|> Observable.ofSeq
|> Observable.filter (fun x -> x % 2 = 0)
|> Observable.add (printfn "Even number: %d")
3. MailboxProcessor (Actor Model)
type Message = 
    | Schedule of (unit -> unit) * int
    | Execute

let scheduler = MailboxProcessor.Start(fun inbox ->
    let rec loop events = async {
        let! msg = inbox.Receive()
        match msg with
        | Schedule(action, priority) -> 
            return! loop ((action, priority) :: events)
        | Execute ->
            events 
            |> List.sortBy snd 
            |> List.iter (fst >> Async.RunSynchronously)
            return! loop []
    }
    loop [])

Why Use FSharpEventAddons?

While F# has built-in event handling, FSharpEventAddons provides:

  1. Priority-based execution - Built-in priority queues
  2. Time-based scheduling - Precise delayed and periodic execution
  3. Functional composition - Designed with functional programming principles
  4. Thread safety - Safe for concurrent usage
  5. Simple API - Easy to understand and use

Comparison with Other Approaches

Feature F# Events MailboxProcessor FSharpEventAddons
Priority scheduling ⚠️ (Manual)
Delayed execution ⚠️ (Complex)
Periodic execution ⚠️ (Complex)
Thread safety
Functional design

Advanced Usage

Combining Schedulers

let priorityScheduler = PriorityEventScheduler()
let delayedScheduler = DelayedEventScheduler()

// Schedule a delayed event that adds a priority event
delayedScheduler.ScheduleDelayed(
    (fun () -> 
        priorityScheduler.Schedule(
            (fun () -> printfn "Delayed priority event"), 
            Some 1)), 
    2.0)

Error Handling

let safeScheduler = PriorityEventScheduler()

safeScheduler.Schedule(
    (fun () -> 
        try
            // Your event logic here
            printfn "Event executed successfully"
        with
        | ex -> printfn "Event failed: %s" ex.Message),
    None)

Implementation Trade-offs

Honest Assessment of Functional Purity vs. Practical Performance

This library makes a deliberate trade-off: it provides a functional API while using mutable state internally for performance reasons. Here's why:

Why Mutable State?
  • Performance: Pure functional implementations with immutable state would require copying the entire event list on every operation
  • Thread Safety: The mutable state is properly locked, ensuring thread safety without sacrificing performance
  • Practicality: For event scheduling systems that handle many events, immutable state would be prohibitively expensive
Functional Patterns Used

Despite the mutable implementation, the library embraces functional programming principles:

  • Immutable Data Structures: Event items are F# records (immutable by default)
  • Pure Functions: Operations like sorting and filtering use pure functional patterns
  • Functional Composition: API designed for functional composition and pipelining

When to Use This Library vs. Pure Functional Alternatives

Use FSharpEventAddons when:

  • You need high-performance event scheduling
  • You're dealing with many events (100+)
  • Thread safety is a requirement
  • You want a simple, practical API

Consider pure functional alternatives when:

  • You're dealing with very few events (< 10)
  • Functional purity is more important than performance
  • You're building a learning/educational project

Performance Considerations

  • PriorityEventScheduler: O(n log n) for execution due to sorting
  • DelayedEventScheduler: O(n) for execution, efficient for small numbers of events
  • PeriodicEventScheduler: O(n) for execution, efficient interval checking

For high-performance scenarios, consider batching events or using specialized data structures.

Building from Source

git clone https://github.com/Pac-Dessert1436/FSharpEventAddons.git
cd FSharpEventAddons
dotnet build
dotnet test  # If tests are added later

Contributing

Contributions are welcome! Please feel free to submit pull requests or open issues for bugs and feature requests.

License

This project is licensed under the BSD 3-Clause License. See the LICENSE file for details.

Version History

  • 1.0.1: Added CompositeEventScheduler with existing schedulers combined
  • 1.0.0: Stable release with priority, delayed, and periodic event schedulers
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.1 100 4/28/2026
1.0.0 106 4/27/2026