Working Through Cohen's Computability Curriculum
I picked up Daniel I.A. Cohen's Introduction to Computer Theory back when I was struggling to get past introductory programming courses and actually understand what a Turing machine was doing under the hood. The book is structured around the standard computability theory sequence: recursive functions, Turing machines, decidability, and complexity. It is not a particularly flashy text. That is partly why it stuck with me. The core approach Cohen takes is grounding abstract theory in formal definitions and then building up to proofs. He starts with primitive recursive functions and partial recursive functions, moves into Turing machine variants, and then tackles the halting problem and reductions. The exercises are where most people actually learn the material. The chapter on undecidability has a set of reduction problems that force you to construct diagonal arguments by hand instead of just reading through them. I spent roughly three weeks on that chapter alone because the proofs require you to be precise about what counts as a valid mapping reduction versus a Turing-reduction. One thing the book handles well is the distinction between decidable and recognizable languages. Beginners often conflate those two. Cohen spells it out clearly: a language is decidable if a Turing machine halts on every input and gives the correct yes or no answer. Recognizable only requires the machine to halt and accept on positive instances, with the possibility of running forever on negatives. That distinction matters when you are actually working through complexity proofs, and it shows up again in NP-completeness discussions later in the text.
I ran into a specific problem a few years ago when I was trying to apply Cohen's framework to a practical verification task. I needed to determine whether a certain state machine pattern could be reduced to a known decidable problem. The issue was that the machine had unbounded counters, which immediately pushed it out of the decidable fragment and into the realm of pushdown automata territory. Cohen's book doesn't cover weighted automata or counter machines in much depth, so I had to fall back on results from Hopcroft and Ullman for the upper bounds, while using Cohen's reduction techniques to establish the lower bounds. The workaround was straightforward: I encoded the counter behavior as a context-free grammar and then applied the standard pumping lemma argument Cohen introduces earlier in the book. It took me about two days to get the encoding right, but once it was in place the decidability argument fell together cleanly. The chapters on context-free grammars and pushdown automata are solid but not exhaustive. If you need deeper coverage of parsing algorithms like CYK or Earley, you will want to supplement with another source. The book gets to the equivalence between CFGs and PDAs, proves the pumping lemma for context-free languages, and covers basic decidability results for CFLs. That is usually sufficient for an introductory course but leaves you wanting more if you are working toward compiler construction or formal verification work.
Practical Tips for Using This Material
When working through the reducibility sections, do not skip the exercises that ask you to prove something is undecidable using a known hard problem. The pattern repeats across many problems: you reduce the halting problem or an accepted undecidable language to your target problem. Getting fast at recognizing which reduction to apply takes practice. I found that spending time drawing the reduction as a flow diagram before writing it down cut my proof-writing time roughly in half. The complexity theory section toward the end covers P, NP, and NP-completeness. Cohen's treatment is standard and adequate. The Cook-Levin theorem proof is included, and the reduction from 3-SAT to other NP-complete problems follows the usual path. One pitfall I see often is students assuming that because a problem is NP-complete it is unsolvable. The book makes the distinction clear, but it is worth emphasizing: NP-completeness is about worst-case computational difficulty, not about impossibility. Instances of NP-complete problems can still be solved in practice using heuristics or approximation algorithms, and the theory section does not replace learning those approaches. If you are looking for a digital copy, the book is available through most academic publishers and used book markets. Older editions are inexpensive and contain the same core material since computability theory does not change. The only meaningful difference between editions is the presentation of examples and the exercise sets. The third edition added more coverage of alternating Turing machines, which is useful if you plan to move into space complexity or higher levels of the polynomial hierarchy.
Get the Full Details

The main limitation of this book is that it assumes mathematical maturity. You should be comfortable with proof techniques like induction and contradiction before diving in. If you are not, spend a few weeks on discrete mathematics fundamentals first. The book will not teach you how to construct a proof; it expects you to already know how and then applies that skill to theory problems. Another gap is the lack of computational lab work. The theory is presented purely mathematically with no simulation or coding exercises. If you want to see Turing machines in action, you will need to build or find a simulator separately. I used a simple Python implementation to test my understanding of Turing machine configurations, and it made the abstract definitions much more concrete. Running a few transitions by hand on paper and then verifying with code is a strategy that works well here.