r/fsharp Jun 07 '20

meta Welcome to /r/fsharp!

72 Upvotes

This group is geared towards people interested in the "F#" language, a functional-first language targeting .NET, JavaScript, and (experimentally) WebAssembly. More info about the language can be found at https://fsharp.org and several related links can be found in the sidebar!


r/fsharp 4h ago

library/package FCQRS 6 released: event-sourced CQRS with two pure functions

Post image
16 Upvotes

I’ve released FCQRS 6, an event-sourced CQRS framework for .NET.

The idea is to keep the domain logic small:

  • decide: turns commands into events
  • fold: rebuilds state from those events

FCQRS handles persistence, recovery, projections, sagas, snapshots, and read-your-writes. It runs on Akka.NET and supports both F# and C#.

Version 6 also includes completely rebuilt documentation, following the path from an incoming command to a queryable read model.

Project and documentation:
https://onurgumus.github.io/FCQRS/

I’d appreciate feedback on whether the new documentation makes the model clear to someone who hasn’t used FCQRS before.


r/fsharp 3d ago

Building a Readable DSL for Playwright Tests in F# | blog

Thumbnail jannikbuschke.de
17 Upvotes

r/fsharp 3d ago

F# weekly F# Weekly #29 — .NET 11 Preview 6 and Mibo 3.0

Thumbnail
sergeytihon.com
22 Upvotes

r/fsharp 10d ago

F# weekly F# Weekly #28 — Mibo 2.0, Fable 5.7, and Cast Shadows in F#

Thumbnail
sergeytihon.com
34 Upvotes

r/fsharp 11d ago

SuaveHooks — a webhook platform built entirely in F# with Suave

20 Upvotes

I just launched SuaveHooks — a webhook capture, inspection, transformation and routing platform built completely with Suave + F#.

Some highlights:

- Live tailing of webhooks over WebSockets

- Type-safe transforms written in F# (also C# and JS) running in an isolated process

- JSON rule-based transforms as a lighter option

- Multi-target forwarding (HTTP + S3, SQS, Kafka, Pub/Sub)

- Retries with exponential backoff + circuit breaker

- Full REST API + MCP server support

Site: https://suavehooks.com

Would love some feedback; like what features would make this more useful for you? Happy to answer any technical questions.


r/fsharp 15d ago

library/package Initial alpha release of Zigote - UI framework and game engine

Thumbnail
11 Upvotes

r/fsharp 17d ago

question Still worth learning F# 2026

54 Upvotes

Hi guys,

Probably another question like this but found none recently.

I'm a little upset with my current view on IT generalistic, ofc AI is not going anywhere besides up, but I feel I want to write more with my hands and new paradigms, maybe just AI as reviser, I would like to ask if learning F# in 2026 will make me able to make perfomance headed systems, and also gaming with something like Nu or Monogame, not a AAA game but something playable.


r/fsharp 17d ago

F# weekly F# Weekly #27, 2026 — Fable 5.5, SkiaSharp 4 & Orleans.FSharp 3.0

Thumbnail
sergeytihon.com
32 Upvotes

r/fsharp 23d ago

library/package Terminal.Gui.Elmish is back with V2 compat

24 Upvotes

A picture is worth a thousand words:

Source:
https://github.com/OnurGumus/Terminal.Gui.Elmish.V2


r/fsharp 24d ago

SIMD-friendly push streams in F#

27 Upvotes

I’ve been playing with SIMD-friendly push streams in F#.
Normal push streams are great for composition: the source pushes elements through map, fold, etc., and with enough inlining the overhead mostly disappears.

But they still push one element at a time, which is not ideal for SIMD.
So I tried pushing Vector<'T> blocks instead, with a scalar path for the tail.

The pipeline stays generic and composable, but in my benchmark it runs around handwritten SIMD speed.

Small thing, but it made me happy: write the abstraction clearly, then make it vanish.


r/fsharp 25d ago

F# weekly F# Weekly #26, 2026 — Fable REPL on BEAM & WebSharper 10.1

Thumbnail
sergeytihon.com
23 Upvotes

r/fsharp 29d ago

