F # Funzioni vs. Valori

Questa è una domanda piuttosto semplice, e volevo solo verificare che quello che sto facendo e il modo in cui sto interpretando l’F # abbia senso. Se ho la dichiarazione

let printRandom = x = MyApplication.getRandom() printfn "%d" x x 

Invece di creare printRandom come funzione, F # lo esegue una volta e poi assegna a esso un valore. Quindi, ora, quando chiamo printRandom, invece di ottenere un nuovo valore casuale e stamparlo, ottengo semplicemente tutto ciò che è stato restituito la prima volta. Posso aggirare questo definendolo come tale:

 let printRandom() = x = MyApplication.getRandom() printfn "%d" x x 

È questo il modo corretto per tracciare questa distinzione tra funzioni e valori senza parametri? Questo mi sembra poco ideale. Ha conseguenze in termini di curriculum, composizione, ecc.?

Il modo giusto di guardare a questo è che F # non ha funzioni paramebili. Tutte le funzioni devono prendere un parametro, ma a volte non ti importa di cosa si tratta, quindi usi () (il valore singleton dell’unità di tipo). Potresti anche fare una funzione come questa:

 let printRandom unused = x = MyApplication.getRandom() printfn "%d" x x 

o questo:

 let printRandom _ = x = MyApplication.getRandom() printfn "%d" x x 

Ma () è il modo predefinito per esprimere che non si utilizza il parametro. Esprime questo fatto al chiamante, perché il tipo è unit -> int not 'a -> int ; così come al lettore, perché il sito di chiamata è printRandom () non printRandom "unused" .

Il curry e la composizione fanno infatti affidamento sul fatto che tutte le funzioni accettano un parametro e restituiscono un valore.

Il modo più comune per scrivere chiamate con l’unità, a proposito, è con uno spazio, specialmente nei parenti non .NET di F # come Caml, SML e Haskell. Questo perché () è un valore singleton, non una cosa sintattica come in C #.

La tua analisi è corretta.

La prima istanza definisce un valore e non una funzione. Ammetto che questo mi ha sorpreso alcune volte quando ho iniziato anche con F #. Provenendo da C # sembra molto naturale che un’espressione di assegnazione che contiene più affermazioni deve essere una lambda e quindi valutare il ritardo.

Questo non è il caso di F #. Le istruzioni possono essere annidate in modo quasi arbitrario (e vengono utilizzate per avere funzioni e valori di ambito locale). Una volta che ci si sente a proprio agio con questo, si inizia a vederlo come un vantaggio in quanto è ansible creare funzioni e continuazioni che sono inaccessibili al resto della funzione.

Il secondo approccio è il modo standard per creare una funzione che logicamente non accetta argomenti. Non conosco la terminologia precisa che la squadra di F # userebbe per questa dichiarazione (forse una funzione che prende un singolo argomento di tipo unit ). Quindi non posso davvero commentare come potrebbe influenzare il curry.

È questo il modo corretto per tracciare questa distinzione tra funzioni e valori senza parametri? Questo mi sembra poco ideale. Ha conseguenze in termini di curriculum, composizione, ecc.?

Sì, quello che descrivi è corretto.

Per quello che vale, ha una conseguenza molto interessante in grado di valutare parzialmente le funzioni sulla dichiarazione. Confronta queste due funzioni:

 // val contains : string -> bool let contains = let people = set ["Juliet"; "Joe"; "Bob"; "Jack"] fun person -> people.Contains(person) // val contains2 : string -> bool let contains2 person = let people = set ["Juliet"; "Joe"; "Bob"; "Jack"] people.Contains(person) 

Entrambe le funzioni producono risultati identici, contains crea le sue persone impostate su dichiarazione e la riutilizza, mentre contains2 crea le sue persone impostate ogni volta che si chiama la funzione. Risultato finale: contains è leggermente più veloce. Quindi conoscere la differenza qui può aiutarti a scrivere codice più veloce.

I corpi di assegnazione che assomigliano ai corpi delle funzioni hanno catturato alcuni programmatori inconsapevoli. Puoi rendere le cose ancora più interessanti avendo il compito restituire una funzione:

 let foo = printfn "This runs at startup" (fun () -> printfn "This runs every time you call foo ()") 

Ho appena scritto un post sul blog su http://blog.wezeku.com/2010/08/23/values-functions-and-a-bit-of-both/ .