Morphir.SDK.FSharp 0.4.0-alpha.3

Prefix Reserved
This is a prerelease version of Morphir.SDK.FSharp.
There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package Morphir.SDK.FSharp --version 0.4.0-alpha.3
                    
NuGet\Install-Package Morphir.SDK.FSharp -Version 0.4.0-alpha.3
                    
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="Morphir.SDK.FSharp" Version="0.4.0-alpha.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Morphir.SDK.FSharp" Version="0.4.0-alpha.3" />
                    
Directory.Packages.props
<PackageReference Include="Morphir.SDK.FSharp" />
                    
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 Morphir.SDK.FSharp --version 0.4.0-alpha.3
                    
#r "nuget: Morphir.SDK.FSharp, 0.4.0-alpha.3"
                    
#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 Morphir.SDK.FSharp@0.4.0-alpha.3
                    
#: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=Morphir.SDK.FSharp&version=0.4.0-alpha.3&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Morphir.SDK.FSharp&version=0.4.0-alpha.3&prerelease
                    
Install as a Cake Tool

Morphir.SDK.FSharp

F# runtime library providing Morphir SDK types and functions for generated F# code.

Overview

Morphir.SDK.FSharp provides the F# runtime used by generated Morphir code. Its namespace and runtime assembly remain Morphir.SDK.

Installation

dotnet add package Morphir.SDK.FSharp

Migrating existing consumers

Replace the Morphir.SDK package reference with Morphir.SDK.FSharp, using the same release version. Keep open Morphir.SDK and existing generated source unchanged. Project-reference consumers use src/Morphir.SDK.FSharp/Morphir.SDK.FSharp.fsproj.

The legacy Morphir.SDK package remains installable. It depends on the exact matching canonical package version and contains no runtime assembly. Installing either package or both resolves the same Morphir.SDK.dll. Do not reference the legacy pack-only project as a runtime project dependency.

The migration preserves the Morphir.SDK.Order type identity, existing public entry points and F# aliases. Aliases such as Maybe<'a>, Dict<'k,'v>, LocalDate and LocalTime retain their FSharp.Core/BCL types. They do not become new CLR wrapper or forwarding types.

Select the FSharp.Core version required by the package. The captured baseline uses 11.0.100; a compiler's lower implicit dependency can otherwise cause NU1605 and runtime loading failures. For an SDK whose implicit version is lower:

<PropertyGroup>
  <FSharpCoreImplicitPackageVersion>11.0.100</FSharpCoreImplicitPackageVersion>
  <WarningsAsErrors>$(WarningsAsErrors);NU1605</WarningsAsErrors>
</PropertyGroup>

The bounded conformance contract and migration corrections are tracked in issue #465. Packaging identity preservation does not claim that behavior corrections replace old code already inlined into an existing binary.

Covered behavior changes

The package migration also corrects the cases admitted by the shared morphir-dotnet-sdk-v1 / dotnet-sdk-v1 contract. It does not certify every SDK operation or every host representation.

  • Dict.union left right now keeps left's value for a shared key. Previously the second dictionary won. For example, unioning [("rate", 1)] with [("rate", 9)] now yields [("rate", 1)]. Both operands remain unchanged.
  • Int32 Basics.add, subtract, multiply and negate report overflow instead of wrapping. The generic public entry points remain available.
  • Basics.remainderBy -1 System.Int32.MinValue and Basics.modBy with those operands return zero. Previously the host remainder instruction overflowed.
  • Basics.compare compares direct String operands by ordinal UTF-16 code units, without culture or normalization. For example, decomposed "e\u0301" sorts before precomposed "\u00e9"; they no longer compare equal through collation.
  • Decimal.toString uses invariant positional text and removes fractional trailing zeros. 1.50M becomes "1.5", and scaled zero becomes "0". Decimal.fromString parses this canonical representation first under every culture, then tries the caller's culture for other input. For example, under de-DE, both "1.5" and localized "1,5" mean 1.5M. Ambiguous dot-separated input follows canonical decimal-point semantics rather than localized grouping. Parsing remains a native API compatibility check outside the frozen inventory.
  • Additive Basics.round, floor, ceiling and truncate convert Float to Int32 with non-finite and overflow checks. Round resolves halves toward positive infinity: round -2.5 = -2.
  • Additive Decimal.div returns None for a zero divisor and Some quotient otherwise. Existing Decimal.divide still returns decimal and retains its existing zero-divisor exception behavior. Exact terminating Decimal96 division is covered; nonterminating or unrepresentable results are separate exclusions.

