Da un punto di vista di programmazione di alto livello, da dove proviene davvero la barriera del "diverso paradigma" tra C # e F #?

6

Sono consapevole del fatto che entrambi utilizzano paradigmi di programmazione diversi, ma da una prospettiva di alto livello oltre alla sintassi diversa sembra che la maggior parte dei compiti di base possano essere raggiunti in modo simile.

Dico questo solo perché quando in precedenza ho toccato linguaggi di programmazione funzionali come Haskell, scrivere codice per i compiti di base era (inizialmente) difficile, frustrante e richiedeva una mentalità completamente diversa.

Ad esempio quanto segue impiega un po 'di tempo per capire come utilizzare la sintassi ricorsiva:

loop :: Int -> IO ()
loop n = if 0 == n then return () else loop (n-1)

Dove un loop F # è riconoscibile e comprensibile quasi immediatamente:

let list1 = [ 1; 5; 100; 450; 788 ]
for i in list1 do
   printfn "%d" i

Quando i programmatori C # iniziano ad apprendere F #, si consiglia di ripensare completamente il loro schema di pensiero (che era sicuramente richiesto per Haskell), ma ora ho scritto diversi programmi F # che trattano condizioni, loop, set di dati, ecc. compiti, e mi chiedo dove diavolo sia la barriera del "paradigma diverso"?

    
posta FBryant87 24.08.2013 - 14:53
fonte

4 risposte

7

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.

    
risposta data 27.08.2013 - 16:37
fonte
9

I'm wondering where the 'different-paradigm' barrier really kicks in?

In gran parte non esiste una "barriera". Sei libero di scrivere F # non-idiomatico tutto ciò che desideri, proprio come potresti programmare C # per anni e non praticare la programmazione orientata agli oggetti.

L'unico caso in cui posso davvero vedere forzare la mano è lavorare con altri sviluppatori. Vedrai il loro stile con la lingua e dovrai adattarti a usarlo. Ancora più importante, non rappresentano la possibilità di scrivere loop in una base di codice funzionale.

    
risposta data 24.08.2013 - 15:34
fonte
5

F # è un linguaggio ibrido: è principalmente un linguaggio funzionale, ma può anche fare praticamente tutto ciò che C # può fare. Quindi, puoi usare variabili mutabili, collezioni modificabili, metodi con effetti collaterali e gerarchie di ereditarietà ovunque. Ma se lo fai, scoprirai che F # non è un ottimo linguaggio imperativo e sarebbe stato meglio se tu fossi rimasto con C #.

D'altro canto, se vuoi scrivere una F # idiomatica, dovrai imparare come usare variabili immutabili, collezioni immutabili, pure funzioni, unioni discriminate e la corrispondenza dei modelli.

Per riassumere, se vuoi veramente approfittare di ciò che F # può offrire, dovrai davvero ripensare a come codifichi, anche se probabilmente non così radicalmente come è richiesto per Haskell.

    
risposta data 24.08.2013 - 17:16
fonte
4

Commenterò dall'esperienza dell'uso di entrambe le lingue (C # e F #) e dei "dossi" che ho trovato. (Sono d'accordo sul fatto che non ci siano barriere, ma ci sono sicuramente "urti" nella strada).

Per prima cosa ho usato F # per lo scripting tramite fsi perché non è possibile farlo con C # e ho imparato alcune cose paradigmatiche funzionali. F # ha svolto un lavoro molto buono e conciso perché l'attività in corso era adatta alla programmazione funzionale (essenzialmente era map / reduce).

L'ostacolo più grande consisteva nel "dimenticare" gli oggetti e nel rendersi conto che non ho per usare loop e condizionali. Ho passato molto tempo in questo spazio "scripting". (Ho guardato indietro su qualche vecchia F # e ho visto come stavo cercando di forzarlo in passato).

Ora sto facendo il mio primo grande lavoro in F #: una DSL. È una lingua più adatta per questo compito. Uso ancora C # - questo è il mio linguaggio 'sistemi'. Oggetti in movimento e mutabilità si adattano al modello mentale che ho di come funziona.

Direi che è più adatto per il lavoro piuttosto che fare un passaggio totale. Il grande vantaggio è che ti fa pensare in modo diverso a come risolvere vari problemi. Da quando ho appreso F # la mia C # è migliorata in modo massiccio.

    
risposta data 24.08.2013 - 21:40
fonte

Leggi altre domande sui tag