-
Notifications
You must be signed in to change notification settings - Fork 10
Description
Overview
Create the Morphir.SDK F# runtime library that provides Morphir SDK types and functions for generated F# code. This is Phase 0 and a critical dependency that blocks all subsequent F# backend implementation phases.
Strategic Decision: Following morphir-scala and morphir-jvm patterns, we create a runtime library rather than pure type mapping. This provides:
- ✅ Consistent behavior across backends
- ✅ Versioning of SDK semantics
- ✅ Reusability from existing
main-archiveSDK code - ✅ Foundation for complex SDK types (Key, Aggregate, Rule)
Context for AI Agents
Repository: finos/morphir-dotnet
Epic: #363 F# Code Generation Backend
Phase: 0 (Foundation)
Priority: P0 (Critical - blocks #365, #366, #367, #368, #369, #370, #371, #372)
Key Resources:
- 📋 Morphir.SDK Library Plan - Detailed implementation guide
- 📋 Migration Assessment - Section 1: SDK Types
- 📋 Maturity Milestones - M0 definition
- 🔍 main-archive SDK - Existing implementation to leverage
- 🔍 morphir-elm SDK - Reference semantics
Technology Stack:
- .NET 10 / F# 9
- Testing: TUnit (same as other projects in repo)
- Package: NuGet (
Morphir.SDK 0.4.0-alpha)
Acceptance Criteria
Project Setup
- Create
src/Morphir.SDK/Morphir.SDK.fsproj(F# class library) - Create
tests/Morphir.SDK.Tests/Morphir.SDK.Tests.fsproj(TUnit) - Add both projects to
Morphir.slnxsolution - Configure NuGet package metadata (PackageId, Version, Authors, Description)
- Set TargetFramework to
net10.0
Core Modules (Phase 0.1 - Week 0.5)
Implement these modules with type aliases and function wrappers:
-
Basics.fs: Order type, comparison functions -
Maybe.fs: Type alias for Option<'a> + helper functions -
Result.fs: Type alias for Result<'v, 'e> + helper functions -
List.fs: Extensions to F# List module -
String.fs: Extensions to F# String type -
Int.fs: Extensions to int type -
Bool.fs: Minimal extensions (F# bool is sufficient) -
Char.fs: Extensions to char type
Collections & Date/Time (Phase 0.2 - Week 0.5)
-
Dict.fs: Type alias for Map<'k, 'v> + extensions -
Set.fs: Extensions to F# Set<'a> -
Tuple.fs: Tuple helper functions -
LocalDate.fs: Type alias for DateOnly (.NET 6+) -
LocalTime.fs: Type alias for TimeOnly (.NET 6+) -
Decimal.fs: Decimal extensions
Advanced Types (Phase 0.3 - Optional for alpha)
Can be deferred to beta/RC releases:
-
Instant.fs: DateTimeOffset alias -
Month.fs: Month enumeration -
UUID.fs: Guid alias -
Regex.fs: Regex wrapper -
Key.fs: Key<'a> discriminated union for lookups -
Aggregate.fs: Aggregation functions
Testing Requirements
- Unit tests for all modules (≥80% coverage)
- Property-based tests using FsCheck for mathematical laws
- Example:
List.map id = id(functor identity law) - Example:
List.map (f >> g) = List.map f >> List.map g(functor composition)
- Example:
- Compatibility tests comparing behavior with morphir-elm semantics
- Executable examples for each module
Documentation Requirements
- XML doc comments on all public APIs (visible in IntelliSense)
-
README.mdinsrc/Morphir.SDK/with:- Quick start guide
- Usage examples for each module
- Installation instructions
- Differences from morphir-elm (if any)
- API reference (auto-generated from XML docs)
- Migration guide from
main-archiveSDK
NuGet Package
- Build and pack:
dotnet pack -c Release - Publish
Morphir.SDK 0.4.0-alphato NuGet.org - Validate package installation:
dotnet add package Morphir.SDK --version 0.4.0-alpha - Test consumption in a sample F# project
Implementation Guidance
Step 1: Review Existing Code (main-archive)
The main-archive branch contains a previous SDK implementation:
git fetch origin main-archive
git checkout main-archive
# Review: src/Morphir.SDK.Core/What to extract:
Basics.fs- Order type and comparison functions (reuse directly)List.fs- Some list extensions (review for quality)- Test patterns and property tests
What to update:
- Upgrade to .NET 10 / F# 9
- Replace LightBDD with TUnit for testing
- Ensure AOT compatibility (no reflection)
Step 2: Create Project Structure
# From repo root
dotnet new classlib -n Morphir.SDK -lang F# -o src/Morphir.SDK
dotnet new tunit -n Morphir.SDK.Tests -lang F# -o tests/Morphir.SDK.Tests
# Add to solution (uses .slnx format)
dotnet sln Morphir.slnx add src/Morphir.SDK/Morphir.SDK.fsproj
dotnet sln Morphir.slnx add tests/Morphir.SDK.Tests/Morphir.SDK.Tests.fsproj
# Add test project reference
cd tests/Morphir.SDK.Tests
dotnet add reference ../../src/Morphir.SDK/Morphir.SDK.fsproj
dotnet add package FsCheck --version 2.16.6Step 3: Implement Core Modules
Example: Maybe.fs
namespace Morphir.SDK
/// <summary>
/// Morphir Maybe type - alias for F# Option.
/// Maintains semantic alignment with Morphir IR.
/// ADAPTED FROM: morphir-elm src/Morphir/SDK/Maybe.elm
/// </summary>
type Maybe<'a> = Option<'a>
module Maybe =
/// <summary>Apply a function to a Maybe value</summary>
let inline map f maybe = Option.map f maybe
/// <summary>Chain Maybe operations (bind)</summary>
let inline andThen f maybe = Option.bind f maybe
/// <summary>Return the value or a default</summary>
let inline withDefault defaultValue maybe =
Option.defaultValue defaultValue maybe
/// <summary>Convert Maybe to Result with error message</summary>
let inline toResult error maybe =
match maybe with
| Some value -> Ok value
| None -> Error errorExample: Basics.fs (from main-archive)
namespace Morphir.SDK
/// <summary>
/// Ordering enumeration for comparisons.
/// MIGRATED FROM: main-archive src/Morphir.SDK.Core/Basics.fs
/// </summary>
type Order =
| LT // Less than
| EQ // Equal
| GT // Greater than
module Basics =
/// <summary>Compare two values</summary>
let inline compare x y =
match Comparer<_>.Default.Compare(x, y) with
| n when n < 0 -> LT
| 0 -> EQ
| _ -> GT
/// <summary>Minimum of two values</summary>
let inline min x y = if x < y then x else y
/// <summary>Maximum of two values</summary>
let inline max x y = if x > y then x else yStep 4: Write Tests
Example: MaybeTests.fs
module Morphir.SDK.Tests.MaybeTests
open TUnit.Core
open TUnit.Assertions
open Morphir.SDK
[<Test>]
let ``Maybe.map transforms Some values`` () =
task {
let result = Maybe.map ((*) 2) (Some 5)
do! Assert.That(result).IsEqualTo(Some 10)
}
[<Test>]
let ``Maybe.map returns None for None`` () =
task {
let result = Maybe.map ((*) 2) None
do! Assert.That(result).IsEqualTo(None)
}
[<Test>]
let ``Maybe.withDefault returns value for Some`` () =
task {
let result = Maybe.withDefault 0 (Some 42)
do! Assert.That(result).IsEqualTo(42)
}
[<Test>]
let ``Maybe.withDefault returns default for None`` () =
task {
let result = Maybe.withDefault 0 None
do! Assert.That(result).IsEqualTo(0)
}Property-Based Tests with FsCheck
module Morphir.SDK.Tests.MaybeProperties
open FsCheck
open TUnit.Core
open Morphir.SDK
[<Test>]
let ``Maybe.map satisfies functor identity law`` () =
task {
let prop (x: int option) =
Maybe.map id x = x
let result = Check.Quick prop
do! Assert.That(result.Succeeded).IsTrue()
}
[<Test>]
let ``Maybe.map satisfies functor composition law`` () =
task {
let prop (x: int option) =
let f = (*) 2
let g = (+) 1
Maybe.map (f >> g) x = (Maybe.map f >> Maybe.map g) x
let result = Check.Quick prop
do! Assert.That(result.Succeeded).IsTrue()
}Step 5: Configure NuGet Package
Edit src/Morphir.SDK/Morphir.SDK.fsproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<!-- NuGet Package Metadata -->
<PackageId>Morphir.SDK</PackageId>
<Version>0.4.0-alpha</Version>
<Authors>FINOS Morphir Team</Authors>
<Company>FINOS</Company>
<Description>F# runtime library for Morphir SDK types and functions</Description>
<PackageTags>morphir;fsharp;sdk;functional-programming</PackageTags>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/finos/morphir-dotnet</PackageProjectUrl>
<RepositoryUrl>https://github.com/finos/morphir-dotnet</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
</Project>Step 6: Build, Test, and Publish
# Build
dotnet build src/Morphir.SDK/Morphir.SDK.fsproj
# Test (should have ≥80% coverage)
dotnet test tests/Morphir.SDK.Tests/Morphir.SDK.Tests.fsproj
# Pack
dotnet pack src/Morphir.SDK/Morphir.SDK.fsproj -c Release -o ./artifacts
# Publish to NuGet (requires API key)
dotnet nuget push ./artifacts/Morphir.SDK.0.4.0-alpha.nupkg \
--source https://api.nuget.org/v3/index.json \
--api-key $NUGET_API_KEY
# Test installation
mkdir /tmp/test-sdk
cd /tmp/test-sdk
dotnet new console -lang F#
dotnet add package Morphir.SDK --version 0.4.0-alphaMigration from morphir-elm
Review the morphir-elm SDK modules for reference semantics:
| morphir-elm Module | F# Implementation Strategy |
|---|---|
Maybe.elm |
Type alias to Option<'a>, delegate all functions to Option module |
Result.elm |
Type alias to Result<'v, 'e>, delegate to Result module |
List.elm |
Extensions to F# List module |
Dict.elm |
Type alias to Map<'k, 'v> (F# has immutable maps built-in) |
Set.elm |
Extensions to F# Set<'a> |
String.elm |
Extensions to F# String type |
Basics.elm |
Create Order type, comparison functions |
LocalDate.elm |
Type alias to DateOnly (.NET 6+) |
LocalTime.elm |
Type alias to TimeOnly (.NET 6+) |
Key Principle: Maintain semantic compatibility with morphir-elm while leveraging F#/.NET's superior type system and standard library.
Success Criteria
-
Morphir.SDKNuGet package published to NuGet.org - All core modules (Phase 0.1 & 0.2) implemented with tests
- Test coverage ≥ 80% (check with
dotnet test --collect:"XPlat Code Coverage") - Package can be consumed by F# projects
- Zero compiler warnings
- AOT compatible (no reflection, no dynamic code generation)
- Ready for F# backend Phase 1 (Phase 1: Foundation - Project Setup and Fabulous.AST Exploration #365) to begin
Definition of Done
- All acceptance criteria met
- Code reviewed and merged to main branch
- NuGet package published and verified installable
- Documentation complete (README, XML docs, API reference)
- CI/CD pipeline updated (if needed)
- Issue Phase 1: Foundation - Project Setup and Fabulous.AST Exploration #365 (Phase 1) unblocked and can proceed
Related Issues
Blocks:
- Phase 1: Foundation - Project Setup and Fabulous.AST Exploration #365 Phase 1: Foundation - Project Setup and Fabulous.AST Exploration
- Phase 2: Type Mapping - Morphir IR Types → F# Types #366 Phase 2: Type Mapping - Morphir IR Types → F# Types
- Phase 3: Value Mapping - Morphir IR Values → F# Functions #367 Phase 3: Value Mapping - Morphir IR Values → F# Functions
- Phase 4: CLI Integration - morphir gen fsharp Command #368 Phase 4: CLI Integration
- Phase 5: SDK Translation - Morphir SDK → F# Standard Library #369 Phase 5: SDK Translation
- Phase 6: Advanced Features - JSON Codecs and Lenses #370 Phase 6: Advanced Features
- Phase 8: Release Preparation #371 Phase 8: Release Preparation
- Phase 7: Testing and Documentation #372 Phase 7: Testing and Documentation
Part of: #363 F# Code Generation Backend (Epic)
Questions or Blockers?
If you encounter any issues or have questions:
- Review the Morphir.SDK Library Plan
- Check the main-archive implementation
- Reference morphir-elm SDK for semantics
- Comment on this issue with specific questions
Status: Ready for Implementation
Estimated Effort: 1-2 weeks
Milestone: M0 - Foundation
Target Version: Morphir.SDK 0.4.0-alpha