Shorthand Combined Functions

JavaScript (ES6),  174  171 bytes

(d,o,f,v,g=(f,V=v)=>f?V.map(v=>f+`(${v})`):V)=>f+f&&v+v?(d<'»'?f[1]?f.map(f=>g(f).join``):g(f[0]):v.map((_,i)=>v.map((_,j)=>g(f[j],[v[i++%v.length]])).join``)).join(o):''

Try it online!

Commented

( d,                              // d   = direction
  o,                              // o   = operator
  f,                              // f[] = list of functions
  v,                              // v[] = list of variables
  g = (f, V = v) =>               // g = helper function taking (f, V[])
    f ?                           //   if f is defined:
      V.map(v =>                  //     for each variable v in V[]:
        f + `(${v})`              //       return f, followed by '(v)'
      )                           //
    :                             //   else:
      V                           //     just return V[] as-is
) =>                              //
  f + f && v + v ?                // if both f[] and v[] are non-empty:
    ( d < '»' ?                   //   if the direction is '«':
        f[1] ?                    //     if there are at least 2 functions:
          f.map(f =>              //       for each function f_i in f[]:
            g(f).join``           //         return 'f_i(v_0)f_i(v_1)...'
          )                       //       end of map()
        :                         //     else:
          g(f[0])                 //       return [ 'f_0(v_0)', 'f_0(v_1)', ... ]
      :                           //   else (the direction is '»'):
        v.map((_, i) =>           //     for each variable at position i:
          v.map((_, j) =>         //       for each variable at position j:
            g(                    //         return [ 'f_j(v_k)' ]
              f[j],               //         where k = i mod v.length
              [v[i++ % v.length]] //         and increment i
            )                     //
          ).join``                //       end of inner map(); join the list
        )                         //     end of outer map()
    ).join(o)                     //   join the final list with the operator
  :                               // else:
    ''                            //   just return an empty string

05AB1E, 63 bytes

UÇ`θiVεY€…(ÿ)«}ëDg©DIgα+>∍Œ®ùεζεðK'(ýyðå≠i')«]Dg≠iJ}¸˜Xý³gIg*Ā×

Four loose inputs in the order operator, direction, [variables], [functions].

Try it online or verify all test cases.

Explanation:

U                   # Pop and store the 1st (implicit) input `operator` in variable `X`
Ç                   # Convert the 2nd (implicit) input `direction` to a list of codepoint
 `                  # Push this codepoint to the stack
  θ                 # Only leave its last digit
   i                # If this digit is 1 (so the direction is "«"):
    V               #  Pop and store the 3rd (implicit) input `variables` in variable `Y`
    ε       }       #  Map over the 4th (implicit) input `functions`:
     Y              #   Push the `variable`-list from variable `Y`
      €…(ÿ)         #   Surround each variable with parenthesis
           «        #   And append it to the current `function` we're mapping
   ë                # Else (so the direction is "»" instead):
    D               #  Duplicate the 3rd (implicit) input `variables`
     g              #  Pop and push the length to get the amount of `variables`
      ©             #  Store it in variable `®` (without popping)
       D            #  Duplicate this length
        I           #  Push the fourth input `functions`
         g          #  Pop and take its length as well
          α         #  Take the absolute difference between the two amounts
           +        #  And add it to the duplicated amount of `variables`
            >       #  And increase it by 1
             ∍      #  Then extend the duplicated `variables` list to that length
    Œ               #  Get all sublists of this list
     ®ù             #  Only leave the sublists of a length equal to variable `®`
       ε            #  Map each remaining sublist to:
        ζ           #   Create pairs with the 4th (implicit) input `functions`,
                    #   with a default space as filler character if the lengths differ
         ε          #   Map each pair:
          ðK        #    Remove the spaces
          '(ý      '#    Join the pair with a ")" delimiter
          yðå≠i     #    If the current pair did NOT contain a space:
               ')« '#     Append a ")"
]                   # Close all open if-statements and maps
 D                  # Duplicate the resulting list
  g≠i }             # Pop and if its length is NOT 1:
     J              #  Join the inner-most list together to a string
       ¸            # Then wrap it into a list (in case it wasn't a list yet)
        ˜           # And deep flatten the list
         Xý         # Join it by the operator we stores in variable `X`
           ³g       # Get the length of the 3rd input-list `variables`
             Ig     # And the length of the 4th input-list `functions`
               *    # Multiply them with each other
                Ā   # Truthify it (0 remains 0; everything else becomes 1)
                 ×  # And repeat the string that many times
                    # (so it becomes an empty string if either list was empty,
                    #  or remains unchanged otherwise)
                    # (after which this is output implicitly as result)