File descriptors don't necessarily have to be text, of course. They don't have to be streams either. There are syscalls that (sendmsg/recvmsg) deal with datagram fds, that's how UDP sockets are implemented in UNIX, for example.

Having everything be a file descriptor greatly simplifies the API, instead of having over 9000 syscalls for every peripheral, you can do anything you can imagine with like 10 different syscalls. I think it's a very powerful design personally.