What is Defunctionalization
Defunctionalization is a compile-time conversion which removes higher-order functions by replacing the higher-order functions with a single first-order apply function.
This method was first represented by John C. Reynolds in the year 1972. It was represented in John C. Reynolds’s paper named “Definitional Interpreters for Higher-Order Programming Languages”. His examination was that :
- There are finitely many function abstractions in a given program, so these function abstractions are restored and assigned by a unique identifier.
- Within the program, every application of the function is then restored by a call to the apply function with the function identifier as the first argument.
- The task of this applied function is to execute on this first argument, and then the instructions are performed, which is indicated by the function identifier on the remaining arguments.
One difficulty with the idea of defunctionalization is that :
- Free variables are referenced by function abstractions. In this type of situation, defunctionalization should be introduced by closure conversion or lambda lifting (It is a meta-process that restructures a program so that functions are defined independently of each other in a global scope), so that if a function abstraction has any free variables, then they are passed as extra arguments to apply.
- In adding to this, if closure conversions are holded up as first-class values, then it becomes mandatory to represent these bindings captured by constructing data structures.
In a program, executes on all function abstractions rather than having a single applied function. To determine what functions should be called at each function application site, there are different kinds of control flow analysis (including simple difference based on type signature) and there is also a specialized apply function which may be recommended alternatively. Instead, indirect calls through function pointers are supported by the target language, which may be well organized and extensible than a dispatch-based approach.
Steps for Defunctionalization :
There are two steps to defunctionalize a higher-order function (HOF) :
- For each and every position where the higher-order function (HOF) is used and applied to a function, restore that function with the value of the assigned data type to constitute that function. This is known as a “defunctionalization symbol”.
- According to HOF’s definition, if a function parameter is applied anywhere, restore that applied function parameter with a call to a dedicated first-order function that will explain the defunctionalization symbol for which this parameter now stands for. Often, this function is called apply and sometimes it is known as eval.
Functional programming ideas are made with the help of Defunctionalization. There is a lot of value of Defunctionalization in languages which have already higher-order functions, i.e, functional languages.
Despite removing higher order functionalities, defunctionalization is a way of mechanically transforming interpreters into abstract machines and it also represents functions by function objects from object-oriented programming languages.
Below is the example given by Olivier Danvy, which is translated to Haskell :
Below is the tree datatype :
data Tree a = Leaf a | Node (Tree a) (Tree a)
Now, here we will have to defunctionalize the program given below :
cons :: a -> [a] -> [a] cons x xs = x : xs o :: (b -> c) -> (a -> b) -> a -> c o f g x = f (g x) flatten :: Tree t -> [t] flatten t = walk t  walk :: Tree t -> [t] -> [t] walk (Leaf x) = cons x walk (Node t1 t2) = o (walk t1) (walk t2)
Now, to defunctionalize the above program, we will replace all higher-order functions (i.e, o which is the only higher order function here) with lam datatype value and rather than calling them directly, we will initiate an apply function that simplify the datatype –
data Lam a = LamCons a | LamO (Lam a) (Lam a) apply :: Lam a -> [a] -> [a] apply (LamCons x) xs = x : xs apply (LamO f1 f2) xs = apply f1 (apply f2 xs) cons_def :: a -> Lam a cons_def x = LamCons x o_def :: Lam a -> Lam a -> Lam a o_def f1 f2 = LamO f1 f2 flatten_def :: Tree t -> [t] flatten_def t = apply (walk_def t)  walk_def :: Tree t -> Lam t walk_def (Leaf x) = cons_def x walk_def (Node t1 t2) = o_def (walk_def t1) (walk_def t2)