Factor Language Blog

Adding named parameters to Factor

Friday, March 30, 2007

Christopher Diggins shows some C# code to convert code that uses named parameters into stack code. The same algorithm can be implemented in Factor:

! Based on
! http://cat-language.googlecode.com/svn/trunk/CatPointFreeForm.cs
USING: kernel namespaces arrays sequences sequences-internals
math inference parser words quotations ;
IN: locals

: arg-list, ( n -- )
    dup , [ f <array> ] %
    <reversed> [ , [ swapd pick set-nth-unsafe ] % ] each ;

: arg-n, ( n -- ) , [ r> dup >r nth-unsafe ] % ;

: localize ( args obj -- )
    tuck swap index dup -1 > [ nip arg-n, ] [ drop , ] if ;

: point-free ( quot args -- newquot )
    [
        dup length arg-list,
        \ >r , swap [ localize ] each-with [ r> drop ] %
    ] [ ] make ;

: with-locals ( quot locals -- ) point-free call ;

\ with-locals 2 [ point-free ] define-transform

DEFER: |LOCALS delimiter

: LOCALS|
    #! Syntax: LOCALS| a b c | ... a ... b ... c ... |LOCALS
    "|" parse-tokens
    [ create-in dup define-symbol ] map >r
    \ |LOCALS parse-until >quotation parsed
    r> parsed
    \ with-locals parsed ; parsing

PROVIDE: libs/locals ;

The fact that this is so easy to implement inside the language itself is no fluke. The above code uses parser features only found in Factor 0.89, so you need the latest darcs code if you want to run it; I added it to the library as libs/locals.

It works by placing all the inputs to a LOCALS| … | … |LOCALS form in an array, which is moved to the retain stack. Named parameter accesses are rewritten into code which moves this parameter array to the data stack, pulls out the right element, and moves it back to the retain stack.

Here is a usage example:

: quadratic LOCALS| x a b c | a x sq * b x * + c + |LOCALS ;

Factor already has (dynamically scoped) variables in the core; using Eduardo’s libs/vars syntax sugar, we could write the above code as:

VARS: x a b c ;
: quadratic [ >c >b >a >x  a x sq * b x * + c + ] with-scope ;

However LOCALS| … |LOCALS is more efficient (and less flexible) because it performs all lookup at compile time – using variables entails hashtable creation and access. Also, with LOCALS| don’t have to define the parameters as symbols first; the LOCALS| parsing word does that for you.

Personally I don’t use local parameters in Factor. I think they are a crutch and result in poor reusability. However I won’t stop people from using whatever coding style they find most satisfying, and I think having more options is good; especially since it is easy to define new abstractions in library code, without complicating the core language.