It's hard to implement generic functors as a protocol without either runtime code generation or generalized dynamic dispatch.
The typical efficient implementation of a protocol (aka an interface) is a vtable - an array of pointers to functions. When you instantiate a generic in a statically compiled language, that usually clones the body of the generic thing (method or type) with references to the generic parameters replaced with the type arguments; and the new, cloned body is handed off to codegen.
But if you instantiate via a protocol reference, the compiler doesn't statically know the implementation of the thing you're trying to call - it can't see the body of code to clone. It's an indirect function call through a variable, and without a restricted language, analyzing it quickly runs into the halting problem.
With runtime code generation, the problem can be solved with sufficient magic - the runtime can rewrite as necessary. That's how .NET implements this.
Alternatively, if the body of each generic method is compiled using some form of dynamic dispatch - e.g. every operation on a value whose type is a generic parameter is done via an table of function pointers, and this table is passed into the generic method at the point of every call - then it can work, at some cost in speed. Haskell works like this, AIUI.
Java does it even more simply, with full-fat dynamic dispatch - all generic parameters turn into Object, and method bodies get runtime casts inserted as necessary.