The comparison between a statefull firewall and NAT is often because they feel like they are doing the same thing from a mechanical point of view.
For example here is how to achieve the same result in PF, note the single additional operator needed to specify nat.
block in on $EXT_IF
#NAT
pass in on $INT_IF to any rdr-to $EXT_IF
#statefullfirewall
pass in on $INT_IF to any
Some people will mention stateful firewalls. They're pretty easy to holepunch through because you just need each side to send a packet to the other, then each firewall sees it as an outgoing connection and allows it. It's nothing like IPv4 NAT.