   """
   @spec max_by(t, (element -> any), (() -> empty_result)) :: element | empty_result
         when empty_result: any
-  def max_by(enumerable, fun, empty_fallback \\ fn -> raise Enum.EmptyError end) do
+  def max_by(enumerable, fun, empty_fallback \\ fn -> raise Enum.EmptyError end)
+      when is_function(fun, 1) and is_function(empty_fallback, 0) do
     first_fun = &{&1, fun.(&1)}
 
     reduce_fun = fn entry, {_, fun_max} = old ->

  @spec min_max_by(t, (element -> any), (() -> empty_result)) :: {element, element} | empty_result
        when empty_result: any
  def min_max_by(enumerable, fun, empty_fallback \\ fn -> raise Enum.EmptyError end)

  def min_max_by(enumerable, fun, empty_fallback) do
    first_fun = fn entry ->
      fun_entry = fun.(entry)
      {{entry, entry}, {fun_entry, fun_entry}}

  """
  @spec min_by(t, (element -> any), (() -> empty_result)) :: element | empty_result
        when empty_result: any
  def min_by(enumerable, fun, empty_fallback \\ fn -> raise Enum.EmptyError end)
      when is_function(fun, 1) and is_function(empty_fallback, 0) do
    first_fun = &{&1, fun.(&1)}

    reduce_fun = fn entry, {_, fun_min} = old ->
