diff --git a/doc/userdoc.org b/doc/userdoc.org index 24bd03f3a93129837a209668696945cd7a3c8d08..e7fe896122d842402bad67c9118fdc22b4e9d8ee 100644 --- a/doc/userdoc.org +++ b/doc/userdoc.org @@ -2076,6 +2076,12 @@ or if ~(y1,...,ym)~ has already been defined in ~Y1*...*Ym~ (y1,...,ym) = f(x1,...,xn); #+END_SRC +The ~pugs~ language handles two kinds of functions. User defined +functions (see [[user-defined-functions]]) and builtin functions (see +[[builtin-functions]]). They behave essentially the same and are both +constant. In this context, it means that one cannot just declare or +modify a function. + *** Pure functions In the ~pugs~ language, functions are *pure functions* in the sense that @@ -2084,18 +2090,196 @@ functions. They act as operators. #+BEGIN_note Actually these functions are not strictly /pure functions/ in the -computer science context. The reason for that is that they can +computer science sense. The reason for that is that they can eventually have side effects. A good example for that, is that it is -possible to modify the random seed used by the code the code. In that -case, the modified value is not a variable of the language itself but -the internal random seed itself. +possible to modify the random seed used by the code. In that case, the +modified value is not a variable of the language itself but the +internal random seed itself. #+END_note *** TODO Implicit type conversion for parameters and returned values -*** TODO User-defined functions +*** User-defined functions<<user-defined-functions>> + +To define user functions, the syntax mimics mathematics. The +definition of the function itself is a *single* expression. This means +that one cannot use ~if...else~, loops and there is not such keyword as +~return~ in the ~C++~. + +The syntax of the definition of functions is +#+BEGIN_SRC pugs :exports code + let f1 : X -> Y, x -> e; + let f2 : X -> Y1*...*Ym, x -> (e1,...,em); + let f3 : X1*...*Xn -> Y, (x1,...,xn) -> e; + let f4 : X1*...*Xn -> Y1*...*Ym, (x1,...,xn) -> (e1,...,em); +#+END_SRC +~X~, ~Y~, ~X1~, ..., ~Xn~ and ~Y1~, ..., ~Ym~ are /simple/ data types (not +compound). The type of ~x~ is ~X~ and for lists, each argument ~xi~ belongs +to ~Xi~. The function themselves are defined by the expressions ~e~ (or +~e1~, ..., ~em~) which types are set by ~Y~ (or ~Y1~, ..., ~Ym~). + + +Let us give a few examples. +#+NAME: R-to-R-function +#+BEGIN_SRC pugs :exports both :results output + let f: R -> R, x -> -x; + + cout << "f(2.3) = " << f(2.3) + << "\nf(1) = " << f(1) + << "\nf(true) = " << f(true) + << "\n"; +#+END_SRC +One observes the implicit conversion of the values passed by argument. +#+results: R-to-R-function + +The following example illustrates the implicit conversion to the +returned type +#+NAME: R-to-R1-function +#+BEGIN_SRC pugs :exports both :results output + let f: R -> R^1, x -> 2*x; + + cout << "f(3.2) = " << f(2.3) << "\n"; +#+END_SRC +One observes the implicit conversion of the values passed by argument. +#+results: R-to-R1-function + +Using compound types as input and output, one can write +#+NAME: R22-R-string-to-R-string-function +#+BEGIN_SRC pugs :exports both :results output + let f : R^2x2*R*string -> R*string*R^3x3, + (A,x,s) -> (x*A[0,0]*A[1,1]-A[1,0], + A+","+x+","+s, + (A[0,0], A[0,1], 0, + A[1,0], A[1,1], 0, + 0, 0, x)); + let x : R, x=0; + let s : string, s= ""; + let A : R^3x3, A = 0; + (x,s,A) = f((1,2,3,4), 2.3, "foo"); + cout << "x = " << x + << "\ns = " << s + << "\nA = " << A << "\n"; + let (y,t,A2):R*string*R^3x3, (y,t,A2) = f((3,1,4,2), -5.2, "bar"); + cout << "y = " << y + << "\nt = " << t + << "\nA2 = " << A2 << "\n"; +#+END_SRC +This meaningless example produces the following result. +#+results: R22-R-string-to-R-string-function + +**** Lifetime of functions arguments + +The arguments used to define a function are *local* variables that +exists only during the evaluation of the function. + +Let us consider the following example +#+NAME: lifetime-of-function-args +#+BEGIN_SRC pugs :exports both :results output + let a:R, a = 1.4; + + let plus: R -> R, a -> (a>0)*a; + + cout << "plus(1) = " << plus(1) << "\n"; + cout << "plus(-3.2) = " << plus(-3.2) << "\n"; + cout << "a = " << a << "\n"; +#+END_SRC +This gives the expected result: the value of the variable ~a~ is +unchanged. +#+results: lifetime-of-function-args + +**** Non-arguments variables in function expressions + +Here we discuss rapidly of using variables (which are not arguments) +in function expressions. + +#+NAME: non-arg-variables-in-functions +#+BEGIN_SRC pugs :exports both :results output + let a:R, a = 1.4; + + let plus: R -> R, x -> (x>0)*a; + cout << "a = " << a << "\n"; + cout << "plus(2.2) = " << plus(2.2) << "\n"; + cout << "plus(-3.2) = " << plus(-3.2) << "\n"; + + a = 2; + cout << "a = " << a << "\n"; + cout << "plus(2.2) = " << plus(2.2) << "\n"; + cout << "plus(-3.2) = " << plus(-3.2) << "\n"; +#+END_SRC +While the function itself is a constant object, one sees that since +the value of ~a~ is changed, the value function is implicitly +modified. /This is a dangerous feature and should be avoid!/ +#+results: non-arg-variables-in-functions + +Since functions themselves are variables one can call functions in +#+NAME: functions-in-functions +#+BEGIN_SRC pugs :exports both :results output + let plus: R -> R, x -> (x>0)*x; + let minus: R -> R, x -> -(x<0)*x; + + let pm : R -> R*R, x -> (plus(x), minus(x)); + let toR2: R*R -> R^2, (x,y) -> (x,y); + + cout << "pm(2) = " << toR2(pm(2)) << " pm(-3) = " << toR2(pm(-3)) << "\n"; +#+END_SRC +One observes the utility function ~toR2~ that is used to perform the +output since ~cout~ does not handle compound types output. One gets +#+results: functions-in-functions + +**** Lifetime of user-defined functions + +Since functions are somehow variables, the lifetime of functions +follows the similar rules. + +Let us give an example +#+NAME: functions-lifetime +#+BEGIN_SRC pugs :exports both :results output + let f: R -> R, x -> 2.3*x+2; + { + let f: R -> R, x -> 1.25*x+3; + cout << "block 1: f(2) = " << f(2) << "\n"; + { + let f: R -> R, x -> 3.25*x-0.3; + cout << "block 2: f(2) = " << f(2) << "\n"; + } + } + for (let i:N, i = 1; i<=2; ++i) { + let f: R -> R, x -> i*x+2; + cout << "for(i=" << i << "): f(2) = " << f(2) << "\n"; + } + cout << "global: f(2) = " << f(2) << "\n"; +#+END_SRC +Running this example produces +#+results: functions-lifetime + +**** Recursion + +Since functions cannot be simply declared but must be defined, double +recursion is not possible. Thus, there is no equivalent construction +in ~pugs~ of the following ~C++~ code +#+BEGIN_SRC C++ :exports source + int f(int i); + int g(int i) + { + if (i < 0) + return 0; + else + return f(i-1); + } + + int f(int i) { return g(i); } +#+END_SRC +Moreover simple recursion if forbidden for similar reasons, since +functions are made of a single expression (one cannot use statements), +there would be no way to end the recursion. Thus, the code +#+NAME: no-recursion +#+BEGIN_SRC pugs-error :exports both :results output + let f: N->N, n -> f(2*n); +#+END_SRC +produces the following compilation time error +#+results: no-recursion -*** TODO Builtin functions +*** TODO Builtin functions<<builtin-functions>> ** TODO modules