@@ -906,7 +906,7 @@ function ``foo`` as
906
906
907
907
julia> stagedfunction foo(x)
908
908
println(x)
909
- :(x*x)
909
+ return :(x*x)
910
910
end
911
911
foo (generic function with 1 method)
912
912
@@ -950,15 +950,17 @@ is re-used as the method body.
950
950
951
951
The reason for the disclaimer above is that the number of times a staged
952
952
function is staged is really an implementation detail; it *might * be only
953
- once, but it *might * also be more often. As a consequence, you should
953
+ once, but it *might * also be more often. As a consequence, you should
954
954
*never * write a staged function with side effects - when, and how often,
955
- the side effects occur is undefined.
955
+ the side effects occur is undefined. (This is true for macros too - and just
956
+ like for macros, the use of `eval ` in a staged function is a sign that
957
+ you're doing something the wrong way.)
956
958
957
959
The example staged function ``foo `` above did not do anything a normal
958
960
function ``foo(x)=x*x `` could not do, except printing the the type on the
959
- first invocation. However, the power of a staged function lies in its
960
- ability to compute different quoted expression depending on the types
961
- passed to it:
961
+ first invocation and incurring a higher compile-time cost. However, the
962
+ power of a staged function lies in its ability to compute different quoted
963
+ expression depending on the types passed to it:
962
964
963
965
.. doctest ::
964
966
@@ -988,16 +990,27 @@ We can, of course, abuse this to produce some interesting behavior::
988
990
989
991
Since the body of the staged function is non-deterministic, its behavior
990
992
is undefined; the expression returned on the *first * invocation will be
991
- used for *all * subsequent invocations with the same type. When we call
992
- the staged function with `` x `` of a new type, `` rand() `` will be called
993
- again to see which method body to use for the new type. In this case, for
994
- one * type * out of ten, `` baz(x) `` will return the string `` "boo!" ``.
995
- In short: don't do this .
993
+ used for *all * subsequent invocations with the same type (again, with the
994
+ exception covered by the disclaimer above). When we call the staged
995
+ function with `` x `` of a new type, `` rand() `` will be called again to
996
+ see which method body to use for the new type. In this case, for one
997
+ * type * out of ten, `` baz(x) `` will return the string `` "boo!" `` .
996
998
997
- While these examples are perhaps not so interesting, they have hopefully
998
- helped to illustrate how staged functions work, both in the definition end
999
- and at the call site. Next, let's build some more advanced functionality
1000
- using staged functions...
999
+ *Don't copy these examples! *
1000
+
1001
+ These examples are hopefully helpful to illustrate how staged functions
1002
+ work, both in the definition end and at the call site; however, *don't
1003
+ copy them *, for the following reasons:
1004
+
1005
+ * the `foo ` function has side-effects, and it is undefined exactly when,
1006
+ how often or how many times these side-effects will occur
1007
+ * the `bar ` function solves a problem that is better solved with multiple
1008
+ dispatch - defining `bar(x) = x ` and `bar(x::Integer) = x^2 ` will do
1009
+ the same thing, but it is both simpler and faster.
1010
+ * the `baz ` function is pathologically insane
1011
+
1012
+ Instead, now that we have a better understanding for how staged functions
1013
+ work, let's use them to build some more advanced functionality...
1001
1014
1002
1015
An advanced example
1003
1016
~~~~~~~~~~~~~~~~~~~
@@ -1013,25 +1026,26 @@ possible implementation is the following::
1013
1026
for i = N-1:-1:1
1014
1027
ind = I[i]-1 + dims[i]*ind
1015
1028
end
1016
- ind + 1
1029
+ return ind + 1
1017
1030
end
1018
1031
1019
1032
The same thing can be done using recursion::
1020
1033
1021
1034
sub2ind_rec(dims::()) = 1
1022
1035
sub2ind_rec(dims::(),i1::Integer, I::Integer...) =
1023
- i1==1 ? sub2ind (dims,I...) : throw(BoundsError())
1036
+ i1==1 ? sub2ind_rec (dims,I...) : throw(BoundsError())
1024
1037
sub2ind_rec(dims::(Integer,Integer...), i1::Integer) = i1
1025
1038
sub2ind_rec(dims::(Integer,Integer...), i1::Integer, I::Integer...) =
1026
- i1 + dims[1]*(sub2ind (tail(dims),I...)-1)
1039
+ i1 + dims[1]*(sub2ind_rec (tail(dims),I...)-1)
1027
1040
1028
1041
Both these implementations, although different, do essentially the same
1029
1042
thing: a runtime loop over the dimensions of the array, collecting the
1030
1043
offset in each dimension into the final index.
1031
1044
1032
1045
However, all the information we need for the loop is embedded in the type
1033
1046
information of the arguments. Thus, we can utilize staged functions to
1034
- move the iteration to compile-time. The body becomes almost identical,
1047
+ move the iteration to compile-time; in compiler parlance, we use staged
1048
+ functions to manually unroll the loop. The body becomes almost identical,
1035
1049
but instead of calculating the linear index, we build up an *expression *
1036
1050
that calculates the index::
1037
1051
@@ -1040,7 +1054,7 @@ that calculates the index::
1040
1054
for i = N-1:-1:1
1041
1055
ex = :(I[$i] - 1 + dims[$i]*$ex)
1042
1056
end
1043
- :($ex + 1)
1057
+ return :($ex + 1)
1044
1058
end
1045
1059
1046
1060
**What code will this staged function generate? **
@@ -1055,20 +1069,20 @@ function::
1055
1069
function sub2ind_staged_impl{N}(dims::NTuple{N}, I...)
1056
1070
ex = :(I[$N] - 1)
1057
1071
for i = N-1:-1:1
1058
- ex = :(I[$i] - 1 + dims[$i]*ex)
1072
+ ex = :(I[$i] - 1 + dims[$i]*$ ex)
1059
1073
end
1060
- :($ex + 1)
1074
+ return :($ex + 1)
1061
1075
end
1062
1076
1063
1077
We can now execute ``sub2ind_staged_impl `` and examine the expression it
1064
1078
returns::
1065
1079
1066
1080
julia> sub2ind_staged_impl((Int,Int), Int, Int)
1067
- :(((I[1] - 1) + dims[1] * ex) + 1)
1081
+ :(((I[1] - 1) + dims[1] * ex) + 1)
1068
1082
1069
1083
So, the method body that will be used here doesn't include a loop at all
1070
1084
- just indexing into the two tuples, multiplication and addition/subtraction.
1071
1085
All the looping is performed compile-time, and we avoid looping during execution
1072
- entirely. Thus, we only loop *once per type *, in this case once per ``N ``
1086
+ entirely. Thus, we only loop *once per type *, in this case once per ``N ``
1073
1087
(except in edge cases where the function is staged more than once - see
1074
1088
disclaimer above).
0 commit comments