Already compiled inline calls can retain their old arithmetic, union or formatting behavior. Recompile consumers to use corrected inline bodies. Installing the canonical runtime alone preserves their type loading; it cannot replace code embedded in another assembly.

Features

  • Type Aliases: Morphir types mapped to idiomatic F# types (e.g., Maybe<'a> = Option<'a>)
  • Standard Library: F# SDK functions; the shared contract defines the bounded conformance inventory
  • Immutable Collections: Dict (Map), Set, List operations
  • Date/Time Support: LocalDate (DateOnly), LocalTime (TimeOnly)
  • AOT Compatible: No reflection, ready for Native AOT compilation

Modules

Core Types

  • Basics: Fundamental operations (Order type, comparison, arithmetic)
  • Maybe: Optional values (Option<'a> alias with helper functions)
  • Result: Error handling (Result<'v, 'e> with combinators)

Collections

  • List: List operations and extensions
  • Dict: Immutable dictionaries (Map<'k, 'v> alias)
  • Set: Set operations
  • Tuple: Tuple helper functions

Primitives

  • String: String manipulation functions
  • Int: Integer operations
  • Bool: Boolean logic
  • Char: Character operations
  • Decimal: Decimal arithmetic

Date/Time

  • LocalDate: Date without timezone (DateOnly alias)
  • LocalTime: Time without timezone (TimeOnly alias)

Usage Examples

Maybe (Option)

open Morphir.SDK

let user = { name = "Alice"; email = Some "alice@example.com" }

let emailDomain =
    user.email
    |> Maybe.map (String.split "@")
    |> Maybe.andThen List.last
    |> Maybe.withDefault "unknown"

Result

open Morphir.SDK

let validateAge age =
    if age < 0 then
        Result.err "Age cannot be negative"
    elif age > 150 then
        Result.err "Age is too high"
    else
        Result.ok age

let processAge =
    validateAge 25
    |> Result.map (fun age -> age + 1)
    |> Result.withDefault 0

List

open Morphir.SDK

let numbers = [1; 2; 3; 4; 5]

let result =
    numbers
    |> List.map (fun x -> x * 2)
    |> List.filter (fun x -> x > 5)
    |> List.sum
// result = 18

Dict

open Morphir.SDK

let scores =
    Dict.fromList [
        ("Alice", 95)
        ("Bob", 87)
        ("Charlie", 92)
    ]

let aliceScore = Dict.get "Alice" scores
// Some 95

let updatedScores = Dict.insert "David" 88 scores

LocalDate

open Morphir.SDK

let today = LocalDate.today()
let nextWeek = LocalDate.addDays 7 today
let formatted = LocalDate.toIsoString nextWeek
// "2025-01-07"

let parsed = LocalDate.fromIsoString "2025-12-31"
// Some (LocalDate(2025, 12, 31))

Relationship to morphir-elm

This library provides F# implementations of types and functions from morphir-elm's SDK. The API is designed to maintain semantic compatibility while leveraging F#'s native types and idioms.

Key Mappings

morphir-elm Morphir.SDK (F#) F# Native Type
Maybe a Maybe<'a> Option<'a>
Result e v F# Result<'v, 'e> Result<'v, 'e>
List a 'a list List<'a>
Dict k v Dict<'k, 'v> Map<'k, 'v>
Set a Set<'a> Set<'a>
LocalDate LocalDate DateOnly
LocalTime LocalTime TimeOnly

Design Principles

  1. Type Aliases Over Wrappers: Use F# built-in types directly when possible
  2. Zero-Cost Abstractions: No runtime overhead for type conversions
  3. Idiomatic F#: Follow F# conventions and naming patterns
  4. AOT Compatibility: No reflection or dynamic code generation
  5. Semantic Compatibility: Maintain morphir-elm behavior

Version Compatibility

  • Canonical package: Morphir.SDK.FSharp
  • Compatibility package: Morphir.SDK, at the matching version
  • .NET: 10.0+
  • F#: 9.0+
  • morphir-elm: Compatible with Morphir IR 2.x and 3.x

Contributing

This library is part of the morphir-dotnet project. For contributions, please see the main repository's CONTRIBUTING.md.

License

Apache License 2.0 - See LICENSE in the morphir-dotnet repository.

Product Compatible and additional computed target framework versions.
.NET 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Morphir.SDK.FSharp:

Package Downloads
Morphir.SDK

Compatibility package for the Morphir F# runtime. Depends on Morphir.SDK.FSharp, which retains the Morphir.SDK namespace and runtime assembly identity.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.5.0-alpha.3 49 11/14/2026
0.5.0-alpha.2 46 11/13/2026
0.5.0-alpha.1 46 11/13/2026
0.4.0-alpha.5 48 11/13/2026
0.4.0-alpha.4 50 11/10/2026
0.4.0-alpha.3 58 11/9/2026
0.4.0-alpha.2 59 11/8/2026
0.4.0-alpha.1 57 11/7/2026

### Added

- Docs: [Compile F# to Morphir IR](https://finos.github.io/morphir-dotnet/docs/getting-started/compile-fsharp/), a five-minute start, and two tutorials: car rental pricing from F# to C#, and compiling F# with the native `morphir` executable. The finished example is in `examples/car-rental-pricing` ([#499](https://github.com/finos/morphir-dotnet/issues/499))
- Releases publish the F# frontend extension `Morphir.Frontend.FSharp.Extension` (command `morphir-fsharp-frontend`) to NuGet next to `Morphir.Tool` ([#499](https://github.com/finos/morphir-dotnet/issues/499))
- `morphir compile`: compiles F# sources to Morphir IR with the in-process F# frontend of the dotnet tool `dotnet-morphir`. Sources come from `--input` files in order, one `--input` directory in lexical order, `--project-file`, `[frontend.<language>].project_file`, or `project.source_directory`. `--ir-version` and `ir.format_version` keep the exact format version, for example `4.0.0`. `--json` writes one report with setting origins; `--json-lines` writes the compile events and ends with the report. Human mode writes each stage's diagnostics to stderr when the stage completes. Exit codes are 0, 1, 2 and 130. The AOT executable `morphir` has no built-in F# frontend; it compiles F# through the MEP guest with `--extension` ([#499](https://github.com/finos/morphir-dotnet/issues/499))
- `morphir compile --extension <command, path or .dll>` and `[frontend.<language>].extension`: run a frontend as a MEP `0.2.0-draft.1` guest process. The AOT `morphir` compiles F# through `morphir-fsharp-frontend` for IR versions 1 to 4; classic versions 1 and 2 are compiled at `3.0.0` and written at the requested version. Extension failures are exit code 2 with `extension.unavailable`, `extension.not_admitted`, `extension.tuple_unavailable`, `extension.protocol_error` or `extension.timeout` ([#499](https://github.com/finos/morphir-dotnet/issues/499))
- `morphir compile --target <id> --generate-output <dir> [--option KEY=VALUE]`: continue into code generation on the compiled IR, as `generate` does ([#499](https://github.com/finos/morphir-dotnet/issues/499))
- Compile task records: `compile` writes `compile.json` under the out root (`--out-dir`, `MORPHIR_OUT_DIR` or `workspace.out_dir`), holds `compile.lock` while it runs, leaves a tombstone when it fails, and installs a copy of the IR with `-o/--output` without changing files it did not write ([#499](https://github.com/finos/morphir-dotnet/issues/499))
- `morphir generate` without `--input` reads the IR of the most recent compile of the project, and fails with `compile.record_missing` when there is none ([#499](https://github.com/finos/morphir-dotnet/issues/499))
- `morphir.unrepresentable`: a classic IR version that cannot hold a value of the model (an integer outside the 64-bit range, or a character outside the Basic Multilingual Plane) fails with exit code 1 and writes no document ([#499](https://github.com/finos/morphir-dotnet/issues/499))
- Configuration: `ir.format_version` accepts a SemVer string such as `"4.1.0-draft.1"`, and every `[frontend.<language>]` table accepts `project_file` and `extension` ([#499](https://github.com/finos/morphir-dotnet/issues/499))
- Generation JSON reports: a diagnostic can have an optional `range`. Reports without ranges are unchanged ([#499](https://github.com/finos/morphir-dotnet/issues/499))
- `Morphir.Mep` foundation: parsed protocol versions, generated describe params, bounded `Content-Length` framing, pinned-contract tests and a C# consumer verified under Native AOT ([#512](https://github.com/finos/morphir-dotnet/issues/512)).
- Myriad System.Text.Json record wire rules: typed scalar, empty-collection and static-provider defaults for missing members, an explicit JSON-literal fallback, empty-collection omission, flattened extension data, exact versioned-member migration and critical-path allowlists. Existing encodings remain unchanged unless annotated.
- `Morphir.FSharp.Annotations`: `[<MorphirName("...")>]` sets the Morphir name of an F# value, type, union case, record field or module, in canonical Morphir name syntax, for example to name an operator. For `netstandard2.0`, with no dependency ([#499](https://github.com/finos/morphir-dotnet/issues/499))
- `morphir-fsharp-frontend`: the F# frontend as a MEP `0.2.0-draft.1` guest, packaged as the framework-dependent dotnet tool `Morphir.Frontend.FSharp.Extension`. It serves `morphir.frontend.compileModel` with JSON model tuples for IR `4.0.0` and `3.0.0` ([#499](https://github.com/finos/morphir-dotnet/issues/499))
- `Morphir.Mep` frontend messages: the `compileModel` request types, the result sum, the reserved options, the request check, RFC 8785 canonical JSON and the request context digest ([#499](https://github.com/finos/morphir-dotnet/issues/499))
- The F# generator writes more Morphir SDK operations: `Basics.integerDivide`, `negate`, `abs`, `min`, `max`, `and`, `or` and `not`; `Decimal.add`, `sub` and `mul`; `List.singleton`, `append`, `length`, `head` and `cons`; `Maybe.andThen` and `Result.andThen`; `String.append`, `length` and `join`; and the `Int.Int64` type ([#499](https://github.com/finos/morphir-dotnet/issues/499))

### Changed

- The F# generator gives each union case field the name of its constructor argument (`Large of seats: int`), so the names stay in the generated F#. A case whose argument names repeat keeps positional fields ([#499](https://github.com/finos/morphir-dotnet/issues/499))
- The F# generator renames a parameter or local value (`__morphirLocal_` prefix) only when it hides a module value that its definition uses. It renamed every local with the name of a module value before ([#499](https://github.com/finos/morphir-dotnet/issues/499))
- The F# generator writes `List.cons` with two arguments as `head :: tail`, not as an applied lambda ([#499](https://github.com/finos/morphir-dotnet/issues/499))

### Fixed

- Custom Myriad plugin scaffolds build and load with Myriad 0.85.0, using Fantomas syntax types and copied runtime dependencies. Myriad and its coupled code generation dependencies now have their own Renovate update group, including the bundled host pins.