This function packages the shared structure we need for all of these list-scanners but, in the process, it's obliged to do the work of all the list scanners no matter which one we're actually doing.  So, in order to compute the length, you also compute the mean, variance, max and min.  This is suboptimal.  Another approach would be to have one function per list-scanner, all calling a common back-end to do the repeated structure.  We could do that, for example, with <code>
static bool FnCallListTraverse(EvalContext *ctx, FnCall *fp, Rlist *finalargs, bool (*digest)(const Rlist *, void *), void *data)
{
    const char *name = RlistScalarValue(finalargs);
    Rval rv;
    if (!GetListReferenceArgument(ctx, fp, name, &rv, NULL)) { return false; }
    for (const Rlist *rp = RvalRlistValue(rv); rp != NULL; rp = rp->next)
    { if (!digest(rp, data)) return false; }
    return true;
}
</code> with each of the particular list-scanners then defining a function and a type for its data, e.g. <code>
typedef struct { double mean, square; int count; } varydata_t;
static bool Welford(const Rlist *rp, void *d)
{
   varydata_t data = (varydata_t *)d;
   const char *cur = RlistScalarValue(rp);
   double x;
   if (strcmp(cur, CF_NULL_VALUE) == 0 || sscanf(cur, "%lf", &x) != 1) { return false; }
   double delta = x - data.mean;
   data.mean += delta / ++data.count;
   data.square += delta * (x - data.mean);
   return true;
}
static FnCallResult FnCallVariance(EvalContext *ctx, FnCall *fp, Rlist *finalargs)
{
    varydata_t data = { 0., 0., 0 };
    if (!FnCallListTraverse(ctx, fp, finalargs, &Welford, (void*)&data) || data.count == 0) { return FnFailure(); }
    /* assert(data.count > 1 || data.square is tiny); */
    return FnReturnF("%lf", --data.count ? data.square / data.count : 0);
}
</code>
Thus the scanners share their traversal code without duplicating each other's work.