Home > OS >  F# using a list to recursively build a Fable.Forms object
F# using a list to recursively build a Fable.Forms object

Time:08-02

I have a recursive problem trying to use a library to generate forms dynamically. The library is Fable.Forms

If we were basing my problem with that page's first example, my goal would be to have email and password (and more fields obviously) in a collection, then have a recursive process that builds the form dynamically.

Here's a naive version of "solving" the problem:

    let fields = [ fieldA; fieldB ]

    match fields |> List.length with
    | 1 ->
        Form.succeed (fun _ -> Nothing)
        |> Form.append (fields |> List.head)
    | 2 ->
        Form.succeed (fun _ _ -> Nothing)
        |> Form.append (fields |> List.head)
        |> Form.append (fields |> List.tail |> List.head)
    | _ -> failwith "..... gotta solve using tail recursion!!"

My problem is that I need to know the exact number of fields in advance to give to the Form.succeed anonymous function the correct number of "input" arguments.

eg, if the list has:

  • 1 fields, I need to call Form.succeed with (fun ? -> ...) then append fields in list
  • 2 fields, I need to call Form.succeed with (fun ? ? -> ...) then append fields in list
  • 3 fields, I need to call Form.succeed with (fun ? ? ? -> ...) then append fields in list.

CodePudding user response:

There is no way to create an anonymous function with a number of arguments that is not known at compile time, so this will not work.

I think the Forms library is really mainly designed for cases where you know the structure of the form statically, so it may not be such a great fit here.

However, you could avoid the issue by collecting all results into a list of key-value pairs. That way, the form will always produce list<string * string> and so the type will be the same, regardless of how many form elements you'll have.

You do not need explicit recursion for this, because you can use List.reduce. Something like this should do the trick:

// A list of form fields returning 'string' together with their keys
let fields : list<string * Form.Form<obj, string, obj>> = 
  [ "field1", Form.textField (failwith "!")
    "field1", Form.textField (failwith "!")]

// Produce a single merged form
let merged = 
  fields 
  |> List.map (fun (name, form) ->
      // First, turn each individual form into one returning key-value pair
      Form.succeed (fun result -> [name, result])
      |> Form.append (form))
  |> List.reduce (fun form1 form2 ->
      // Merge forms by taking two, appending them and concatenating the
      // two lists of key-value pairs they produce (to get result of the same type)
      Form.succeed (fun r1 r2 -> r1 @ r2 )
      |> Form.append form1
      |> Form.append form2)
  • Related