Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
pugs
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Model registry
Operate
Environments
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
code
pugs
Commits
542dc913
Commit
542dc913
authored
3 years ago
by
Stéphane Del Pino
Browse files
Options
Downloads
Patches
Plain Diff
Add description of user functions
parent
5dcf95a2
No related branches found
No related tags found
1 merge request
!145
git subrepo clone git@gitlab.com:OlMon/org-themes.git packages/org-themes
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
doc/userdoc.org
+190
-6
190 additions, 6 deletions
doc/userdoc.org
with
190 additions
and
6 deletions
doc/userdoc.org
+
190
−
6
View file @
542dc913
...
@@ -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
th
at
possible to modify the random seed used by the code
. In that case,
th
e
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
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment