Search code examples
functionrebolrebol3

How to wrap up a function with a refinement so the refinement isn't required?


I am trying to wrap up functions with refinements in a generic way so they can be called without the refinement. For instance, ARRAY-INITIAL size value instead of ARRAY/INITIAL size value

wrap: function [refined [path!] args [block!]] [
    function args compose [
        (refined) (args)
    ]
]

array-initial wrap 'array/initial [size value]

Not too fancy. Seems to work in general, but this has something weird if you call it using a function:

>> n: 0 array/initial 4 does [++ n] 
== [10 11 12 13]

>> n: 10 array-initial 4 does [++ n]
== [10 10 10 10]

When I source it I get this:

>> source array-initial 
array-initial: make function! [[size value][array/initial size value]]

Okay, so what's happening is that the function is being called in the wrapper and the result of the call passed...not the function. One workaround would be to use a get-word to avoid the evaluation:

>> array-initial-2: function [size value] [array/initial size :value]

>> array-initial-2: 10 array-initial-2 4 does [++ n]
[10 11 12 13]

But I was looking for a general approach. What's the best way to proxy the parameters without having this happen?


Solution

  • This was a fascinating exercise, gotta love SO.

    Turns out you actually need a "do reduce" to wrap functions with get-word arguments ...

    R3 only at the moment:

    unrefine: func [
      "Return an equivalent function with the given refinements wired in."
      refined [any-path! block!] "The function, with the refinements to include."
      /local ospec spec body found p s w b r
    ] [
      ospec: spec-of get first refined: to lit-path! :refined
      body: append copy spec: make block! length? ospec copy/deep [do reduce []]
      append/only body/3 :refined
      parse ospec [
        set s 0 1 string! (all [s append spec s])
        any [
          set w [word! | get-word! | lit-word!] (
            append spec :w
            append body/3 either word! = type? :w [reduce ['quote to get-word! w]][w])
          set b 0 1 block! (
            all [b append/only spec copy b])
          set s 0 1 string! (
            all [s append spec copy s])
        |
          /local to end
        |
          set r refinement! (
            p: any [p tail spec]
            if not found: find next refined r [append spec r])
          set s 0 1 string! (all [not found s append spec copy s])
          any [
            set w [word! | get-word! | lit-word!] (
              either found [p: insert p :w][append spec :w]
              append body/3 either word! = type? :w [reduce ['quote to get-word! w]][w])
            set b 0 1 block! (
              all [b either found [p: insert/only p copy b][append/only spec copy b]])
            set s 0 1 string! (
              all [s either found [p: insert p copy s][append spec copy s]])
          ]
        ]
      ]
      func spec body
    ]