Wow @generated is like having a superpower in your back pocket. Thanks @CameronBieganek for that!
Also: its performance is great. I think I was mistaken previously.
Third-pass Code for a typed arbitrary-index partial application functor
struct Fix{F,fixinds,V<:Tuple,KW<:NamedTuple}
    f::F
    fixvals::V
    fixkwargs::KW
    Fix{F,fixinds,V,KW}(f,fixvals,fixkwargs) where {F,fixinds,V,KW} = begin
        orderok(a, b) = a < b || (a > 0 && b < 0) # not a perfect test ... just want args ordered left to right
        length(fixinds) > 1 && @assert all(orderok.(fixinds[begin:end-1], fixinds[begin+1:end]))
        new{F,fixinds,V,KW}(f,fixvals,fixkwargs)
    end
end
Fix{fixinds}(f, fixvals; fixkwargs...) where {fixinds} = 
    Fix{typeof(f), fixinds, typeof(fixvals), typeof((; fixkwargs...))}(f, fixvals, (; fixkwargs...))
@generated (f::Fix{F,fixinds,V,KW})(args...; kwargs...) where {F,fixinds,V,KW} = begin
    combined_args = Vector{Expr}(undef, length(fixinds)+length(args))
    args_i = fixed_args_i = 1
    for i ∈ eachindex(combined_args)
        if any(==(fixinds[fixed_args_i]), (i, i-length(combined_args)-1))
            combined_args[i] = :(f.fixvals[$fixed_args_i])
            fixed_args_i = clamp(fixed_args_i+1, eachindex(fixinds))
        else
            combined_args[i] = :(args[$args_i])
            args_i += 1
        end
    end
    :(f.f($(combined_args...); kwargs..., f.fixkwargs...))
end
Performance:
julia> @btime Fix{(1,2,-3,-1)}((args...;kwargs...)->(args...,(;kwargs...)), (:a,:b,:getting_close,:END), (z=5))(:y, 1, 2; k=2)
  1.000 ns (0 allocations: 0 bytes)
(:a, :b, :y, 1, :getting_close, 2, :END, (k = 2, z = 5))
Notes:
- call 
Fix{fixinds::Tuple}(f, fixvals::Tuple; fixkwargs...)to construct functor
–fixindsis a tuple of indices starting from left (e.g.(1, 2, 3)), and any indices counting from the right are negative (e.g.,(1, 2, 3, -3, -2, -1)). Index1is left-most argument,-1is right-most. - Fixed keyword arguments override called keyword arguments. Not sure if this is the right decision.
 - There is no check that the number of arguments or keyword arguments fit a profile; the combined argument list simply grows with number of arguments passed during call, with new arguments filling in the middle between the arguments with positive indices and the arguments with negative indices.
 - This could use some more road testing, for sure
 - 
FixFirst(f,x)is created byFix{(1,)}(f, (x,))whichisa Fix{<:Any, (1,)}. It is presumed that such an object would be created byf(x, _...). - 
FixLast(f,x)is created byFix{(-1,)}(f, (x,))whichisa Fix{<:Any, (-1,)}. It is presumed that such an object would be created byf(_..., x). - In many locations where 
Base.Fix2is used, people will probably usef(_, x), which will create aFix{<:Any, (2,)}object. When calling a function with two arguments, the fact thatFix{<:Any, (2,)}behaves asFix{<:Any,(-1,)}means the type signature of a partial function which does the intended task is not unique. For the people who care about the types of the object, not sure if this matters.