scripting with #r NuGet and comparing some python syntax
Getting Started
- install latest dotnet SDK
- install vscode and add all ionide extensions
mkdir fsx && cd fsx; touch new.fsx; code .
VSCode and IONIDE
Ionide is a great F# cross-platform IDE plug-in for VScode, it works pretty well for F# scripting but sometimes might need you to clear cache or restart vscode
though.
I will also compare a bit of F# and Python between the lines, as python is the de-facto standard for scripting in 2021.
A function in Python
def sum(a,b):
return a + b
The same function in F#
let sum a b = a + b // or quicker, let sum = (+)
A class in Python
class Dog:
species = "Canis familiaris"
def __init__(self, name, age):
self.name = name
self.age = age
def description(self):
return f"{self.name} is {self.age} years old"
def speak(self, sound):
return f"{self.name} says {sound}"
A class in F#
type Dog(name,age) =
member val Description =
$"{name} is {age} years old"
member this.Speak(sound) =
$"{name} says {sound}"
member val Species = "canis familiaris"
Manipulating Sequences (*)
F#: with the pipe operator |> and List module functions:
let sumOfEvenNumbersFromOneTo100Adding1 =
[1..100]
|> List.filter (fun x -> x % 2 = 0)
|> List.map (fun x -> x + 1)
|> List.sum //2600
Python: using the list module (there is no pipe operator, more verbose)
sample2 = range(1,101) # 1..100
filteredList = list(filter(lambda x : x % 2 == 0, sample2))
addOneList = list(map(lambda x : x + 1 , filteredList))
total = sum(addOneList) # 2600
List Comprehensions (*)
F#: list comprehensions are a powerful construct enabled by computation expressions, so regular F# code is valid within a list comprehension!
let listComprehension =
[
for x in [1..100] do
let z = "hello" // just to show you can!
if x % 2 = 0 then
yield x + 1 // value is returned whenever we want
]
Python: also has list comprehensions, but not all Python code is valid within a list comprehension but just a subset, kind of like a “query subset” of Python
?
list_comprehension =
[
x + 1 # value is always returned at the top
for x in range(1,101) # 1..100
if x % 2 == 0
]
As we see both languages allow for working with data and sequences, but in my view F# beats Python both in succinctness, typing and capabilities here.
Reading a JSON file
As an example task, we want to read some fields in a json
file, so parse it and display the captured value on the console.
In Python
import json
with open('test.json', 'r') as myfile:
data = myfile.read()
obj = json.loads(data)
print(obj["hello"])
In F# with JsonParser
#r "nuget: FSharp.Data"
open FSharp.Data
open FSharp.Data.JsonExtensions // ? op
let result =
(__SOURCE_DIRECTORY__ + "/test.json")
|> System.IO.File.ReadAllText
|> JsonValue.Parse
printfn $"{result?Hello}"
In F# with Type Providers
#r "nuget: FSharp.Data"
open FSharp.Data
[<Literal>]
let jsonFilePath = __SOURCE_DIRECTORY__ + @"/test.json"
type MyJson = JsonProvider<jsonFilePath>
let result = MyJson.Load(__SOURCE_DIRECTORY__ + "/prod.json")
printfn $"{result.Hello}" //strongly typed!
Reading a CSV file
Name,Surname,Age
John,Red,33
Mike,Bianchi,55
...
Python
import csv
with open('test.csv') as csv_file:
csv_reader = csv.reader(csv_file, delimiter=',')
for row in csv_reader:
print(row[0]) //Name
F#
#r "nuget: FSharp.Data"
open FSharp.Data
[<Literal>]
let csvPath = __SOURCE_DIRECTORY__ + "/test.csv"
type MyCsv = CsvProvider<csvPath, ",">
for row in MyCsv.GetSample().Rows do
printfn $"{row.Name}" // first column header = Name
Plotting some lines with Plotly
Python
import plotly.express as px
df = list(range(1, 11)) //1...10
fig = px.line(df)
fig.show()
F#
#r "nuget: XPlot.Plotly"
open XPlot.Plotly
[ 1 .. 10 ] |> Chart.Line |> Chart.Show
Starting a local web server
Python
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello World!'
app.run(host='0.0.0.0', port=81)
F#
#r "nuget: Suave"
open Suave
startWebServer defaultConfig (Successful.OK "Hello World!")