Linguaggi

Introduzione a F#

F# è un linguaggio funzionale (functional-first) ma non solo, un linguaggio che abbraccia più di un paradigma (multi -paradigm language), da quello imperativo all'OOP.



Partendo da Visual Studio 2013 Update 4, vediamo quali sono i progetti che possiamo creare una volta selezionato dal menù il linugaggio F#:

 

  • Console Application: crea una nuova applicazione eseguibile da console.
  • Library: crea una nuova libreria che può essere referenziata da altre applicazioni o librerie.
  • Tutorial: è un progetto che contiene molti esempi per iniziare a programmare con F# ma che non ha realmente una utilità se si vuole creare un nuovo progetto. 
  • Portable Library: crea una libreria di classi “portabile” che può essere usata da applicazione .NET 4.5 e del Windows Store. 
  • Portable Library (Legacy): crea una libreria di classi “portabile” che può essere usata da applicazioni .NET 4.0 e Silverlight.
  • Silverlight Library: crea una nuova libreria che può essere referenziata da una applicazione Silverlight.

Innanzitutto capiamo la filosofia che c’è dietro l’organizzazione dei programmi e del codice scritto in F#.
Il codice viene eseguito dall’alto verso il basso (from top to bottom) e questo è vero non solo per l’ordine delle dichiarazioni all’interno di un singolo file ma anche per l’ordine dei files all’interno di un progetto. Le funzioni dichiarate, ad esempio, all’interno di un file sono “visibili” solo dal codice dei files che gerarchicamente sono definiti dopo quello in cui esse sono dichiarate: spostando il file più in basso comporta un errore di compilazione!!!
Il codice F# a differenza di quello C# non ha parentesi {} ma utilizza l’indentazione per definire la struttura e lo scopo del codice. Ad esempio volendo definire una variabile in un modulo è necessario scrivere:

module Integers =
    let sampleInteger = 176
 
​    /// Do some arithmetic starting with the first integer
    let sampleInteger2 = (sampleInteger/4 + 5 - 7) * 4

e il compilatore comprende che le variabili sampleInteger e sampleInteger2 sono definite nello scopo del modulo Integers.
Esistono essenzialmente due principali modi per aggregare le funzioni in F#: i namespaces e i modules. In effetti tutto il codice scritto all’interno di uno specifico file, ie. Esempio.fs viene riguardato dal compilatore come appartenente al modulo con nome Esempio come quello del file.
I namespaces in F# hanno la stessa funzione espletata in altri linguaggi come C#, ad esempio, e permettono di contenere moduli (modules) e definizione di tipi (type definitions) ma non possono contenere direttamente funzioni e variabili.
Si dichiarano semplicemente come :

namespace IlMioProgetto.Parte1

potendo anche creare namspaces compositi. Per quanto riguarda altre osservazioni, il modo di referenziare un namespace nel codice è identico a quello utilizzato in altri linguaggi, come C# ad esempio, e cioè in maniere completa o importandolo.

​//Fully qualified name
let now = System.DateTime.Now
//Imported namespace
open System
let today = DateTime.Now.Date

I moduli (modules) sono simili ai namespaces sotto un certo riguardo, in quanto forniscono un contenitore dove incapsulare il codice, solo che possono contenere direttamente dichiarazioni di variabili e funzioni. Questi possono essere innestati uno dentro l’altro, ad esempio:

​module ModuleEsterno
module ModuleInterno =
faiLaCosa ()

e possono essere dichiarati all’interno di una specifico namespace:

​module ilMioNameSpace.Programma1.IlMioPrimoModulo

e cioè in questo caso IlMioPrimoModulo è definito nel namespace ilMioNameSpace.Programma1.
La parola open permette di referenziare il modulo in maniera semplice ed efficace:

​open ilMioNameSpace.Programma1.IlMioPrimoModulo

L’osservazione più importante da fare su F# è che questi è un linguaggio funzionale e cioè ogni parte atomica di codice ritorni un risultato laddove altri linguaggi esercitino questo paradigma solo nelle funzioni (metodi) o negli operatori. Inoltre in F, laddove non specificato esplicitamente, tutte le variabili sono immutabili e cioè una volta assegnate siano “read only” e cioè di sola lettura.
Partendo dal semplice modulo seguente abbiamo:

module sample =
    let sampleInteger = 0 
    let sampleInteger2 = (sampleInteger/4 + 5 - 7) * 4
    sampleInteger2 = sampleInteger*3
    printfn "Valore finale: %A" sampleInteger2

selezionandolo e premendo Alt-Enter questi viene eseguito nella finestra F# Interactive Window dove si evidenzia il fatto che la variabile sampleInteger2 non modifichi il valore dopo la prima elaborazione.


 
Un’altra osservazione che può essere fatta su F# è che il compilatore inferisca il tipo della variabile dall’operazione presente sulla parte destra dell’uguale.

let add x y = x + y

Tirando le somme è possibile dire che F# sia un linguaggio funzionale nel senso che un programma o script sia una catena di funzioni (piping) ognuna delle quali può modificare lo stato di quella successiva. Non utilizzando l’approccio imperativo è possibile limitare i cosiddetti “side-effects” e cioè una funzione non può modificare indirettamente il risultato di un'altra funzione.

Ad esempio se passiamo gli argomenti 1 e 7 alla funzione: f(x, y) -> x * y questa restituirà sempre 7. Laddove utilizzando un paradigma imperativo il risultato potrebbe dipendere dallo stato di variabili non locali alla funzione, e la funzione stessa potrebbe modificare direttamente il valore di variabili esterne ad essa.