FSharp.Interop.Dynamic
7.0.0
dotnet add package FSharp.Interop.Dynamic --version 7.0.0
NuGet\Install-Package FSharp.Interop.Dynamic -Version 7.0.0
<PackageReference Include="FSharp.Interop.Dynamic" Version="7.0.0" />
<PackageVersion Include="FSharp.Interop.Dynamic" Version="7.0.0" />
<PackageReference Include="FSharp.Interop.Dynamic" />
paket add FSharp.Interop.Dynamic --version 7.0.0
#r "nuget: FSharp.Interop.Dynamic, 7.0.0"
#:package FSharp.Interop.Dynamic@7.0.0
#addin nuget:?package=FSharp.Interop.Dynamic&version=7.0.0
#tool nuget:?package=FSharp.Interop.Dynamic&version=7.0.0
FSharp.Interop.Dynamic
Test and coverage badges are the last green CI run on master. CI fails the PR if any test is skipped, line coverage drops below 77%, or branch coverage drops below 100%.
F# operators for the Dynamic Language Runtime. target?Name, target?Name <- value, and !?target are the F# spelling of C# dynamic, with piping and 'T option lookups.
Docs: fsprojects.github.io/FSharp.Interop.Dynamic
This library sits on Dynamitey.Community 4.0.0. It is the F# surface, not Dynamitey itself.
F# has no dynamic keyword. Use this when the member is not known at compile time: Expando/JSON bags, optional fields, method names from config, C# APIs that return dynamic, pythonnet, COM. Use ordinary F# when the type is in your project. Full argument: Why this library.
Version story
7.0.0 is the current package, and the last one that supports .NET Standard 2.0. That target is what reaches .NET Framework 4.6.1 through 4.8.1. It also builds for net10.0, on Dynamitey.Community 4.0.0. The namespace is still Dynamitey. The assembly is Dynamitey.Community. Dyn.namedArg, Dyn.staticContext, and Dyn.staticTarget return types from that assembly. A project that also references the Dynamitey 3.0.3 package gets CS0433.
A Framework application stays on 7.0.0. Framework 4.8.1 is still serviced with Windows. It does not get new BCL APIs. .NET Standard 2.0 freezes this library on the Framework 4.6.1 surface. A newer API is either skipped, or written twice. That is the tax. Dynamitey.Community #95 is the same decision for the dependency: 4.0.0 is its last netstandard2.0 release, and 5.0.0 (after .NET 11 is generally available) targets net10.0 and net11.0 only.
8.0.0 (#108, milestone) follows that. The target date is 10 November 2026, the scheduled .NET 11 general availability. It targets net10.0 and net11.0 and drops .NET Standard 2.0, once Dynamitey.Community 5.0.0 has dropped the same target. The gains are in that library, and this package takes them by referencing it:
- One copy of Dynamitey's call-site code. The
netstandard2.0branch,Guard.NotNull, and the attribute polyfills go away.ArgumentNullException.ThrowIfNullis on every target. - Ordinal hashing and
string.Contains(string, StringComparison). The Framework build cannot call those overloads, so it uses a different hash and threeIndexOfcalls. Microsoft.CSharpandSystem.Reflection.Emitleave Dynamitey's package graph. They are in the shared framework on .NET 10 and .NET 11. The more-than-14-argument path still uses Emit. It stops being a separate package.[RequiresUnreferencedCode]and[RequiresDynamicCode]are the real framework attributes on every Dynamitey build. The library stays DLR-based. It does not become trim-safe.
A warm dynamic call does not get faster. It is already a cached delegate.
6.0.0 was netstandard2.0 + net10.0 on Dynamitey 3.0.3, with Dyn.tryGet / Dyn.exists. That was a TFM break from 5.0.1.268 (net45 / netstandard1.6 / netstandard2.0).
Install (7.0.0)
dotnet add package FSharp.Interop.Dynamic
open FSharp.Interop.Dynamic
open FSharp.Interop.Dynamic.Operators // optional: ?+?, ?=?, …
Quick start
open System.Dynamic
open FSharp.Interop.Dynamic
let o = ExpandoObject()
o?Name <- "Ada"
let name: string = o?Name
let hello: string = "HelloWorld"?Substring(0, 5)
Annotate the result type when F# cannot infer it. Void CLR methods need unit:
let items = ResizeArray<string>()
let _: unit = items?Add("x")
Check a member without throwing
let present: string option = o |> Dyn.tryGet "Name" // Some "Ada"
let missing: string option = o |> Dyn.tryGet "NoSuch" // None
o |> Dyn.exists "Name" // true
o |> Dyn.exists "NoSuch" // false
Lookup is Dynamitey InvokeGet. A present null is still present. A present value that cannot convert to 'T still throws — that is not a miss.
Pipe through Dyn
o |> Dyn.set "Name" "Ada"
let name: string = o |> Dyn.get "Name"
let hello: string = "HelloWorld" |> Dyn.invokeMember "Substring" (0, 5)
Target is last so piping works.
Binary operators
open FSharp.Interop.Dynamic.Operators
let n: int = 5 ?+? 4
let f: float = 5 ?+? 3.5
let s: string = "Hello" ?+? " World"
[1; 2; 3; 4] |> List.reduce (?+?)
Direct invoke
let add3: int -> int = !?(+) 3
add3 4 // 7
What ? does
| You write | DLR |
|---|---|
target?Name when 'T is not a function |
InvokeGet |
target?Name when 'T is a function, then apply |
InvokeMember |
target?Name <- value |
InvokeSet |
!?target |
Invoke on the target itself |
That is why target?Foo(1, 2) is a method call: application forces a function type.
Dynamitey
7.0.0 references Dynamitey.Community 4.0.0, from dynamitey-community/dynamitey. The Dynamitey package id stopped at 3.0.3 on 8 November 2023. The namespace is still Dynamitey. More: Dynamitey.
You can still open Dynamitey for Build, Dynamic.Curry, DynamicObjects.Dictionary.
Caveats
- The DLR cannot see explicit interface members (same as C#
dynamic). - Not trim-safe or NativeAOT-safe.
- 15 or more arguments threw
TypeLoadExceptionon 6.0.0 (Dynamitey 3.0.3). 7.0.0 takes the fix in Dynamitey.Community 4.0.0. - C# optional parameters must all be passed. The DLR does not fill defaults.
tryGet/existson a null target areNone/false.Dyn.seton a null target still throwsNullReferenceException.- Do not build member names from untrusted input. SECURITY.md.
- Historical: .NET Core 2.0.0–2.0.2 broke
dynamicon nested types inside generics (#11). Current TFMs are fine.
Full list: Caveats.
Every F# snippet above is a test in Tests/ReadmeExamples.fs. pythonnet and SignalR sketches live on the why page and are not compiled here.
Build
Requires the .NET 10 SDK.
dotnet restore
dotnet build -c Release -warnaserror
dotnet test --project Tests/Tests.fsproj -c Release
Docs: dotnet tool install -g docfx --version 2.78.5, then dotnet build FSharp.Interop.Dynamic/FSharp.Interop.Dynamic.fsproj -c Release && docfx docfx/docfx.json.
Release: add the version's section to CHANGELOG.md, then tag v7.0.0 and push. Releasing.
Feedback and contributing
- Bugs and feature requests: open an issue. Include the F# and .NET versions and a minimal snippet.
- Security problems: do not open a public issue. Follow SECURITY.md.
- Code and docs: pull requests are welcome. CONTRIBUTING.md covers the process and what a PR needs before it merges.
Maintainers
fsprojects default: @fsprojectsgit.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. 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 is compatible. 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. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Dynamitey.Community (>= 4.0.0)
- FSharp.Core (>= 4.7.2)
- Microsoft.CSharp (>= 4.7.0)
-
net10.0
- Dynamitey.Community (>= 4.0.0)
- FSharp.Core (>= 4.7.2)
NuGet packages (7)
Showing the top 5 NuGet packages that depend on FSharp.Interop.Dynamic:
| Package | Downloads |
|---|---|
|
FCQRS
Package Description |
|
|
HOG.TezFs
Ultra high level F# Tezos library. |
|
|
Softellect.Sys
Softellect Sys Library provides primitive types and functionality needed by other Softellect packages. |
|
|
GMusicApi
Wrapper around https://github.com/simon-weber/gmusicapi |
|
|
FAkka.Server.Linux
Package Description |
GitHub repositories
This package is not used by any popular GitHub repositories.