The concept of "Point Free" or "Tacit Programming" is that it's a way of relating functions to each other without mentioning their arguments.
So instead of writing something like:
append-to-data: function [value] [append data value]
You'd have some way of avoiding the redundancy of having to say "value" twice. So imagine declaring an identical function with a syntax like:
append-to-data: pointfree [append data]
That would notice you didn't provide all the parameters to append, so an implicit parameter to the new function would be added. But what if you wanted instead something like:
append-10: function [series] [append series 10]
So maybe you would have some syntax in POINTFREE for that where you put a placeholder in spots that you wanted implicitly picked up. Maybe BLANK! as a default:
append-10: pointfree [append _ 10]
(Note: The name pointfree is deliberately chosen as bad, since it won't be taken for other reasons...and also reinforces the term for Rebol programmers unused to FP concepts. What I'm actually thinking is that this will be folded into lambda as the behavior when you don't use a block. So (-> append _ 10) for example.)
UPDATE: To eliminate potential accidents, it was unbundled from lambda and uses the similar-but-notably-distinct notation
<-
now.
Writing This In Usermode In Historical Rebol Is Non-Trivial
If you think writing a reliable POINTFREE yourself would be easy in Rebol2/Red, I would invite you to try. There's a lot to get right with parameter analysis and ordering refinements; beyond the average user. It doesn't get much easier if you are coding inside the system, either.
It would also be noticeably slower, using any method in near-reach. The only mechanisms would involve creating a whole new function spec, where the evaluator would have to pass parameters to that function...then start evaluating again to make a nested call...type checking all over again.
Ren-C has several tricks up its sleeve, including a new tool introduced today for writing your own specializations. That's the ability to MAKE ACTION! out of a FRAME! with some of its parameters filled. And since you can MAKE FRAME! out of an ACTION!, this provides a convenient round-trip:
>> data: [a b c]
>> f: make frame! :append
>> f/series: data
>> apd: make action! f
>> apd [d e f]
>> data
== [a b c d e f]
There's also a great convenience afforded by Ren-C's smart specialization handling. Consider:
>> parameters of :append
== [series value /part /only /dup /line]
>> apdup: :append/dup
>> parameters of :apdup
== [series value dup /part /only /line]
So if you have someone writing pointfree [append/dup ...] you don't worry...just GET the PATH!, and the function you are handed back doesn't even report DUP as a refinement. It's a normal parameter now.
There's also more unlocked by the idea of refinements naming their 0-or-1 arguments. We've seen it open doors with AUGMENT, but it helps here and elsewhere.
A Start on the Vision of POINTFREE
This usermode implementation is incomplete and raises some issues (including that I'm not totally thrilled with the @var skippable syntax in EVALUATE).
But it shows relatively little code taking care of some rather complex acrobatics. I'd hope it wouldn't be too far beyond the reach of a novice to write:
pointfree: func [
{Specialize by example: https://en.wikipedia.org/wiki/Tacit_programming}
return: [action!]
block [block!]
][
let action: (match action! any [
if match [word! path!] :block/1 [get block/1]
:block/1
]) else [
fail "POINTFREE requires ACTION! argument at head of block"
]
block: next block ; rest of block is invocation by example
; If we did a GET of a PATH! it will come back as a partially specialized
; function, where the refinements are reported as normal args at the
; right spot in the evaluation order. (e.g. GET 'APPEND/DUP returns a
; function where DUP is a plain WORD! parameter in the third spot).
;
; We prune out any unused refinements for convenience.
;
let params: map-each w parameters of :action [
match [word! lit-word! get-word!] w ; !!! what about skippable params?
]
let frame: make frame! :action ; all frame fields default to NULL
; Step through the block we are given--first looking to see if there is
; a BLANK! in the slot where a parameter was accepted. If it is blank,
; then leave the parameter null in the frame. Otherwise take one step
; of evaluation or literal (as appropriate) and put the parameter in the
; frame slot.
;
let var
iterate params [
case [
blank? :block/1 [block: next block]
word? params/1 [
if not block: evaluate @var block [
break ; ran out of args, assume remaining unspecialized
]
frame/(params/1): :var
]
all [
lit-word? params/1
match [group! get-word! get-path!] :block/1
][
frame/(params/1): reeval :block/1
block: next block
]
default [ ; hard literal argument or non-escaped soft literal
frame/(params/1): :block/1
block: next block
]
]
]
; We now create an action out of the frame. NULL parameters are taken as
; being unspecialized and gathered at the callsite.
;
return make action! :frame
]