Interesting Copilot instructions found in the F# repo

34 Upvotes

The dotnet team writing Copilot instructions like an angry senior developer who's tired of everyone's excuses is peak engineering culture. 😂


r/fsharp Jun 21 '26

showcase I built a small Qwen voice agent orchestrated in F# / Fable

25 Upvotes

I built a rough browser-based voice agent where most of the orchestration is in F#.

You can talk to it, interrupt it while it is speaking, and ask it to edit live page state through tool calls. For example, the shared notes box on the page can be read and updated by the agent.

The interesting F# part is that the same codebase targets:

  • the browser side via Fable
  • the Python side via Fable.Python
  • shared protocol/types between the two, so the frontend and backend do not drift apart

There are two demo backends:

  • CPU edition, slower but cheaper
  • GPU edition, A100-backed and faster, but temporary because renting an A100 is not exactly free :)

Source is closed for now because this is part of a larger experiment, but I wanted to share it here since F# felt like a really nice fit for this kind of AI orchestration: async flows, state transitions, tool calls, and typed protocol boundaries.

Also, credit to Dag Brattli for the Fable.Python work. That made this experiment much more practical.

Demo: https://novian.works/voice_gpu

Please keep sessions short if you try the GPU version. I will probably only keep it online for a few days.

Sample video:

https://reddit.com/link/1ubp4le/video/ngpealpr5p8h1/player


r/fsharp Jun 20 '26

F# weekly F# Weekly #25, 2026 — Expecto 11.1 & Fable 5.3

Thumbnail
sergeytihon.com
27 Upvotes

r/fsharp Jun 17 '26

question Where to Find F# Jobs on 2026

19 Upvotes

r/fsharp Jun 17 '26

video/presentation Introducing F#/Elm to a C#/JS organization - hazards and wins by David Eduardo Mellum

Thumbnail
youtu.be
38 Upvotes

Want to introduce functional programming into your own organization? Learn from our successes and failures! At the Norwegian insurance company Frende Forsikring we’ve introduced F# and Elm and lived with both of them long enough to call it a long term relationship.

We’ll start looking at the introduction. From fast moving exploration in a single team, to structured validation with other tech leads and careful moving to production.

Beyond that we'll be looking what happens in the years afterwards. Does functional programming help hiring? Are there less bugs? What is the biggest hurdle in spreading adoption to new teams?


r/fsharp Jun 13 '26

F# weekly F# Weekly #24, 2026 — Fable 5.2, Expecto 11 & .NET 11 Preview 5

Thumbnail
sergeytihon.com
34 Upvotes

r/fsharp Jun 12 '26

question Why does it work?

16 Upvotes

```fsharp type Monoid<'t> = static abstract member Empty: 't static abstract member Append: 't -> 't -> 't

[<Struct>] type Vector3= {X: float Y: float Z: float}

interface Monoid<Vector3> with
    static member Empty = {X=0.0; Y=0.0; Z=0.0}
    static member Append (v1: Vector3) (v2: Vector3) =
        {X=v1.X + v2.X; Y=v1.Y + v2.Y; Z= v1.Z + v2.Z}

// Note the type constraint here let mempty<'t when Monoid<'t>> = 't.Empty let mappend<'t when Monoid<'t>> x y = 't.Append x y

printfn "%A" (mappend {X=1.0; Y=2.0; Z=3.0} mempty) ```

The type notation was suggested by the inline suggestions and it actually works. I tried some other forms like 't when Monoid<int> but only 't when Monoid<'t> works.

I suppose it should be <'t when 't :> Monoid<'t>> or even longer <'m, 't when 'm :> Monoid<'t>>.

It's good to know one can write it like this but I've never seen it mentioned in the docs (maybe not yet?).

The project file is also clean. It works even without setting the language version to Preview.

"It works I don't know why"


r/fsharp Jun 09 '26

question Does anyone know what happened to fsharpest.xyz?

8 Upvotes

Hello, The website currently seems to be down or unreachable from my side. I’m wondering whether this is a temporary outage, a DNS/hosting issue, or whether the site has been discontinued permanently. Any information would be appreciated.

