I have not used kqueue (not doing a lot of *BSD programming here) but I have used epoll a lot lately, and also done some multi-threaded programming in Haiku where I think we have a bit different challenges.
Here are some things I don’t like about epoll.
In my application I use several sockets, but I also use threads. I wanted to do something like “wait until either a socket is readable, or a mutex is unlocked by another thread”. With epoll, this is not possible: mutexes are not file descriptors. So, I had to replace some mutex in my application with eventfd instead. It is not as nice an API to use. Fortunately I could put a C++ wrapper above it.
In Haiku context, a typical problem that would be great to have a solution for is "wait until my thread receives a BMessage, or until a socket becomes readable. To solve this, we have added the wait_for_objects function to our kernel. As you can see, it takes a kqueue-like approach as to what it can wait on. It allows to wait on file descriptors, semaphores, ports (the low level thing on which BMessage is built), and threads. It would make no sense for semaphore or threads to be exposed as file descriptors, I think.
(note: the wait_for_objects API has other limitations similar to the differnce between poll and epoll, but that I think is outside the scope of this discussion).
If you look at epoll vs kqueue in isolation, it may seem that epoll is better. If you look at them both in a context where you need to support both in your code, you are limited to the common denominator of both, which means your code will probably already be architectured to use file descriptors for everything. But if you are designing your application from scratch and planning to use only one of them, something like wait_for_objects enable you to use semaphores and ports normally in all other places in your app, and wait on them at the same time as you wait on files.
To put it in another way, it means the kernel provides an high level API for many things directly (acquiring and releasing a semaphore, for example). Whereas if you go with epoll, you have to use the “everything is a file descriptor” approach pretty much everywhere.
This is actually quite visible already: in Linux, if you want to be notified about changes to a file, you use inotify (a file descriptor). In Haiku, you use the node monitor (it delivers the data using a port/BMessage).