The library user can use an argument of type Foo in several ways:
val ft = new FooT[Int]{}
def foo1[T](f : FooT[T]) = f
def foo2[F <: Foo](f : F) = f
def bad_foo(f : Foo) = f
Calling foo1(ft) or foo2(ft) will preserve the knowledge that T is Int, but calling bad_foo will not.
Is there some way I can warn the library user in cases similar to bad_foo?
I don’t think the OP wants to return the Foo. That bad_foo, if I understood correctly, will be defined by the library user. Issue is, how do we enforce the user to retain the T in Foo[T].
If it’s important to preserve, use a type parameter instead of a type member. Type member syntax makes it easy to leave off and hard to preserve. Type parameter syntax makes it easier to preserve, and harder to leave off.