0

The 8.X version of vim introduced new possibilities for the function() command: the ability to create Partial functions or to package funcrefs with arguments.

The difference of the function() help is shown below.

version 7.4

function({name})                    *function()* *E700*
        Return a |Funcref| variable that refers to function {name}.
        {name} can be a user defined function or an internal function.

version 8.2

                    *function()* *E700* *E922* *E923*
function({name} [, {arglist}] [, {dict}])
        Return a |Funcref| variable that refers to function {name}.
        {name} can be the name of a user defined function or an
        internal function.
[...]
        When {arglist} or {dict} is present this creates a partial.
        That means the argument list and/or the dictionary is stored in
        the Funcref and will be used when the Funcref is called.

I'm trying to downport to 7.X a 8.2 vimscript that make heavy use of such packaged-funcrefs/Partials.

Can someone point me in the right direction for v7.x design patterns that could mimic the new function() behavior?

Right now, all I can think of is to share the arguments array along the funcref but that would require to refactor all the logic around the code.

Any hint appreciated, thanks!

8
  • 2
    Vim 7.4 has been launch in August 2013. Just by curiosity could you tell us why you need to support a 10 years old version? Nov 13, 2023 at 12:49
  • 2
    If you need partially-applied functions, you’ll have to simulate them. No way around that.
    – D. Ben Knoble
    Nov 13, 2023 at 13:10
  • 1
    @Vivian De Smedt: sadly, some of us are forced to work on rather old linux distributions to stay compatible with legacy software (SL7 with gvim 7.4 in my case). sob
    – Laurent'
    Nov 13, 2023 at 13:41
  • 1
    Thanks I'm glad to hear that 7.4 is still in use in some old distributions. Nov 13, 2023 at 13:52
  • 1
    It's possible to edit old code on a new editor. I don't know your exact situation but - from the top of my head - you should be able to remote into your legacy box or mount its hard drive on a current machine or use rsync to copy your changes there and similar ideas. Vim itself can edit remotely using netrw and ssh.
    – Friedrich
    Nov 14, 2023 at 9:17

1 Answer 1

2

The newer function() signature was introduced with Vim patch
7.4.1559. It may have been applied to some Vim 7.4 builds.

Should you decide to port a script, with a view to touching
as few lines as possible, you may consider the following
approach:

  • implement and encapsulate bespoke partial support in
    user-defined functions, i.e. Call() and Function(),
    whose signatures should match their namesake builtins;

  • substitute user-defined names for builtin names whenever
    partials are passed around;

  • resort to user-defined functions for Funcref invocation,
    e.g.,

    change:

    let b:Printer = function('printf', ['%s'])
    echo b:Printer('hello')
    

    to:

    let b:Printer = Function('printf', ['%s'])
    echo Call(b:Printer, ['hello'])
    
  • review Funcref-related type() tests, if there are any.

For example, these definitions and test examples are valid
for base Vim 7.0 builds:

" Join line-breaks.
set cpoptions-=C

" (Note that the _dict_ parameter is not implemented.)
" Function({name} [, {arglist}])
" Function({func} [, {arglist}])
" Function({list} [, {arglist}])
function! Function(prefix, ...) abort
    return (a:0 > 0)
        \ ? type(a:prefix) == type([])
            \ ? [a:prefix[0], extend(deepcopy(a:prefix[1], 1),
                        \ deepcopy(a:000[0], 1))]
            \ : [a:prefix, deepcopy(a:000[0], 1)]
        \ : a:prefix
endfunction

" (Note that the _dict_ parameter is not implemented.)
" Call({name}, {arglist})
" Call({func}, {arglist})
" Call({list}, {arglist})
function! Call(prefix, args) range abort
    return type(a:prefix) == type([])
        \ ? call(a:prefix[0], extend(deepcopy(a:prefix[1], 1),
                        \ deepcopy(a:args, 1)))
        \ : call(a:prefix, a:args)
endfunction


""""
""""
"""" A FEW TEST EXAMPLES.
""""
""""

echo Call(Function('len'), [[""]]) == 1
echo Call(function('len'), [[""]]) == 1
echo call(Function('len'), [[""]]) == 1
echo call(function('len'), [[""]]) == 1

echo Call(Function('printf', ['%s %s', 'hello']), ['world']) == 'hello world'

function! PrettyPrinter(format) abort
    return Function('printf', [a:format])
endfunction

let b:pp_1 = Call('PrettyPrinter', ['%s, %s, %s, %s'])
let b:pp_2 = Function(b:pp_1, ['hello'])
let b:pp_3 = Function(b:pp_2, ['hello'])
let b:pp_4 = Function(b:pp_3, ['hello'])
echo Call(b:pp_4, ['hello']) == 'hello, hello, hello, hello'

let b:pf_1 = Function('printf', ['%s, %s, %s, %s'])
let b:pf_2 = Function(b:pf_1, ['hello'])
let b:pf_3 = Function(b:pf_2, ['hello'])
let b:pf_4 = Function(b:pf_3, ['hello'])
echo Call(b:pf_4, ['hello']) == 'hello, hello, hello, hello'


" C: x -> y = x
function! s:C(x, y) abort
    return a:x
endfunction

function! Constancy(value) abort
    return Function('s:C', [a:value])
endfunction

let b:const = Call('Constancy', [0])
echo Call(b:const, [1]) == 0
echo Call(b:const, [2]) == 0


" T: f -> y -> x = fxy
function! s:T(f, y, x) abort
    return call(a:f, [a:x, a:y])
endfunction

function! Mapper(func) abort
    return Function('s:T', ['map', a:func])
endfunction

let b:doubler = Call('Mapper', ['v:val * v:val'])
echo Call(b:doubler, [range(7)]) == [0, 1, 4, 9, 16, 25, 36]

let b:letterer = Call('Mapper', ['nr2char(v:val)'])
echo Call(b:letterer, [range(65, 65 + 3)]) == ['A', 'B', 'C', 'D']
1
  • Excellent, thanks!
    – Laurent'
    Nov 20, 2023 at 9:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.