What hasn't been stated, I think: any kind of memory is relatively expensive and slow. MCUs are not only atrociously slow, but often nondeterministic as well.
True, memories are only a buck or two, but a discrete logic function (which since it's not standard logic, I guess, means an ASIC... so, this is a pretty bad example, isn't it?) would be cents on the dollar in a fair comparison. It's also on the order of 50-200ns response time, whereas the optimal solution would be on the order of the logic family's characteristics (say, 20-50ns for 74LS or HC, down to a few ns or sub-ns for LVC, ECL and so on). Or without an ASIC, implementing it with a standard logic family (using discrete gates and adders and such) might have comparable delay to the memory (i.e., several times the logic family's basic delay, so ~100-200ns for HC), but could still be quite a bit faster in other families (<10ns for LVC, ECL, etc.?).
And, true, you probably don't even need that kind of speed. Say this were part of a classical microprocessor system, where you're monitoring a peripheral input, and communicating with a parallel bus. Perhaps the input is merely polled every so often: it might be perfectly reasonable to have the processor wait a few cycles (potentially several us) before releasing the data bus. In that time, you could implement not only a static (combinational) solution, but an iterated solution as well (i.e., accumulating "register <= register + bit[N]" -- only need a register, counter and mux, and lg(N) full adders, to complete in N cycles), or even use a secondary MCU for the same computation.
The absolute worst part about using an MCU is timing. You cannot build a digital logic circuit to any kind of performance, if you can't count on the timing characteristics of the logic you're using. Can you imagine building a computer out of gates, where the RTL* is implemented as software loops on individual MCUs?
*Register Transfer Level, i.e., more or less, a high level / MSI/LSI building block implementation. Registers, adders, muxes, those sorts of things.
Even if you are careful with your software implementations, the best you can do is trigger an interrupt from a given event (say a clock edge). And even in the lowest latency MCUs, this burns many cycles. True, those cycles may be at 50 or 100MHz internal clock, but even just reading ports will take more and more cycles, and pretty soon, by the time you've finished implementing your function and written it to the output ports, it's taking 10 or 20us, and probably the elapsed time varies nondeterministically, too! (I.e., the result doesn't arrive consistently following the 'request' event.) Trying to inspect the results with traditional methods (logic source, analyzer) will be maddening, because you'll have some ports changing before others, and the delay is flopping around, and...
True, you can very easily make something that behaves like, say, an LED display driver. And it'll work statically. And often times, that's all that matters. But that's no challenge. That's trivial, and hardly worth discussing. If something is computable, Turing Completeness guarantees that it can be done, eventually (given enough memory, anyway). Where the challenge lies, is in doing it quickly. And more practically, computer engineering has to do with doing it, and quickly, AND cheaply.
To put it another way, I'd be impressed if that little PIC-faking-a-display-driver (different thread) were capable of being used in a muxed display. Can it change state, coherently, in under a millisecond? How about in ten microseconds or less?
Not to demean the fake-display-driver project, because it's still interesting. But for other reasons. The software is likely wholly unremarkable (as stated on the project page by its own author, no less -- not in the same words, but I expect this statement would not be found untrue, either).
Tim