open Infers type GM<'w, 'p> = ('p -> 'p) -> 'w -> 'w type GMap<'w, 'p> = {gm: GM<'w, 'p>} type [] GMap () = member g.same () : GMap<'p, 'p> = {gm = id} member g.notSame (gm: GM<'w, 'p>) : GMap<'w, 'p> = {gm = gm} member g.int () : GM = fun _ -> id member g.string () : GM = fun _ -> id member g.bool () : GM = fun _ -> id member g.option (wG: GMap<'w, 'p>) : GM, 'p> = wG.gm >> Option.map member g.pair (vG: GMap<'v, 'p>, wG: GMap<'w, 'p>) : GM<'v * 'w, 'p> = fun p2p (v, w) -> (vG.gm p2p v, wG.gm p2p w) member g.list (wG: GMap<'w, 'p>) : GM, 'p> = wG.gm >> List.map // A bit of boilerplate (could be mostly eliminated) static member make () : GM<'w, 'p> = match Engine.TryGenerate (GMap ()) with | None -> failwithf "GMap: Unsupported type %A" typeof<'w> | Some gmap -> gmap.gm static member Get () = StaticMap.Memoize GMap.make // User interface let gmap (p2p: 'p -> 'p) : 'w -> 'w = GMap.Get () p2p // Examples do gmap ((+) 1) [("vesa", 1)] |> printfn "%A" do gmap (fun (x: string) -> x.ToUpper ()) [("vesa", 1)] |> printfn "%A"