F # supporta la programmazione orientata agli oggetti, ma OOP non è veramente un F #. OOP incoraggia il raggruppamento di dati e comportamenti mentre la programmazione funzionale incoraggia la loro separazione.
Dai un'occhiata alla differenza tra le classi System.Collections.Generic.List<T>
e Microsoft.FSharp.Collections.List<'T>
.
La classe regolare (OOP) ha un ampio elenco di metodi e metodi di estensione, ad esempio List<T>.Add(T)
. Al contrario, l'elenco F # ha solo poche proprietà mentre tutte le funzioni correlate sono raggruppate in un modulo List.
Esaminiamo un esempio OOP familiare:
public interface IAnimal
{
void Speak();
}
public class Duck : IAnimal
{
public void Speak()
{
Console.WriteLine("Quack!");
}
}
public class Cat : IAnimal
{
public void Speak()
{
Console.WriteLine("Meow!");
}
}
Potresti tradurlo in F # (non idiomatico):
type IAnimal =
abstract member Speak : unit -> unit
type Duck =
interface IAnimal with
member this.Speak () -> printfn "Quack!"
type Cat =
interface IAnimal with
member this.Speak () -> printfn "Meow!"
Puoi vedere che il codice è più corto ma non molto diverso. Se ti ritrovi a scrivere un codice come questo, stai pensando come un C # dev :) Ecco una traduzione più idiomatica:
type Animal =
| Duck
| Cat
let speak animal =
match animal with
| Duck -> printfn "Quack!"
| Cat -> printfn "Meow!"
Ecco una versione alternativa di speak
:
let speak = function
| Duck -> printfn "Quack!"
| Cat -> printfn "Meow!"
A questo punto, il codice sembra molto diverso rispetto alla versione C # in gran parte perché abbiamo separato il comportamento ( speak
o Speak()
) dai dati (in questo caso, solo il tipo di Animal
o IAnimal
noi abbiamo). Un'altra differenza è che abbiamo espresso i possibili stati di dati (tipi di animali) in un modo in cui il compilatore (e il sistema di caratteri) comprendono più intimamente del compilatore / tipo di C #. Considera una funzione che ottiene il primo elemento in un elenco:
public T GetHead(List<T> list)
{
return list[0];
}
vs
let getHead = function
| head :: tail -> head
Entrambi prendono un elenco idiomatico e restituiscono il primo elemento in esso ( ::
è l'operatore di destrutturazione della lista).
Se dovessi compilare entrambi, la versione di C # verrebbe compilata senza errori o avvisi e la versione di F # ti avvertirà che non hai considerato tutti i casi (che ne dici della lista vuota?). Il compilatore F # ti rende conto di non aver tenuto conto di tutti gli input possibili e ti avverte di conseguenza.
Questa è una delle altre differenze tra i linguaggi, come programmatore (se hai un codice idiomatico), puoi affidarti al compilatore molto di più per individuare errori e bug che C # / OOP non catturerebbe fino al runtime.