   @spec zip_with(enumerables, (Enumerable.t() -> term)) :: Enumerable.t()
         when enumerables: [Enumerable.t()] | Enumerable.t()
   def zip_with(enumerables, zip_fun) when is_function(zip_fun, 1) do
-    &prepare_zip(enumerables, &1, &2, zip_fun)
-  end
-
-  defp prepare_zip(enumerables, acc, fun, zip_fun) do
+    if is_list(enumerables) and :lists.all(&is_list/1, enumerables) do
+      &zip_list(enumerables, &1, &2, zip_fun)
+    else
+      &zip_enum(enumerables, &1, &2, zip_fun)
+    end
+  end
+
+  defp zip_list(_enumerables, {:halt, acc}, _fun, _zip_fun) do
+    {:halted, acc}
+  end
+
+  defp zip_list(enumerables, {:suspend, acc}, fun, zip_fun) do
+    {:suspended, acc, &zip_list(enumerables, &1, fun, zip_fun)}
+  end
+
+  defp zip_list(enumerables, {:cont, acc}, fun, zip_fun) do
+    case zip_list_heads_tails(enumerables, [], []) do
+      {heads, tails} -> zip_list(tails, fun.(zip_fun.(heads), acc), fun, zip_fun)
+      :error -> {:done, acc}
+    end
+  end
+
+  defp zip_list_heads_tails([[head | tail] | rest], heads, tails) do
+    zip_list_heads_tails(rest, [head | heads], [tail | tails])
+  end
+
+  defp zip_list_heads_tails([[] | _rest], _heads, _tails) do
+    :error
+  end
+
+  defp zip_list_heads_tails([], heads, tails) do
+    {:lists.reverse(heads), :lists.reverse(tails)}
+  end
+
+  defp zip_enum(enumerables, acc, fun, zip_fun) do
     step = fn x, acc ->
       {:suspend, :lists.reverse([x | acc])}
     end

    {:done, acc}
  end

  defp do_zip_next_tuple([{fun, [], :cont} | zips], acc, callback, yielded_elems, buffer, zip_fun) do
    case fun.({:cont, []}) do
      {:suspended, [elem | next_acc], fun} ->
        next_buffer = [{fun, next_acc, :cont} | buffer]
        do_zip_next_tuple(zips, acc, callback, [elem | yielded_elems], next_buffer, zip_fun)

      {_, [elem | next_acc]} ->
        next_buffer = [{fun, next_acc, :halt} | buffer]
        do_zip_next_tuple(zips, acc, callback, [elem | yielded_elems], next_buffer, zip_fun)

      {_, []} ->
        # The current zipped stream terminated, so we close all the streams

       ) do
    [elem | rest] = zip_acc
    next_buffer = [{fun, rest, zip_op} | buffer]
    do_zip_next(zips, acc, callback, [elem | yielded_elems], next_buffer, zip_fun)
  end

  defp do_zip_next([] = _zips, acc, callback, yielded_elems, buffer, zip_fun) do
    # "yielded_elems" is a reversed list of results for the current iteration of
    # zipping. That is to say, the nth element from each of the enums being zipped.
    # It needs to be reversed and passed to the zipping function so it can do it's thing.