I see. It went also out of business: https://github.com/fsprojects/awesome-fsharp/blob/main/ARCHIVE.md

So probably some of the moderators remove that link from the resources linked here.


r/fsharp Jun 06 '26

F# weekly F# Weekly #23, 2026 – Wolverine/Marten F# Improvements

Thumbnail
sergeytihon.com
26 Upvotes

r/fsharp Jun 05 '26

library/package TapeSim – Practice Reading the Tape

Thumbnail
tapesim.app
11 Upvotes

I made this replay simulator desktop app as a part of the Building The Trading Edge playlist on Youtube. It's my first commercial product. I needed a replay simulator that could do full tick-by-tick order book replay on US equities and when I couldn't find any, I made my own in F# + Avalonia.

If any of you ever wanted to learn to daytrade stocks, use this to practice before risking any money first.

TapeSim itself is in a private repo, but I do have the older version of its viewer in my Trading Edge repo. And even though the repo itself is private I've screencast its entire development (though not all vids are out yet at the time of writing this post.)


r/fsharp Jun 04 '26

question formatting question from a newb

9 Upvotes

Just starting out with F# and I'm enjoying all the whitespace but to my newbie eyes I think I've spotted an inconsistency in how fantomas formats. Maybe somebody can explain it.

// function that takes param of int*int
let myFun (x, y) = x + y

let r = myFun (1, 2) // <--- fantomas formats with space before tuple, makes sense

let dict = new Dictionary<int, int>()

// dict.Add takes a single tuple of int*int, just like the above function
dict.Add (1, 2) // <--- looks right, takes a tuple

// fantomas doesn't like the clarity and smushes it, why?
dict.Add(1, 1) // <--- yuck, now it looks like a function call with two arguments in another language

r/fsharp Jun 02 '26

showcase Built an E2E ML Pipeline (Titanic) with Polars.FSharp and ML.NET

18 Upvotes

Hi everyone,

Just wanted to share a compact, end-to-end Machine Learning script on the Titanic dataset. One thing for sure is that writing F# makes me happy.

Github: https://github.com/ErrorLSC/Polars.NET-Cookbook

Performance:

  • Execution Time: ~1.07 seconds (Data prep + GBDT training + Evaluation + Inference + Export).
  • Metrics: Accuracy: 77.71%, AUC: 0.8324
  • GC gen0: 3, gen1: 3, gen2: 3

