F# ohne Windows mit Visual Studio Code
In Einstieg in Visual Studio mit F# haben wir im Schnelldurchgang die ersten Schritte im Zusammenhang mit Anwendungen in F# kennen gelernt. Dabei haben wir auf Visual Studio zurückgegriffen und uns somit auf Windows oder MacOS beschränkt.
Mit Visual Studio Code bietet Microsoft hingegen einen plattformunabhängigen Editor an. In diesem Blogpost zeigen wir ähnlich wie im vorherigen Post erste Schritte in F#, diesmal in Visual Studio Code unter einem linuxbasierten Betriebssystem. Dabei erhalten wir auch Einblicke in die Bedienung mit Kommoandozeilentools, die in nahezu gleicherweise unter Windows und MacOS anwendbar sind. Dieser Artikel geht an manchen Stellen weniger ins Detail als Einstieg in Visual Studio mit F#, weshalb wir diesen Blogpost vorab empfehlen.
Installation
Unter Windows genügt es, Visual Studio zu installieren und die Entwicklung kann beginnen. Unter Linux müssen wir mehr Aufwand betreiben. Wir installieren neben Visual Studio Code noch .NET SDK, Mono und einige Plugins für Visual Studio Code. Abhängig des verwendeten Betriebssystems muss hierfür verschieden vorgegangen werden. In unserem Blogartikel verwenden wir den Nix-Paket-Manager (siehe auch Mit Nix raus aus der Versionshölle). Wir stellen unser Setup in einer nicht-invasiven Nix-Shell her. Dazu führen wir in einem Terminal
NIXPKGS_ALLOW_UNFREE=1 nix-shell -p dotnet-sdk_3 -p vscode -p fsharp -p mono
aus. Da .NET SDK nicht unter freier Lizenz steht, muss mit NIXPKGS_ALLOW_UNFREE=1
die Installation explizit erlaubt werden. Wir befinden uns nun in einer virtuellen Umgebung, die die installierten Pakete und Visual Studio Code beinhaltet. Wir erstellen einen leeren Ordner und starten Visual Studio Code:
mkdir -p ersteschritte
cd ersteschritte
code .
Als nächstes installieren wir die Erweiterungen Ionide-fsharp und C#. C# wird später für die Funktionalität der Haltepunkte und des Debuggings benötigt. In unserem Fall haben wir noch zusätzlich German Language Pack for Visual Studio Code installiert.
Projekt erstellen
Mit dem Tastenkürzel STRG + UMSCHALT + P erscheint eine Kommandozeileneingabe. Wir tippen F#: New Project. In den folgenden Abfragen wählen wir Console Application als Anwendungstyp, .
(aktuelles Verzeichnis) als Projektordner und ErsteSchritte als Projektname.
Wir sehen links im Explorer einige erstellte Dateien, unter anderem die Program.fs mit einer beispielhaften Main-Methode. Unter Terminal, Neues Terminal erhalten wir eine Betriebssystemkonsole. Wir führen unser Programm erstmalig aus:
dotnet run
Wir erhalten als Rückgabe wie erwartet Hello World from F#!
.
In der obigen Abbildung sehen wir, dass die Typsignatur von main
als // string [] -> int
inferiert und angezeigt wird. Fahren wir mit dem Mauszeiger über eine Definition, erhalten wir ebenfalls diese Information.
F# Interactive
Auch in Visual Studio Code können wir die interaktive Repl von F# nutzen. Dazu drücken wir in einer Zeile oder nach einem markierten Codeblock ALT + ENTER. Der entsprechende Code wird mit dem Kommandozeilentool fsharpi
ausgeführt. In dem offenbleibenden F# Interactive-Fenster können wir auch eigene Anweisungen (z. B. 14 * 5;;
) eingeben. Dabei muss jede Eingabe mit ;;
gefolgt von ENTER beendet werden.
Debugging & Haltepunkte
Um unsere Anwendung in Visual Studio Code debuggen zu können, müssen wir einmalig eine Konfiguration anlegen. Wir gehen auf Debuggen, Debuggen starten oder drücken F5. Im erscheinenden Fenster wählen wir .NET Core. Darauf folgt die Fehlermeldung, dass keine .NET-Konfiguration angelegt werden konnte. Ebenso öffnet sich die Datei .vscode/launch.json
. Hier fügen wir unsere Konfiguration ein.
Im Wert von configurations
drücken wir zwischen den eckigen Klammern Enter und tippen .NET
. In der erscheinenden Vorschlagsliste wählen wir .NET: Launch .NET Core Console App. Wir müssen die beiden Platzhalter <target-framework>
und <project-name.dll>
ersetzen und dafür die richtige Version des Frameworks und den Namen der dll-Datei ermitteln: Wenn wir im Explorer von Visual Studio Code den Ordner bin/Debug
öffnen, sehen wir das verwendete Framework inklusive Version. In diesem Ordner befindet sich auch die dll-Datei. Diese heißt normalerweise gleich wie das Projekt selbst. In unserem Fall sieht die launch.json mit den beiden ermittelten Werten netcoreapp3.1
und ErsteSchritte.dll
wie folgt aus:
{
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (console)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/bin/Debug/netcoreapp3.1/ErsteSchritte.dll",
"args": [],
"cwd": "${workspaceFolder}",
"stopAtEntry": false,
"console": "internalConsole"
}
]
}
Wir speichern launch.json
und wählen erneut Debuggen starten. Es kommt die Meldung, dass der Task build nicht gefunden wurde. In launch.json haben wir build
als preLaunchTask
festgelegt, da wir unser Projekt vor dem Debuggen bauen müssen.
Wir klicken auf Aufgabe konfigurieren gefolgt von Datei task.json aus Vorlage erstellen. Als Aufgabenvorlage nehmen wir .NET Core
. Die automatisch erzeugte Vorlage ist für unseren Fall passend:
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "shell",
"args": [
"build",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"group": "build",
"presentation": {
"reveal": "silent"
},
"problemMatcher": "$msCompile"
}
]
}
Das Debuggen ist jetzt eingerichtet. Wir setzen in Program.fs einen Haltepunkt auf 0
. Dazu klicken wir links neben die entsprechende Zeilennummer oder drücken F9 während der Cursor in dieser Zeile steht. Es erscheint ein roter Punkt am Anfang der Zeile.
Mit Debuggen starten wird unser Projekt gebaut und ausgeführt. Die Ausführung bleibt am Haltepunkt stehen. Links werden die Werte der bisher berechneten Variablen angezeigt. Oben erscheint eine kleine Videorecorder-Navigation, mit der wir fortsetzen, pausieren, abbrechen oder weiter springen können. Wir drücken einmal auf Weiter. Der Haltepunkt wird durchlaufen und das Programm ist beendet. Wir entfernen den Haltepunkt wieder, analog zum Hinzufügen.
Neue Datei hinzufügen
Möchten wir eine neue Datei hinzufügen, wählen wir im Explorer-Bereich Neue Datei im Kontextmenü und benennen sie MeinModul.fs. Wir geben der Datei einen Namensraum und definieren uns ein Modul mit einem abgerundeten Wert von Pi:
namespace ErsteSchritte
module MeinModul =
/// Pi als abgerundete Dezimalzahl
let pi : decimal =
3.141M
Weiter ändern wir in der Zeile printfn
der Program.fs ab zu:
printfn "%A" ErsteSchritte.MeinModul.pi
IntelliSense, die ständige Echtzeitkompilierung von F#, unterstreicht unmittelbar den Aufruf von ErsteSchritte
. Wir müssen die neue Datei noch unserem Projekt hinzufügen. Dazu öffnen wir ErsteSchritte.fsproj und fügen die Zeile
<Compile Include="MeinModul.fs" />
oberhalb der gleichlautenden Zeile für Program.fs ein. Die Reihenfolge ist hier maßgebend. Die Program.fs beinhaltet den Einstiegspunkt und muss als letztes definiert sein. Ggf. müssen wir Program.fs neu öffnen. Der Aufruf ErsteSchritte.MeinModul.pi sollte nun bekannt sein. dotnet run
in der Konsole ausgeführt liefert uns:
user@rechner:~/ersteschritte$ dotnet run
3.141M
Pakete installieren
Im nächsten Schritt definieren wir Testfälle. Dafür benutzen wir die Test-Frameworks NUnit und FSUnit. Um die benötigten Pakete zu installieren, bearbeiten wir ErsteSchritte.fsproj
und fügen vor </Project>
folgende Pakete an:
<ItemGroup>
<PackageReference Include="FsUnit" Version="3.8.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.16.1" />
</ItemGroup>
Bei der nächsten Ausführung von dotnet build
werden die Pakete heruntergeladen und referenziert.
Testfall definieren und ausführen
Im Explorer von Visual Studio Code legen wir einen neuen Ordner Test
an und darin die Datei MeinModulTest.fs
. Wir fügen das folgende Modul mit einem Testfall für unsere Funktion MeinModul.pi
ein:
namespace ErsteSchritte.Test
module MeinModulTest =
open NUnit.Framework
open FsUnit
[<Test>]
let ``Defintion of Pi`` () =
ErsteSchritte.MeinModul.pi |> should equal 3.141M
Wir müssen die neue Datei noch als Referenz in unser Projekt aufnehmen. Wir fügen daher in ErsteSchritte.fsproj
<Compile Include="Test/MeinModulTest.fs" />
zwischen die Zeilen mit MeinModul.fs und Program.fs ein.
Um die Tests auszuführen, führen wir den Befehl dotnet test
aus. Wir erhalten in etwa:
user@rechner:~/ersteschritte 1$ dotnet test
Testlauf für "/home/td/ersteschritte/bin/Debug/netcoreapp3.1/ErsteSchritte.dll" (.NETCoreApp,Version=v3.1)
Microsoft (R) Testausführungs-Befehlszeilentool Version 16.3.0
Copyright (c) Microsoft Corporation. Alle Rechte vorbehalten.
Die Testausführung wird gestartet, bitte warten...
Insgesamt 1 Testdateien stimmten mit dem angegebenen Muster überein.
Der Testlauf war erfolgreich.
Gesamtzahl Tests: 1
Bestanden: 1
Gesamtzeit: 0,9498 Sekunden
Fazit
Auch unter Betriebssystemen abseits von Windows können wir .NET-Core-Anwendungen mit F# entwickeln. Visual Studio Code und das Plugin Ionide bieten uns hierfür eine gute Basis. Im Vergleich zu Visual Studio unter Windows müssen wir mehr mit der Konsole arbeiten und einige Einstellungen von Hand vornehmen. Am Ende können aber Dinge, wie das Projekt zu bauen, Debuggen mit Haltepunkten oder Testfälle ähnlich einfach genutzt werden.
In einem weiteren Blogartikel werden wir die Sprache F# selbst kennen lernen. Dabei spielt es dann keine Rolle mehr, ob wir mit Visual Studio unter Windows oder wie hier mit Visual Studio Code arbeiten.