Keep in mind that the intent of comprehensions is to work with Enumerables which Range happens to implement. Hence the “result” of the comprehension is a list of :ok values; in this particular case the side-effect is the intended behaviour.
Now you can suppress the list of :ok values by abusing the filter expression:
iex(1)> for x <- 0..10, (fn i -> if (i > 0), do: IO.puts i; false end).(x), do: :ok
1
2
3
4
5
6
7
8
9
10
[]
But that’s really going from bad to worse.
There is nothing stopping you from creating your own “one-liner”:
iex(2)> do_it = fn (low,high,f) ->
...(2)> aux = fn
...(2)> c,i when i <= high ->
...(2)> f.(i)
...(2)> c.(c, i+1)
...(2)> _,_ ->
...(2)> :ok
...(2)> end
...(2)> aux.(aux,low)
...(2)> end
#Function<18.99386804/3 in :erl_eval.expr/5>
iex(3)> do_it.(0,10,&IO.puts/1)
0
1
2
3
4
5
6
7
8
9
10
:ok
iex(4)>