Etymon.Base 0.1.0-preview.10

This is a prerelease version of Etymon.Base.
dotnet add package Etymon.Base --version 0.1.0-preview.10
                    
NuGet\Install-Package Etymon.Base -Version 0.1.0-preview.10
                    
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="Etymon.Base" Version="0.1.0-preview.10" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Etymon.Base" Version="0.1.0-preview.10" />
                    
Directory.Packages.props
<PackageReference Include="Etymon.Base" />
                    
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 Etymon.Base --version 0.1.0-preview.10
                    
#r "nuget: Etymon.Base, 0.1.0-preview.10"
                    
#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 Etymon.Base@0.1.0-preview.10
                    
#: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=Etymon.Base&version=0.1.0-preview.10&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Etymon.Base&version=0.1.0-preview.10&prerelease
                    
Install as a Cake Tool

Etymon.Base

The .NET base library, returning option and Result instead of null and exceptions.

Part of the Etymon suite, but it depends on nothing but FSharp.Core — not even another Etymon package. Take it on its own if that is all you want.

The base library signals failure three different ways: null, a thrown exception, and a bool with an out parameter. None of them are visible in a type, so none of them are things the compiler can remind you about. Every function here replaces one of those with a value: something absent is option, something that failed for a reason worth branching on is Result, and nothing throws for an outcome that was always going to happen sometimes.

Parsing, with the culture spelled out

Parse.int "42"              // Some 42
Parse.decimal "1.50"        // Some 1.50m  -- scale preserved
Parse.bool "YES"            // Some true
Parse.guid "not-a-guid"     // None
Parse.dateOnly "2026-09-20" // Some ...

The plain names use the invariant culture, because a value that came off a wire, out of a config file or from a command line has no culture and must not acquire the machine's by accident. The In variants take one, for text a person typed:

Parse.decimal "1,50"                          // None
Parse.decimalIn (CultureInfo.GetCultureInfo "de-DE") "1,50"  // Some 1.50m

That first line matters more than it looks. Decimal.TryParse with NumberStyles.Number — the obvious thing to write — parses "1,5" as 15 under the invariant culture, because the comma is a group separator there. A price becomes ten times a price and nothing complains. The invariant parsers here disallow grouping for exactly that reason.

Two other sharp edges are handled: Parse.float rejects NaN and the infinities, and Parse.enum rejects a number with no matching member, which is how (DayOfWeek)99 normally gets into a domain.

Strings

Str.toOption "  "                       // None -- blank is absent
Str.split ',' "a, b ,, c"               // [ "a"; "b"; "c" ]
Str.splitFirst '=' "KEY=a=b"            // Some ("KEY", "a=b")
Str.equalsIgnoreCase "Content-Type" "content-type"  // true
Str.truncate 8 "a long sentence"        // "a long …"

Every comparison is ordinal unless its name says otherwise, because culture-aware comparison is for sorting prose and is almost never right for a header name, a config key or an identifier. Nothing here throws on null.

Named Str, not String, so it never shadows FSharp.Core's String module — String.concat keeps working.

Dictionaries

Dict.tryFind "accept" headers                    // Some "application/json"
Dict.tryPick Parse.int "retries" settings        // Some 3
Dict.ofListIgnoreCase [ "Content-Type", "json" ] // the right comparer for headers

Written over IDictionary and IReadOnlyDictionary, so they work on Dictionary, ConcurrentDictionary and anything else implementing them.

Environment

Env.tryGet "PATH"            // Some "..."
Env.tryGetInt "PORT"         // Some 8080
Env.isEnabled "ETYMON_DEBUG" // false when unset -- the shape of a feature switch

Unset and set-but-blank are both absent by default, because an empty connection string is as useless as a missing one. Env.tryGetAllowEmpty is there for the rare case that genuinely cares.

File IO with a typed error

match File.tryReadAllText "appsettings.json" with
| Ok contents -> ...
| Error (FileError.NotFound path) -> ...      // create a default
| Error (FileError.AccessDenied path) -> ...  // tell the operator
| Error e -> eprintfn "%s" (FileError.describe e)

FileError names the cases a caller can actually do something about — NotFound, DirectoryNotFound, AccessDenied, InUse, InvalidPath, NotValidText — and puts everything else in IoFailure. Nobody should have to match on a message.

Two deliberate behaviours: writing creates the containing directory, since not doing so only ever produces a failure the caller would fix by creating it; and deleting something already absent succeeds, because the caller's intent has been met either way. Text is UTF-8 without a byte order mark.

Dir is the same for directories. Listings are sorted, so anything generated from one is reproducible.

Non-goals

This package is deliberately bounded. It will not grow:

  • Collection combinators. List, Seq, Array and Map are already good.
  • Option and Result combinators. Those belong in Etymon.Core, or in FsToolkit if you want the full set.
  • Custom operators. No >>=, no <!>, nothing you have to learn.
  • HTTP, JSON, serialisation, logging, DI, process control. Not a framework.
  • Async or task helpers. That is a design space of its own, and a large one.
  • Wrappers for APIs that are already fine from F#. If calling it directly reads well, it does not belong here.

The list exists so that "no" is a cheap answer to a feature request, which is the only thing that keeps a utility package from becoming everything.

Public surface

The complete public surface of this package, every value with its full signature and its documentation, is in Surface.fsi beside this file. It is written by the build from the implementation, so it is where to learn what a function hands back without compiling anything.

Licence

MIT.

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

NuGet packages (3)

Showing the top 3 NuGet packages that depend on Etymon.Base:

Package Downloads
Etymon.Config

Configuration loaded through an Etymon schema, from environment variables, JSON files or a source of your own. Reports every missing or invalid setting at once, each with what was expected and which source supplied it. Sensitive settings are redacted wherever Etymon prints them.

Etymon.Api

HTTP endpoints described once: method, typed route, query parameters, request and response schemas, and typed failures. Performs no HTTP itself. Separate adapter packages interpret the same declaration as a Giraffe handler, an ASP.NET Core route, a typed client, OpenAPI paths or TypeScript declarations.

Etymon

The Etymon suite: one schema definition as the source of truth for JSON codecs, validation, OpenAPI, TypeScript, configuration and database structure. This package has no code of its own; it references the parts of the suite that cost nothing but FSharp.Core. The adapters (Etymon.Api.Giraffe, Etymon.Api.AspNetCore) and the generator package (Etymon.Invariants.FsCheck) are deliberately left out, because each carries a dependency that would then be everyone's.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0-preview.10 0 9/22/2026
0.1.0-preview.9 0 9/22/2026
0.1.0-preview.8 0 9/22/2026
0.1.0-preview.7 0 9/21/2026
0.1.0-preview.6 0 9/21/2026
0.1.0-preview.5 0 9/21/2026
0.1.0-preview.4 28 9/21/2026
0.1.0-preview.3 33 9/21/2026
0.1.0-preview.2 37 9/20/2026
0.1.0-preview.1 34 9/20/2026