```fsharp

time "on" // Enable timer

r "nuget: Polars.FSharp, 0.5.0"

r "nuget: Polars.NET.Core, 0.5.0"

r "nuget: Polars.NET.Native.linux-x64, 0.5.0"

r "nuget: FSharp.Data"

r "nuget: Microsoft.ML"

r "nuget: Microsoft.ML.FastTree"

r "nuget: Polars.NET.ML, 0.5.0"

open FSharp.Data open Polars.FSharp open Polars.NET.ML.DataView open Polars.NET.ML.FSharpExtensions open Microsoft.ML open Microsoft.ML.Data

// Define file paths for the Kaggle Titanic dataset [<Literal>] let trainPath = "train.csv"

[<Literal>] let testPath = "test.csv"

// Use FSharp.Data CsvProvider extract schema names type train = CsvProvider<trainPath>

let schema = Unchecked.defaultof<train.Row>

// Configure Polars formatting options for console output pl.setEnvVar "POLARS_FMT_MAX_COLS" "15" pl.setEnvVar "POLARS_FMT_MAX_ROWS" "10"

// List of name prefixes to keep; less frequent ones will be categorized as "Rare" let whiteList = ["Mr";"Mrs";"Master";"Miss"]
/// Step 1: Base Feature /// Extracts name prefixes, handles missing values, and derives initial structural features. let addBaseFeature(df:DataFrame) = df // Extract title (e.g., "Mr.", "Miss.") from the Name column |> pl.withColumn ((pl.col (nameof schema.Name)).Str.Extract(",\s+(?:[A-Za-z]+\s+)*([A-Za-z]+.)").Str.StripSuffix "." |> pl.alias "Prefix")

|> pl.withColumns([
    // Combine sibling/spouse and parent/child counts into FamilySize metric
    pl.col (nameof schema.SibSp) + pl.col (nameof schema.Parch) + pl.lit 1 
        |> pl.alias "FamilySize"

    // Fill missing Embarked ports with the most common port 'S'
    pl.col(nameof schema.Embarked).FillNull(pl.lit "S")

    // Group rare titles into a single "Rare" category to reduce cardinality
    pl.when' (pl.col("Prefix").IsIn(pl.lit(whiteList).Implode())) 
        |> pl.then'(pl.col "Prefix") 
        |> pl.otherwise(pl.lit "Rare") 

    // Extract the deck letter from the Cabin string (e.g., "C123" -> "C")
    pl.col(nameof schema.Cabin).Str.Extract("^([A-Za-z]+)").FillNull(pl.lit "Unknown") 
        |> pl.alias "Deck"    

    // Log-transform Fare to normalize its highly skewed distribution
    pl.col(nameof schema.Fare).FillNull(pl.lit 0).Log1p() 
        |> pl.alias "LogFare“   

    // Create a specific domain feature: IsMother
    pl.when' (pl.col (nameof schema.Sex) .== pl.lit "female" 
        .&& (pl.col (nameof schema.Age) .> pl.lit 18) 
        .&& (pl.col (nameof schema.Parch).> pl.lit 0))
        |> pl.then'(pl.lit 1)
        |> pl.otherwise(pl.lit 0)
        |> pl.alias "IsMother"  

    // Separate alphabetical ticket prefixes from pure numbers
    pl.col(nameof schema.Ticket)
        .Str.Extract("^([A-Za-z./]+[0-9]*)")
        .FillNull(pl.lit "NumOnly")
        |> pl.alias "TicketPrefix"
    ])

    // Drop redundant source columns
    |> _.Drop(nameof schema.Name,
            nameof schema.SibSp,
            nameof schema.Parch,
            nameof schema.Cabin,
            nameof schema.Fare)

/// Step 2: Aggregation - Calculate Median Age per Title/Sex group let calGroupPrefix(df:DataFrame) = df |> pl.groupBy [pl.col "Prefix";pl.col(nameof schema.Sex)] |> pl.agg [ [nameof schema.Age] |> pl.median |> pl.alias "AgeMedian"] |> pl.sortAscending [pl.col "Prefix";pl.col (nameof schema.Sex)]

/// Step 3: Aggregation - Calculate Group Size based on shared Ticket numbers let calTicketGroupSize(df:DataFrame) = df |> pl.groupBy [pl.col(nameof schema.Ticket)] |> pl.agg [ pl.len() |> pl.alias "TicketGroupSize" ]

/// Step 4: Advanced Feature Engineering & Imputation /// Joins aggregate metrics back to the main DataFrame, bucketizes age, and casts numeric cols to single type let addExtraFeature(groupPrefix) (ticketGroupSize) (df:DataFrame) = df |> pl.joinOn groupPrefix [pl.col "Prefix";pl.col (nameof schema.Sex)] JoinType.Left |> pl.joinOn ticketGroupSize [pl.col (nameof schema.Ticket)] JoinType.Left |> pl.withColumn(pl.col(nameof schema.Age).Coalesce [pl.col "AgeMedian"]) |> pl.withColumn(pl.col(nameof schema.Age).Cut [12;19;39;59] |> _.ToPhysical() |> pl.alias "AgeBucket") |> pl.withColumn(pl.col "FamilySize" .== pl.lit 1L |> pl.castWithNetType<int> |> pl.alias "IsAlone") |> _.Drop("AgeMedian",nameof schema.Ticket,nameof schema.Age) |> pl.withColumn(pl.cs.numeric().ToExpr() |> pl.castWithNetType<single>)

/// Step 5: Finalize Training Data /// Formats the target label column as Boolean as expected by ML.NET Binary Classification let trainFinalize(df:DataFrame) = df |> pl.withColumns([ pl.col "Survived" |> pl.castWithNetType<bool> |> pl.alias "Label"] ) |> _.Drop("Survived",nameof schema.PassengerId)

// Execute Pipeline: Training Data Preparation let dfTrainBase = DataFrame.ReadCsv trainPath |> addBaseFeature let trainGroupPrefix = dfTrainBase |> calGroupPrefix let trainTicketGroupSize = dfTrainBase |> calTicketGroupSize let dfTrainFinal = dfTrainBase |> addExtraFeature trainGroupPrefix trainTicketGroupSize |> trainFinalize

// --- ML.NET Machine Learning Pipeline --- let mlContext = MLContext(seed = 42)

// Convert Polars DataFrame into ML.NET IDataView let fullData = dfTrainFinal.AsDataView()

// Split data into 80% Train and 20% Validation sets let splits = mlContext.Data.TrainTestSplit(fullData, testFraction = 0.2)

// Define categorical columns that require encoding let categoricalCols = [| nameof schema.Sex; nameof schema.Embarked; "Prefix"; "Deck"; "TicketPrefix" |] let encodedCols = categoricalCols |> Array.map (fun c -> c + "_Encoded")

// Filter out features that are purely numeric let numericCols = dfTrainFinal.Columns |> Array.filter (fun c -> c <> "Label" && not (Array.contains c categoricalCols))

// Combine numeric and newly encoded features for the trainer let allFeatures = Array.append numericCols encodedCols

// Map original categorical columns to One-Hot Encoded column outputs let ohePairs = categoricalCols |> Array.zip encodedCols |> Array.map (fun (enc, raw) -> InputOutputColumnPair(enc, raw))

// Helper function to avoid explict interface conversion let inline append estimator (chain: EstimatorChain<#ITransformer>) = chain.Append estimator

// Build the ML.NET training pipeline let pipeline = EstimatorChain<ITransformer>() |> append (mlContext.Transforms.Categorical.OneHotEncoding ohePairs) |> append (mlContext.Transforms.Concatenate("Features", allFeatures)) |> append (mlContext.BinaryClassification.Trainers.FastTree())

// Train the model let model = pipeline.Fit splits.TrainSet

// Evaluate performance on the validation split let predictions = model.Transform splits.TestSet let metrics = mlContext.BinaryClassification.Evaluate(predictions, labelColumnName = "Label")

// Print out out-of-sample performance validation metrics printfn "=== Training Results ===" printfn "Accuracy: %.2f%%" (metrics.Accuracy * 100.0) printfn "AUC: %.4f" metrics.AreaUnderRocCurve printfn "F1 Score: %.4f" metrics.F1Score

// --- Inference Pipeline & Submission Generation --- let testPredictions = DataFrame.ReadCsv testPath |> addBaseFeature |> addExtraFeature trainGroupPrefix trainTicketGroupSize |> _.AsDataView() |> model.Transform

// ML.NET will generate duplicated column names in some cases, we can check and decide which columns should be exported // testPredictions.Schema |> Seq.iter (fun col -> printfn $"{col.Name} : {col.Type}") let keepCols = [| nameof schema.PassengerId; "PredictedLabel"|] let exportCols = [| nameof schema.PassengerId; nameof schema.Survived|] // Extract predictions, transform columns back to Polars, and format for Kaggle submission mlContext.Transforms.SelectColumns(keepCols) .Fit(testPredictions) .Transform(testPredictions) .ToDataFrame() // Map over seq<Series>, casting to int and renaming according to Kaggle's schema |> Seq.mapi (fun i s -> s.Cast<int>().Rename(exportCols.[i])) |> pl.dataframe |> _.WriteCsv("submission.csv",quoteStyle=QuoteStyle.Never)

time "off"

// === Training Results === // Accuracy: 77.71% // AUC: 0.8324 // F1 Score: 0.7176 // Real: 00:00:01.074, CPU: 00:00:02.401, GC gen0: 3, gen1: 3, gen2: 3

```


r/fsharp May 31 '26

F# weekly F# Weekly #22, 2026 – Fable 5.1 & Mibo.Raylib 1.0

Thumbnail
sergeytihon.com
35 Upvotes