Diciamo che ho una lista e un set e voglio fare alcune cose con loro:F #: uccidere la ridondanza in Map/Reduce/Filter
let outA = inA |> List.map(fun x -> x + 1) |> List.filter(fun x -> x > 10)
let outB = inB |> Set.map(fun x -> x + 1) |> Set.filter(fun x -> x > 10)
Ora, ovviamente una maniglia liste e B maniglie set. Tuttavia, trovo molto fastidioso dover scrivere ancora List.
List.
: non solo è verbose, ripetitivo boilerplate che non trasmette informazioni e interferisce con la lettura della funzionalità del codice, ma è anche di fatto scrivi annotazioni che devo tenere traccia e mantenere sincronizzate con il resto del mio codice.
Quello che voglio essere in grado di fare è qualcosa di simile al seguente:
let outA = inA |> map(fun x -> x + 1) |> filter(fun x -> x > 10)
let outB = inB |> map(fun x -> x + 1) |> filter(fun x -> x > 10)
con il compilatore sapendo che inA è una lista e inb è un insieme, e, quindi, tutte le operazioni sono dalla classe corretta e quindi outA è una lista e outB è un insieme. Posso parzialmente ottenere questo risultato con Seq:
let map(x) =
Seq.map(x)
let filter(x) =
Seq.filter(x)
E posso scrivere esattamente questo. Il problema con questo è che inserisce tutto in Sequenze e non posso più eseguire operazioni di elenco/set su di essi. Analogamente,
let outA = inA.Select(fun x -> x + 1).Where(fun x -> x > 10)
let outB = inB.Select(fun x -> x + 1).Where(fun x -> x > 10)
Rimuove anche tutto ciò, ma quindi trasmette tutto a IEnumerable. Sono riuscito ad ottenere abbastanza bello convertendo tutti i metodi statici in metodi di istanza tramite estensioni:
type Microsoft.FSharp.Collections.List<'a> with
member this.map(f) = this |> List.map(f)
member this.filter(f) = this |> List.filter(f)
let b = a.map(fun x -> x + 1).filter(fun x -> x > 10)
ma ho il sospetto che si incorrere in problemi di tipo inferenza qui menzionati: Method Chaining vs |> Pipe Operator? Non lo so davvero; Non ho familiarità con il funzionamento dell'algoritmo di inferenza del tipo.
La linea di fondo è, immagino che sto andando a fare un sacco di queste operazioni lista/serie/matrice mappa/riduci/filtro e voglio renderle più belle e pulite possibile. In questo momento, oltre a distogliermi dai bit importanti dell'espressione (cioè la "mappa" e il lambda), forniscono anche annotazioni di tipo de-facto in un punto in cui il compilatore dovrebbe sapere-jolly-bene quale sia la collezione entrare è
Inoltre, questo è esattamente il tipo di cosa che l'eredità e il polimorfismo OO sono pensati per risolvere, quindi mi chiedevo se ci fosse qualche schema funzionale equivalente che non so che lo risolverebbe altrettanto elegantemente. Forse potrei fare un controllo di tipo nel mio statico map
ed eseguire la funzione map
del tipo rilevante sull'input?
Qualcuno ha una soluzione migliore di quelle che ho già provato, o sto sbattendo la testa in una limitazione fondamentale del motore di inferenza di tipo F #, che dovrei semplicemente accettare e andare avanti?
A mio parere, la il nome del modulo rende il codice più descrittivo. Sai che è un elenco/set, solo guardando il codice. – ebb
Fondamentalmente vuoi classi di tipi :) Fai clic su Haskell, o rimani con noi e utilizza nomi qualificati. –
@ebb: Ma il punto di inferenza del tipo era che non avevo bisogno di descrivere il codice così tanto = D. –