# What workarounds are there to TagSetDelayed::tagpos?

My program creates an object based on an Association, with a custom head:

g = Gate[<| "key1" -> val1 |>]


I have given it an UpValue for Lookup to behave nicely with it:

Lookup[Gate[assoc_], a___] ^:= Lookup[assoc, a]


I would like to be able to use the Lookup list syntax to return {val1, val2} in both the following lines:

Lookup[{ <| "key1" -> val1 |>, <| "key1" -> val2 |> }, "key1"]
Lookup[{Gate[ <| "key1" -> val1 |> ], Gate[ <| "key1" -> val2 |> ]}, "key1"]


The obvious thing to try is the following:

Gate /: Lookup[{a___, Gate[b_], c___}, d___] := Join[Lookup[{a}, d], {Lookup[b, d]}, Lookup[{c}, d]]


I get the TagSetDelayed::tagpos error, of course, because I'm trying to assign Gate a value from the second level of the expression Lookup[List[Gate[]]]. (Ignore for now the fact that Lookup[{}, key] returns a Missing expression rather than {}, and so even if TagSetDelayed were clever enough, the assignment above would still fail to do what I wanted. I can deal with that afterwards.)

The "other question in mind" question from this StackExchange question is basically my question, but I don't think it's answered there.

Is there a workaround that doesn't require me to replace Lookup with a special GateLookup function I define myself?

More generally, are there any workarounds to TagSetDelayed::tagpos which don't involve creating helper functions and going full object-oriented?

I am not aware of any good workaround (if you insist on using the same syntax) - the depth-1 limitation for UpValue search is pretty restrictive.
ClearAll[Gate];

That said, most such issues are syntactic in nature, and often the better way out would've been to give up the standard syntax (Lookup etc), but instead define your own data type with all required methods, where you can impose the names and syntax you like.