diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
index c643711..d1a67bc 100644
--- a/.github/copilot-instructions.md
+++ b/.github/copilot-instructions.md
@@ -16,6 +16,7 @@
│ ├── AssemblyInfo.fs – assembly metadata
│ ├── ProcessingOptions.fs – processing configuration helpers
│ ├── Observable.fs – observable wrappers/extensions
+│ ├── ObservableOption.fs – `option` variants of the `Observable` functions (`choose`)
│ ├── ObservableFactories.fs – factories reachable as `Observable.xxx` (`ofSeq`)
│ ├── AsyncObservable.fs – async observable helpers
│ └── TaskObservable.fs – task-based observable helpers
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9d4f319..9ed6f2f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,13 +9,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
+- `Observable.choose` as a function of the `Observable` module; like every function of the library that works with optional values it takes a `voption` chooser, and the `option` variant is `ObservableOption.choose`
- `Observable.ofSeq (items)` and `Observable.ofSeq (items, cancellationToken)`, reachable after `open FSharp.Control.R3`
- `rxqueryWith cancellationToken` and `RxQueryBuilder (cancellationToken)` to cancel the query operators that return a task
### Changed
- **Breaking:** `AwaitOperationConfiguration` cases are prefixed with `Await` (`AwaitSequential`, `AwaitParallel 4`, ...) so they no longer collide with `System.Threading.Tasks.Parallel`
-- **Breaking:** the `Observable.Extensions` type is replaced by `Observable.ofSeq`
+- **Breaking:** the `OptionExtensions`/`ValueOptionExtensions` types are replaced by `Observable.choose`, which takes a `voption` chooser, and `ObservableOption.choose` for `option`; the `Observable.Extensions` type is replaced by `Observable.ofSeq`
+- **Breaking:** after `open FSharp.Control.R3`, `Observable.choose` is the R3 function, like `Observable.map` and `Observable.filter` already were; use `Microsoft.FSharp.Control.Observable.choose` for `IObservable` and F# events
- **Breaking:** after `open FSharp.Control.R3`, the bare type name `Observable` refers to `FSharp.Control.R3.ObservableFactories.Observable`; write `open type R3.Observable` and extend `R3.Observable` by its full name
- **Breaking:** `rxquery` `sumBy` adds values of the projected type: it requires `(+) : 'Value * 'Value -> 'Value`
- `mapAsync` and `iterAsync` validate the concurrency limit of the options when they are called
diff --git a/src/FSharp.Control.R3/FSharp.Control.R3.fsproj b/src/FSharp.Control.R3/FSharp.Control.R3.fsproj
index cb270e1..d60e00f 100644
--- a/src/FSharp.Control.R3/FSharp.Control.R3.fsproj
+++ b/src/FSharp.Control.R3/FSharp.Control.R3.fsproj
@@ -19,6 +19,7 @@
+
diff --git a/src/FSharp.Control.R3/Observable.fs b/src/FSharp.Control.R3/Observable.fs
index 3078610..551a998 100644
--- a/src/FSharp.Control.R3/Observable.fs
+++ b/src/FSharp.Control.R3/Observable.fs
@@ -74,32 +74,24 @@ let inline take (count : int) (source) = ObservableExtensions.Take (source, coun
/// Filters the observable elements of a sequence based on a predicate
let inline where ([] f : 'T -> bool) source = ObservableExtensions.Where (source, f)
-open System.Runtime.CompilerServices
-
-[]
-module OptionExtensions =
-
- []
- type Observable private () =
-
- /// Applies the given function to each element of the observable. Returns
- /// a sequence comprised of the results "x" for each element where
- /// the function returns Some(x)
- []
- static member choose f = map f >> where Option.isSome >> map Option.get
-
-[]
-module ValueOptionExtensions =
-
- []
- type Observable private () =
-
- /// Applies the given function to each element of the observable. Returns
- /// a sequence comprised of the results "x" for each element where
- /// the function returns ValueSome(x)
- []
- static member choose f = map f >> where ValueOption.isSome >> map ValueOption.get
+// choose is a module function rather than a static member of an extension type: a module function here shadows
+// FSharp.Core's Observable.choose, which would otherwise win the name resolution of Observable.choose.
+// Like every function of the library that works with optional values it takes value options, which do not allocate;
+// the variants that take options live in the ObservableOption module.
+///
+/// Applies the chooser to each element and emits the values of the results
+/// that hold one.
+///
+/// The variant that takes an chooser is
+/// .
+///
+///
+let inline choose ([] chooser : 'T -> 'R voption) (source : Observable<'T>) =
+ source
+ |> map chooser
+ |> filter ValueOption.isSome
+ |> map ValueOption.get
/// The query builder and its instances.
[]
diff --git a/src/FSharp.Control.R3/ObservableOption.fs b/src/FSharp.Control.R3/ObservableOption.fs
new file mode 100644
index 0000000..7531e28
--- /dev/null
+++ b/src/FSharp.Control.R3/ObservableOption.fs
@@ -0,0 +1,23 @@
+///
+/// Variants of the functions of that work with
+/// instead of .
+///
+/// The functions of that work with optional values take value options,
+/// which do not allocate; use this module where options are already at hand.
+///
+///
+[]
+module FSharp.Control.R3.ObservableOption
+
+open R3
+open FSharp.Control.R3
+
+///
+/// Applies the chooser to each element and emits the values of the results
+/// that hold one.
+///
+let inline choose ([] chooser : 'T -> 'R option) (source : Observable<'T>) =
+ source
+ |> Observable.map chooser
+ |> Observable.filter Option.isSome
+ |> Observable.map Option.get