The main things that trip people up in Verilog coming from programming are the difference between synthesis and simulation, and the intrinsic parallelism with the two kinds of assignment operator.
There are all kinds of idioms that are syntactically valid Verilog that cannot be compiled to hardware. Usually you only use these while constructing a "testbench" to drive the simulated hardware.
There are also two kinds of assignment: = and <=. So for example, starting with a being set to 1 and b being set to zero:
b = a; c = b+1;
results in c being set to 2.
always @(posedge clk) b <= a; c <= b+1; end
results in b being set to a and c being set to 1: the value of b on the previous cycle plus 1. It also causes flip-flops to be inferred to hold b and c.
(Note that <= behaves differently depending on what's in the sensitivity list, which is also potentially very confusing!)
If I were still doing HDL design I'd advance my plan for modern object(bus)-orientated friendly HDL..
The @(*) syntax is what's called the sensitivity list, it's saying that the state of the module should be recalculated whenever any of the inputs change. Other possible values might include something like @(posedge clk) if you have a clock signal called "clk" and you only want the outputs of the module to update on the rising edge of the clock signal. This is dramatically oversimplifying the insanely complex world of HDL, but it's ultimately not /that/ hard if you know your computer architecture fundamentals.
In short, coding in verilog is like creating a schematic of the circuit.
Mostly hardware designers get away without thinking about all those above details in those terms, but sometimes you get bit by them (race-conditions between threads can occur, sadly) and it helps to understand it.
All programming is modeling of some sort or another.
I want to kind of give this library a similar direction. It's in its infancy at the moment :P