Doesn't this violate "UpValues before DownValues"?
If I understand correctly, "UpValues before DownValues" applies when you have two definitions that match simultaneously. Using your function names, we'd need UpValues for f and DownValues for g that both apply (and the UpValues for f would apply first).
What you have is UpValues for f and DownValues for f. Those aren't in contention when evaluating g[f[3]], because f[3] will evaluate according to the DownValues and then the evaluator will move on with that result as the argument to g.
Here is a demonstration:
f /: g[arg : f[_]] := "upvalue of f: g[" <> ToString[arg] <> "]"; g[f[a]] (* "upvalue of f: g[f[a]]" *)
Now add a definition:
g[arg_] := "downvalue of g: g[" <> ToString[arg] <> "]"; g[f[a]] (* "upvalue of f: g[f[a]]" *) (* demonstrating UpValues before DownValues *)
Add another definition:
f[arg_] := "downvalue of f: f[" <> ToString[arg] <> "]"; g[f[a]] (* "downvalue of g: g[downvalue of f: f[a]]" *)
For your particular problem, maybe something like this would work:
f /: Length[f[x_]] := "cheap" /; x < 100; f /: Length[f[x_]] := "expensive" /; x >= 100
Without more context, I'm not sure what else to suggest.
EDIT: Thought of something else
You might want a custom Length function.
f[x_] := Pause[x]; SetAttributes[MyLength, HoldAllComplete]; MyLength[f[x_]] := (f[x]; "cheap f, length based on evaluating f") /; x < 10; MyLength[f[x_]] := "expensive f, length bypassed f" /; x >= 10
f[100]to be inert sinceLengthhas noHold*attribute $\endgroup$