Programmable current sink
Building a constant current load with an opamp is a fairly straightforward way to do this. Take a look at the first diagram in this post, for instance:



An opamp adjusts its output to make the voltage on its - and + terminals identical. In this case, the negative input is the voltage across a current sense resistor (R1) and the positive output is the voltage across a potentiometer. Making the two voltages equal means that the current through R1 - and hence, your load, which should go where "V+" is on the schematic - equals the voltage on the + terminal divided by the resistance of R1.

In this case the output of the opamp is driving Q1, a FET, because the FET can sink far more current than the opamp.

To control the load digitally, replace R3 and R6 with the output of a DAC, or a suitably filtered PWM output. For a smaller load like 4mA, you'll want a much bigger resistor for R1; the value of R1 is a tradeoff between the accuracy you get from using larger set voltages, and the extra power dissipation and burden voltage across it.