Using F# scripts is something I only started doing after dabbling in F# for quite a while. This is unfortunate because they are a really fast and easy way of throwing some code together and thus a really good way to learn F#. This post is for anyone getting started with F# scripting.
Installation
Check out the documentation for installing F#. The easiest way is to install F# as part of Visual Studio. You may still need to add FSI.exe to your PATH.
F# Interactive
FSI allows you to execute F# code in an interactive console. Just type fsi.exe
on Windows or fsharpi
on Linux/Mac.
> let x = 1;;
val x : int = 1
Note: Each expression needs to end with
;;
in the interactive window.
To get help: #help;;
To quit: #quit;;
Scripting
So entering code directly into fsi is ok for trying simple things out but what about more complex code. That is where .fsx
files come in.
Imagine we have a file called print-name.fsx with the following content:
let name = "Devon"
printfn "Name: %s" name
Executing it we would see the following:
> fsi .\samples\print-name.fsx
> Name: Devon
Including other fsx files
You can load other fsx files into a script. If we have Strings.fsx
containing the following code:
let toUpper (s:string) = s.ToUpper()
let toLower (s:string) = s.ToLower()
let replace (oldValue:string) (newValue:string) (s:string) = s.Replace(oldValue,newValue)
module StringBuilder =
open System.Text
let init() = new StringBuilder()
let initWith(s:string) = new StringBuilder(s)
let append (s:string) (sb:StringBuilder) = sb.Append(s)
We can now use it in our script file like so:
#load "Strings.fsx"
open Strings
let name = "Devon" |> StringBuilder.initWith
|> StringBuilder.append " Burriss"
|> string |> toUpper
printfn "Name: %s" name
We use #load "path/to/script.fsx"
to make it available and then open NameOfFileWithoutExtension
to import it. So each script file is then treated as a module
.
Executing it we would see the following:
> fsi .\samples\print-name.fsx
> Name: DEVON BURRISS
Taking Arguments
It is possible to pass arguments into a script file. They are available in a field fsi.CommandLineArgs
. Let's change our script one more time to demonstrate the usage. The arguments come through as an array so we pattern match on the number of elements to decide what to print.
Note: The first element of the array is always the name of the script the arguments are passed into
#load "Strings.fsx"
open Strings
let stringWithSpace x = x |> string |> (sprintf " %s")
let name first = first |> toUpper
let nameAndLastName first last = first |> StringBuilder.initWith |> StringBuilder.append last |> stringWithSpace |> toUpper
let nameAndLastNameWithOccupation first last occ =
first |> StringBuilder.initWith
|> StringBuilder.append " "
|> StringBuilder.append last
|> StringBuilder.append (sprintf " (%s)" occ)
|> string |> toUpper
match fsi.CommandLineArgs with
| [|scriptName;|] -> failwith (sprintf "At least a name required for %s" scriptName)
| [|_;firstName|] -> name firstName |> printfn "Name: %s"
| [|_;firstName; lastName|] -> nameAndLastName firstName lastName |> printfn "Name: %s"
| [|_;firstName; lastName; occ|] -> nameAndLastNameWithOccupation firstName lastName occ |> printfn "Name: %s"
| _ -> failwith (sprintf "Too many arguments %A" (fsi.CommandLineArgs |> Array.tail))
Executing it we would see the following:
> fsi .\samples\print-name.fsx devon burriss developer
> Name: DEVON BURRISS (DEVELOPER)
You could of course also access the arguments as a zero based array fsi.CommandLineArgs.[0]
or loop through them.
Nuget packages
Edit: Since .NET 5 it is possible to reference nuget packages directly in your fsx script.
You can reference nuget packages in your script files by using the #r nuget:Package.Name
syntax.
As an example:
#r "nuget: FSharp.Data"
open FSharp.Data
let jVal = JsonValue.Parse """{ "lang": "fsharp" }"""
Referencing DLLs
You can reference DLLs using #r "path/to/file.dll"
. If you want to pull down DLLs from Nuget, check out my article on using Paket dependency manager.
Resources
Credits
- Social Image by yifei chen