```v struct Repo[T] { db DB } struct User { id int name string } struct Post { id int user_id int title string body string } fn new_repo[T](db DB) Repo[T] { return Repo[T]{db: db} } // This is a generic function. V will generate it for every type it's used with. fn (r Repo[T]) find_by_id(id int) ?T { table_name := T.name // in this example getting the name of the type gives us the table name return r.db.query_one[T]('select * from ${table_name} where id = ?', id) } db := new_db() users_repo := new_repo[User](db) // returns Repo[User] posts_repo := new_repo[Post](db) // returns Repo[Post] user := users_repo.find_by_id(1)? // find_by_id[User] post := posts_repo.find_by_id(1)? // find_by_id[Post] ``` Currently generic function definitions must declare their type parameters, but in future V will infer generic type parameters from single-letter type names in runtime parameter types. This is why find_by_id can omit [T], because the receiver argument r uses a generic type T. ```v fn compare[T](a T, b T) int { if a < b { return -1 } if a > b { return 1 } return 0 } // compare[int] println(compare(1, 0)) // Outputs: 1 println(compare(1, 1)) // 0 println(compare(1, 2)) // -1 // compare[string] println(compare('1', '0')) // Outputs: 1 println(compare('1', '1')) // 0 println(compare('1', '2')) // -1 // compare[f64] println(compare(1.1, 1.0)) // Outputs: 1 println(compare(1.1, 1.1)) // 0 println(compare(1.1, 1.2)) // -1 ```