How do I write a computation expression builder that accumulates a value and also allows standard language constructs?

前端 未结 4 1541
悲哀的现实
悲哀的现实 2021-02-03 10:34

I have a computation expression builder that builds up a value as you go, and has many custom operations. However, it does not allow for standard F# language constructs, and I\'

4条回答
  •  悲哀的现实
    2021-02-03 10:40

    Following a similar struggle to Joel's (and not finding §6.3.10 of the spec that helpful) my issue with getting the For construct to generate a list came down to getting types to line up properly (no special attributes required). In particular I was slow to realise that For would build a list of lists, and therefore need flattening, despite the best efforts of the compiler to put me right. Examples that I found on the web were always wrappers around seq{}, using the yield keyword, repeated use of which invokes a call to Combine, which does the flattening. In case a concrete example helps, the following excerpt uses for to build a list of integers - my ultimate aim being to create lists of components for rendering in a GUI (with some additional laziness thrown in). Also In depth talk on CE here which elaborates on kvb's points above.

    module scratch
    
        type Dispatcher = unit -> unit
        type viewElement = int
        type lazyViews = Lazy>
    
        type ViewElementsBuilder() =                
            member x.Return(views: lazyViews) : list = views.Value        
            member x.Yield(v: viewElement) : list = [v]
            member x.ReturnFrom(viewElements: list) = viewElements        
            member x.Zero() = list.Empty
            member x.Combine(listA:list, listB: list) =  List.concat [listA; listB]
            member x.Delay(f) = f()
            member x.For(coll:seq<'a>, forBody: 'a -> list) : list  =         
                // seq {for v in coll do yield! f v} |> List.ofSeq                       
                Seq.map forBody coll |> Seq.collect id  |> List.ofSeq
    
        let ve = new ViewElementsBuilder()
        let makeComponent(m: int, dispatch: Dispatcher) : viewElement = m
        let makeComponents() : list = [77; 33]
    
        let makeViewElements() : list =         
            let model = {| Scores = [33;23;22;43;] |> Seq.ofList; Trainer = "John" |}
            let d:Dispatcher = fun() -> () // Does nothing here, but will be used to raise messages from UI
            ve {                        
                for score in model.Scores do
                    yield makeComponent (score, d)
                    yield makeComponent (score * 100 / 50 , d)
    
                if model.Trainer = "John" then
                    return lazy 
                    [ makeComponent (12, d)
                      makeComponent (13, d)
                    ]
                else 
                    return lazy 
                    [ makeComponent (14, d)
                      makeComponent (15, d)
                    ]
    
                yield makeComponent (33, d)        
                return! makeComponents()            
            }
    
    
    

提交回复
热议问题