Skip to content
Snippets Groups Projects
Commit 542dc913 authored by Stéphane Del Pino's avatar Stéphane Del Pino
Browse files

Add description of user functions

parent 5dcf95a2
No related branches found
No related tags found
1 merge request!145git subrepo clone git@gitlab.com:OlMon/org-themes.git packages/org-themes
...@@ -2076,6 +2076,12 @@ or if ~(y1,...,ym)~ has already been defined in ~Y1*...*Ym~ ...@@ -2076,6 +2076,12 @@ or if ~(y1,...,ym)~ has already been defined in ~Y1*...*Ym~
(y1,...,ym) = f(x1,...,xn); (y1,...,ym) = f(x1,...,xn);
#+END_SRC #+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 *** Pure functions
In the ~pugs~ language, functions are *pure functions* in the sense that In the ~pugs~ language, functions are *pure functions* in the sense that
...@@ -2084,18 +2090,196 @@ functions. They act as operators. ...@@ -2084,18 +2090,196 @@ functions. They act as operators.
#+BEGIN_note #+BEGIN_note
Actually these functions are not strictly /pure functions/ in the 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 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 possible to modify the random seed used by the code. In that case, the
case, the modified value is not a variable of the language itself but modified value is not a variable of the language itself but the
the internal random seed itself. internal random seed itself.
#+END_note #+END_note
*** TODO Implicit type conversion for parameters and returned values *** 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 ** TODO modules
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment