refactor to avoid ugly downcasts