Using Types to Designing Domain
Do you have only a limited number of metric types?

In which case, you might want to make a union case of the possible types.
The compound cases can be recursive, referring to the type.

type Metric = 
    | Revenue of string * string 
    | Volume  of string * string
    | Compound  of string * Metric list


Then your formatMetric function needs to handle each case separately

let formatMetric metric =
    match metric with
    | Revenue (label,formula) -&gt; 
        sprintf "%s: {%s}" label formula
    | Volume  (label,formula) -&gt; 
        sprintf "%s: {%s}" label formula
    | Compound (label,metrics) -&gt; 
        sprintf "%s: {%s}" label "other metrics"


Note that that if you use this approach you don't really need the label any more, as the cases are the label.

type Metric = 
    | Revenue of string 
    | Volume  of string
    | Compound  of Metric list


And then you could also make formatMetric recursive, printing all the child formulas for a compound formula

let rec formatMetric metric =
    match metric with
    | Revenue formula -&gt; 
        sprintf "Revenue={%s}" formula
    | Volume  formula -&gt; 
        sprintf "Volume={%s}" formula
    | Compound metrics -&gt; 
        let childFormats = 
            metrics 
            |&gt; List.map formatMetric
            |&gt; List.reduce (+) 
        sprintf "Compound: {%s}" childFormats


The formatting is not exactly what you want, but I'm sure you get the general idea.

EDIT

General Comment

Personally, I wouldn't add those member functions to the type. I would focus on designing the types in their own module independent of any particular representation, and then have a separate module that mapped them into the appropriate representation for MongoDB.  That way you are not mixing up different requirements.  

String join.

A simple string join is trivial to write:

let stringJoin sep list :string = 
    let append s t = s + sep + t
    list |&gt; List.reduce append

stringJoin ", " ["a"; "b"; "c"; ]


Separate the types from the representation

But if you separate the types from the representation, you may find that you naturally create MongoDB specific functions that generate the appropriate representation. The idea is to build up a mini-language that you can compose together to make bigger blocks.
I don't know the MongoDB syntax, but maybe something like this?

let mSum p = sprintf "{ $sum: ""%s"" }" p
let mDivide p q = sprintf "{ $divide: [""%s"", ""%s""] }" p q
let mLabel p q = sprintf "%s : %s" s
let mGroup id ps = sprintf "$group: { %s, %s }" id (stringJoin ", " ps)
let mProject p q = sprintf "$project: { %s }" (stringJoin ", " ps)


If you are doing a lot of this, you might be better off using a JSON library. In which case the workflow would be:

domain type ==&gt;  convert to intermediate type for Mongo API ==&gt; convert to JSON