-
Notifications
You must be signed in to change notification settings - Fork 6
/
arity.mdp
166 lines (111 loc) · 5.26 KB
/
arity.mdp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
Arity of Rebol functions.
Author: Ladislav Mecir
===Introduction
This article discusses the number of arguments Rebol functions take. It describes both R2 and R3 versions of the interpreter.
===Specified number of arguments
Every Rebol function has got a specification block which, besides other things, also specifies the number of arguments a function is supposed to take.
We can find the number by using the *nargs* function written by Carl Sassenrath:
nargs: func [
{The number of the function arguments specified}
f [any-function!]
] [
-1 + index? any [find words-of :f refinement! tail words-of :f]
]
Example:
nargs :add ; == 2
===Actual number of arguments
To be able to find the actual number of arguments a function takes I defined the following function:
args-taken?: func [
{How many arguments the given function actually takes?}
f [any-function! word! path!]
args [block!]
/local test r
] either system/version < 2.90.0 [[
test: make block! 1 + length? args
insert/only test :f
repeat i length? args [
insert/only tail test to paren! reduce ['first at args i]
]
if error? r: try [do/next test] [do r]
(length? args) - (length? second r)
]] [[
test: make block! 1 + length? args
insert/only test :f
repeat i length? args [
insert/only tail test to paren! reduce ['first at args i]
]
if error? set/any 'r try [do/next test 'pos] [do r]
(length? args) - (length? pos)
]]
Let's test the behaviour of the *list-dir* function:
nargs :list-dir ; == 1
args-taken? :list-dir [%.]
; feedback.r license.html nntp.r notes.html rebdoc.r
; rebol.exe rebol.r setup.html user.r
; == 1
We have seen, that the *list-dir* function took the specified number of arguments in this case.
---Unsupplied unset arguments and argument passing styles
In both R2 and R3 the *list-dir* function is able to take less than standard number of arguments, because its *dir* argument can be of the *unset!* datatype:
args-taken? :list-dir []
; feedback.r license.html nntp.r notes.html rebdoc.r
; rebol.exe rebol.r setup.html user.r
; == 0
Here the *list-dir* function took 0 arguments, because we didn't supply more.
In R2, if a function has one or more arguments that can have the *unset!* datatype at the end of its argument list it is possible to omit some arguments and the interpreter will supply *#[unset!]* values to the function for the omitted arguments.
This is also true for R3 but only for arguments passed using the literal argument passing style or for arguments using the unevaluated passing style.
In R3, arguments using the evaluated passing style cannot be omitted even if they are allowed to have the *unset!* datatype.
Example:
f: func [arg [unset!]] []
(f)
** Script error: f is missing its arg argument
---Refinements
A different situation can be observed with function refinements. A function can take more than standard number of arguments in this case:
nargs :copy ; == 1
args-taken? 'copy [[1 2]] ; == 1
args-taken? 'copy/part [[1 2] 1] ; == 2
---Operators
If we look at the *+* operator, we see that its specified number of arguments is two.
In R2, the same number we obtain when we check the actual number of arguments taken when the operator is used as prefix:
nargs :+ ; == 2
args-taken? :+ [1 2] ; == 2
In R2 the *-* operator is an exception, since it acts as unary if we use it as prefix operator:
nargs :- ; == 2
args-taken? :- [1 2] ; == 1
In R3 these examples do not work since R3 does not allow to use infix operators this way (in a prefix expression).
---Variadic functions
There are functions that take varying number of arguments depending on the arguments supplied. Such functions are called variadic functions.
For example, *make* is specified to take two arguments:
nargs :make ; == 2
args-taken? :make [word word] ; == 2
However, in R2, if we supply different arguments, *make* can take three arguments:
args-taken? :make reduce [function! [] []] ; == 3
This is not the case in R3, where *make* works as follows:
args-taken? :make reduce [function! [[] []]] ; == 2
always taking the fixed number of arguments.
An example of a variadic function is the *do* function which can take unlimited number of arguments both in R2 as well as in R3:
nargs :do ; == 1
args-taken? :do [1] ; == 1
args-taken? :do reduce [:negate 1] ; == 2
args-taken? :do reduce [:subtract 1 2] ; == 3
, etc...
In R3, we can define our own variadic functions as follows:
; define a variadic function called sumn
use [result arg-adder] [
arg-adder: func [:value [number! none! unset!]] [
unless number? get/any 'value [return result]
result: result + value
return/redo :arg-adder
]
sumn: func [] [
result: 0
return/redo :arg-adder
]
]
We can find out that the *sumn* function actually doesn't specify any arguments:
nargs :sumn ; == 0
However, it can take unlimited number of arguments, in fact:
args-taken? :sumn [1] ; == 1
args-taken? :sumn [1 1] ; == 2
args-taken? :sumn [1 1 1] ; == 3
, etc...
The end.