2010-08-29 19 views

risposta

11

Non che io sappia; senza supporto integrato nel linguaggio/compilatore, mi aspetto che l'unica alternativa sia una versione basata su reflection. (Non so come sia implementato lo Uniplate, vero?

Ecco il codice per una versione basata su riflessione basata sull'esempio della presentazione originale. Non ho pensato profondamente ai suoi limiti, ma era molto più semplice scrivere di quanto avrei immaginato.

type Company = C of Dept list 
and Dept = D of Name * Manager * SubUnit list 
and SubUnit = | PU of Employee | DU of Dept 
and Employee = E of Person * Salary 
and Person = P of Name * Address 
and Salary = S of float 
and Manager = Employee 
and Name = string 
and Address = string 

let data = C [D("Research",E(P("Fred","123 Rose"),S 10.0), 
        [PU(E(P("Bill","15 Oak"),S 5.0))])] 
printfn "%A" data 

open Microsoft.FSharp.Reflection 
let everywhere<'a,'b>(f:'a->'a, src:'b) = // ' 
    let ft = typeof<'a>    // ' 
    let rec traverse (o:obj) = 
     let ot = o.GetType() 
     if ft = ot then 
      f (o :?> 'a) |> box // ' 
     elif FSharpType.IsUnion(ot) then 
      let info,vals = FSharpValue.GetUnionFields(o, ot) 
      FSharpValue.MakeUnion(info, vals |> Array.map traverse) 
     else 
      o 
    traverse src :?> 'b  // ' 

let incS (S x) = S(x+1.0) 

let newData = everywhere(incS, data) 
printfn "%A" newData 

La funzione everywhere attraversa l'intera struttura di un DU arbitraria e applica la funzione f a ciascun nodo che è il tipo che f funziona, lasciando tutti gli altri nodi così com'è.

+0

Questo è un buon suggerimento, dovrò pensare alle implicazioni delle prestazioni di un tale approccio. In ogni caso, probabilmente non ha importanza per il mio caso particolare. –

+0

re: la tua domanda su come implementare Uniplate la fonte è disponibile su http://community.haskell.org/~ndm/darcs/uniplate/. –

+0

Ho accettato la tua risposta poiché ritengo sia un buon approccio, ma apporto una modifica per farlo funzionare correttamente: Ho sostituito "ft = ot" con "ot.IsSubclassOf (ft)" - altrimenti non riesce a corrispondere quando il tipo di argomento di f, cioè 'a è più generico rispetto all'argomento specifico che viene passato. –