Defunctionalizing is a technique to use high-order functions in a language that does not support it. For example, in a language that supports high-order functions, we could write fold as:

let rec fold = (fn, initial, list) =>
  switch (list) {
  | [] => initial
  | [hd, ...tail] => fn(hd, fold(fn, initial, tail))
  };
let sum = list => fold((el, acc) => el + acc, 0, list);
sum([1, 2, 3]);
/* int = 6 */
let add = (n, list) => fold((el, acc) => [el + n, ...acc], [], list);
add(2, [1, 2, 3]);
/* list(int) = [3, 4, 5] */

But in a language that do not, we have to abstract somehow the fn argument, we could do that by creating a type called arrow(_, _) which is a GADT that holds information for our sum and add helper functions.

type arrow(_, _) =
  | FnPlus: arrow((int, int), int)
  | FnPlusCons(int): arrow((int, list(int)), list(int));

Here FnPlus is abstracting (el, acc) => el + acc, and FnPlusCons(int) is abstracting (el, acc) => [el + n, ...acc]. The first type argument for FnPlus is (int, int) which will hold our function arguments, the second type argument is a int that represents the result of the function. The same structure applies to FnPlusCons(int), but with an additional int which will hold what was the n in let add = (n, list) => fold((el, acc) => [el + n, ...acc], [], list);

Now we can write an apply helper function as:

let apply: type a b. (arrow(a, b), a) => b =
  (fn, value) => {
    switch (fn) {
    | FnPlus => {
      let (el, acc) = value;
      el + acc;
    }
    | FnPlusCons(n) => {
      let (el, acc) = value;
      [el + n, ...acc];
    }
    }
  };

And use it in our new fold function:

let rec fold: type a b. (arrow((a, b), b), b, list(a)) => b =
  (fn, inital, list) => 
    switch (list) {
    | [] => u
    | [hd, ...tail] => apply(fn, (hd, fold(fn, initial, tail)))
    }

Then it’s done! We have successfully avoided the use of high order functions.

let sum = list => fold(FnPlus, 0, list);
let add = (n, list) => fold(FnPlusCons(n), [], list);

sum([1, 2, 3]);
/* int = 6 */
add(2, [1, 2, 3]);
/* list(int) = [3, 4, 5] */

This concept is a building block towards understanding this paper.

More on this topic can be found here:
Defunctionalization at Work
Defunctionalization
Definitional interpreters for higher-orderprogramming languages