functorfactor-lang

What are functors, and why do we need them?


I don't understand Factor's functors. I guess it would help to first understand what a "functor" is.

Google says:

a function; an operator.

In Factor, all functions (words) are operators, and are first-class. (In fact, I can't think of much in Factor that isn't first class). This definition isn't so helpful.

Wikipedia says:

Functor may refer to:

  • ...
  • In computer programming:
    • function object used to pass function pointers along with its state
    • ...
    • In Haskell a Functor describes a generalisation of functions that perform mapping operations

The page for "function object" says:

an object to be invoked or called as if it were an ordinary function, usually with the same syntax (a function parameter that can also be a function).

So a functor is a first-class function? This is nothing special, and anyways words and quotations and stuff are already first-class in Factor.

Factor Functors have weird syntax that reminds me of generics or something.

resource:unmaintained/models/combinators/templates/templates.factor:

FROM: models.combinators => <collection> #1 ;
FUNCTOR: fmaps ( W -- )
W IS ${W}
w-n      DEFINES ${W}-n
w-2      DEFINES 2${W}
w-3      DEFINES 3${W}
w-4      DEFINES 4${W}
w-n*     DEFINES ${W}-n*
w-2*     DEFINES 2${W}*
w-3*     DEFINES 3${W}*
w-4*     DEFINES 4${W}*
WHERE
MACRO: w-n ( int -- quot ) dup '[ [ _ narray <collection> ] dip [ _ firstn ] prepend W ] ;
: w-2 ( a b quot -- mapped ) 2 w-n ; inline
: w-3 ( a b c quot -- mapped ) 3 w-n ; inline
: w-4 ( a b c d quot -- mapped ) 4 w-n ; inline
MACRO: w-n* ( int -- quot ) dup '[ [ _ narray <collection> #1 ] dip [ _ firstn ] prepend W ] ;
: w-2* ( a b quot -- mapped ) 2 w-n* ; inline
: w-3* ( a b c quot -- mapped ) 3 w-n* ; inline
: w-4* ( a b c d quot -- mapped ) 4 w-n* ; inline
;FUNCTOR

The documentation is extremely sparse on these. What are they? When should I use them?


Solution

  • Don't think of functors as that 'They're named "functors" to annoy category theory fanboys and language purists.' :)

    Their use is mainly to generate boilerplate or template code. Just like C++ templates are an optimization feature because generic dispatch can be slow, so are Factor functors.

    Example here:

    USING: functors io lexer namespaces ;
    IN: examples.functors
    
    FUNCTOR: define-table ( NAME -- )
    
    name-datasource DEFINES-CLASS ${NAME}-datasource
    
    clear-name DEFINES clear-${NAME}
    init-name DEFINES init-${NAME}
    
    WHERE
    
    SINGLETON: name-datasource
    
    : clear-name ( -- ) "clear table code here" print ;
    
    : init-name ( -- ) "init table code here" print ;
    
    name-datasource [ "hello-hello" ] initialize
    
    ;FUNCTOR
    
    SYNTAX: SQL-TABLE: scan-token define-table ;
    

    You can now write SQL-TABLE: person and Factor will create the words clear-person, init-person and person-datasource for you.

    When to use them? I think never unless you have performance problems that warrants their use. They are very bad for grepability.