Projects and NuGet Packages
Prerequisites: Modules and namespaces
Scripts are great for exploring; applications and libraries live in
projects, built by the dotnet command-line tool.
Creating a solution
dotnet new sln -n Shop
dotnet new classlib -lang F# -o src/Shop.Core
dotnet new console -lang F# -o src/Shop.Cli
dotnet new xunit -lang F# -o tests/Shop.Tests
dotnet sln add src/Shop.Core src/Shop.Cli tests/Shop.Tests
dotnet add src/Shop.Cli reference src/Shop.Core
dotnet add tests/Shop.Tests reference src/Shop.Core
This gives a class library for your domain code, a console app that uses it, and a test project — a common layout for real F# work.
The .fsproj file
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="Money.fs" />
<Compile Include="Orders.fs" />
<Compile Include="Library.fs" />
</ItemGroup>
</Project>
Unlike C#, F# lists every source file explicitly, in compilation
order: Orders.fs can use Money.fs, but not the other way round.
Editors keep this list for you ("Move file up/down" in Ionide and
Rider). This rule prevents circular dependencies and makes a codebase
easy to read from top to bottom.
NuGet packages
NuGet is .NET's package manager, with hundreds of thousands of packages at nuget.org:
dotnet add package FSharp.Data
dotnet add package FsToolkit.ErrorHandling
dotnet list package
dotnet restore
dotnet add package adds a <PackageReference> line to the .fsproj.
Every C# library works from F#, plus F#-specific ones such as
FSharp.Data (type providers), FsToolkit.ErrorHandling (result
helpers), Argu (command-line parsing), Thoth.Json and
FSharp.SystemTextJson (JSON), Expecto (tests), and Giraffe (web).
Packages in scripts
Scripts can reference packages directly — no project needed:
// illustrative: downloads the package on first run
#r "nuget: Humanizer"
open Humanizer
printfn "%s" ("some_database_column".Humanize())
printfn "%s" (System.TimeSpan.FromMinutes(90.0).Humanize(2))
Some database column
1 hour, 30 minutes
Pin a version with #r "nuget: Humanizer, 2.14.1" so the script keeps
working the same way. #load "Helpers.fsx" includes another script
file.
Everyday commands
| Command | Does |
|---|---|
dotnet build |
compile (Debug by default) |
dotnet run |
build and run the console project in this folder |
dotnet watch run |
rebuild and restart on every save |
dotnet test |
build and run test projects |
dotnet publish -c Release |
produce deployable output |
dotnet tool install fantomas |
install a tool (after dotnet new tool-manifest) |
Paket is an alternative dependency manager popular in parts of the
F# community; plain NuGet with PackageReference is the default and
works well for most projects.