I recreate an example from Groovy language.
Wikipedia about Partial Application
' Partial application
Curry=lambda (f as lambda, k)->(lambda f, k ->(f(k,![])))
joinTwoWordsWithSymbol=lambda (s, a, b)->a+s+b
Assert joinTwoWordsWithSymbol("#","Hello", "World")="Hello#World"
concatWords =Curry(joinTwoWordsWithSymbol, " ")
Assert concatWords("Hello", "World")="Hello World"
prependHello =Curry(concatWords, "Hello")
Assert prependHello("World")="Hello World"
Print "done"
Explanation of the program:
First we make the Curry function. This function return another function with two closures:
The f closure is a function type of lambda. Not all functions of M2000 are lambda functions. There are three types of user functions:
1. A general function
A general function is a block of code which return one or more values (using comma for more values). There is a stack of values which we pass parameters. There is no restriction of how and what parameters we pass when we call a function (interpreter take everything, on an new defined stack of values and pass this to function block of code. The = as a statement record the return value but not return from the calling. For returning from the function, the block has to execute all or an Exit statement can be used or a Break statement. Also a Goto to a non exist label is an exit too. We can exit using Error "error message" raising an error. Or an actual error not controlled in a Try block also exit a function. Also a general function can be redifend
Here in Example #1 we redifine alfa and check the result in an Assert statement (an assert used for logical errors. An actual error also break the statement. An Assert not processed if we use statement Escape Off (this status is true for code executed when we save the code with a run statement, and with hidden script - See Save statement for options). Asserts used to check the code and stop if the compaeison has false result. We can use a list of checks, and the Assert can stop when a check not passed, and show which one not passed.
function alfa {
=100
}
assert alfa()=100
' works that too
' more parameters not used deleted at the exit
assert alfa(3, 4, 5)=100
' number can pop a value
function alfa {
=100*number
}
assert alfa(3)=300
'we can use a Read statement
function alfa {
read k
=100*k
}
assert alfa(3)=300
Some "hidden" properties of general functions are:
1.1) The passing by reference. A function reference is the code of code as string inside curly brackets
push "{=100*number}"
read &a()
assert a(3)=300
push "{read k:=200*k}"
read &a()
assert a(3)=600
A more advanced example (prototype is a syntactic sugar to let editor to use syntax color on a string value. So Prototype {a} as b$ is same as b$={a}, also we can use a variable name without $ for string. Here the example use "hand made" functions as string, pushed to stack of values, and the Read statement which doing the magic:
push "{=100*number}"
read &a()
assert a(3)=300
push "{read k:=200*k}"
read &a()
assert a(3)=600
Prototype {
=100*number, 200*number
} as TwoParam
Prototype {
read m as array, a, b
(a1, b1)=m
=a1=a and b1=b
} as Compare2
' one line function using old BASIC DEF statement:
Def MakeAsFunctionReference(a)="{"+a+"}"
Flush ' empty current stack of values
' Data used to push to end of stack - we use it as FIFO here
Data MakeAsFunctionReference(TwoParam), MakeAsFunctionReference(Compare2)
read &a(), &CompareTwoValues()
assert CompareTwoValues(a(3, 2),300,400)=true
? "done"
Notice from code above the two value assigment. We can use (aa, bb, cc)=(100, "ok", 500)
1.2) The using of a general function like code from a previous module (like a call back function), using a reference from Lazy$() function (is a string return function, sending the code as a string, adding the namespace for the provided code)
function alfa(k) {
=100*k
}
function test1 {
assert alfa()=300
}
function test2 {
assert alfa(2)=300
}
function test3 {
assert alfa(3)=300
}
Module TestMe(&f()) {
try ok {
call f()
}
if error then print error$
}
TestMe lazy$(&test1()) ' k can't initialize
TestMe lazy$(&test2()) ' assert fault (not =200)
TestMe lazy$(&test3()) ' is ok
? "done"
Use the statement ? lazy$(&test1()) to see the code;
We have the TestMe module which execute code trapping errors (so we see the error without stopping the execution). Normaly a function can't see names (identifiers) which ins't local defined to function, or global defined. Here the alfa() function isn't defined to module TestMe, so can't used. So how the f() function knows about alfa() function? The lazy$() get the code of test1() and insert a line which change the current NameSpace to the one where the Lazy$() executed, so for this example is the namespace where the alfa() exist as local identifier. The CALL statement call a function as a Module. This means that the Stack of values passed to function's code. (Modules use the stack of values of caller, can't be passed by reference, and they pass back values through the stack of values, or by using by reference pass of vaeiables, or both)
Because a Call to General Function or Module executed on a new "execute object", any new definition deleted. So we can run code as code of a previous module (in the execution list), but anything new are used temporary. Also the actual stack of values of the previous module maybe not in use for the Call Back. Here is the same because er call TestMe and then we use Call which these two calls also pass the same stack of values, but we have to think when we use this if on our program we have or not access to "back" module. General speaking all "call backs" use variables from an environment which we provide from the definition of the call back. Here the environment is the namespace, so we hava access to identifiers (and not by definition to stack of values, which may happen or not).
1.3) The Final Clause. This used for functions defined in Groups (Group is the user object in M2000).
The following example define a Group with one function, as Final. Group here is a variable who start life executed the definition and end life at the exit of the module/function which created.
A class is a general function which ticked as "group factory" and by default is GLOBAL (except for those Classes definded as members of Groups. This function use the block of code as the Group definition. Also a line types:SomethingElse insert to attach a type name to the object. As return value the "float object", the compact copy of the group returned. Also we can call the class function with parameters and these can be send to the module with the same name of class if exist, or just throwed at the exit of the call.
The Kappa=SomethinEls() is peculiar. Because Kappa exist as Group, the M2000 Interpreter make a Merging process. From this merging tge Final Clause exclude the merging for alfa() function.
This example also showing why using Objects you can mess everything. Here Kappa and Delte are type of SomethingElse. But the alfa() member isn't compatible because we have use the Final statement. Some languages use the Virtual caluse to explicitly state that this function can be changed. For M2000 all general functions can be changed execpt those which are explicitly stated Final in Groups.
Group kappa {
function final alfa(a) {
=100*a
}
}
Class SomethingElse {
function alfa(a) {
=300*a
}
}
Kappa=SomethingElse()
Delta=SomethingElse()
Assert kappa.alfa(2)=200
Assert Delta.alfa(2)=600
Print Delta is Kappa ' false
Print Kappa is type SomethingElse ' true
Print Delta is type SomethingElse 'true
1.4) Functions can defined as Globals:
Also global functions can change code. So we can mess the code if we doit. Using local functions with same name we get local over global aggrement, so we can define our local without need to tale care for same global function. Also we can make new global function using Function Global name {} without mute the current global function's code. The one option to change the global function which defined some modules previous on the execution list is to use th SET statement which execute code like it is from command line interpreter (CLI). CLI always executed at Global name space (but has restrictions like it can't execute a loop).
function global alfa(x) {
=100*x
}
module inner {
Print alfa(2)=200
function alfa(x) {
=200*x
}
Print alfa(2)=400
}
inner ' true true
' if we use Function Global a new function defined
' Using Set we change a Global function if exist, or make new
set function alfa(x) {
=500*x
}
inner ' false true
1.5) Functions may have static variables (just skip this).
Static variables can be initialized and used per execution object. Here this example use two execution objects. One is for running this code (so the Clear statement clear all static variables for all executions objects defined from this point including the current, but not the higher objects).
The Inner module executed on a new object every time. At the end of execution the values of static variables as a backet passed to previous object. So the next time we call the Inner the Interpreter pass the backet to the new object.
But here is something bad. Because the backets are assigned based on the order of the defintion, for a particular code (module's or funciton's), the problem will be when we make a function using Read statement (like in Inner module, which the (&f(), a,b) are copied in a line: Read &(f), a, b
So when we send Stat2() passing by reference, then this will be the first. So the backet of static variables attached to this function (which isn't the Stat1()). As we see at the level of out module (the code supposed witten in a module say A), calling Stat2() not mess with k, because the Stat2() has an order of definition different from Stat1().
So one way to not have this problem is to not use k for the static variable of Stat2(). The other way is to not use Static variables, so go on to next example of using Functions as members of Groups (so the "static" variable is a member of the Group)
function Stat1(x) {
static k=1
=k*x
k++
}
function Stat2(x) {
static k=400
=k+x
k++
Rem list
}
Print Stat1(10)=10
Print Stat1(10)=20
Print Stat2(10)=410
module inner (&f(), a, b) {
Print f(10)=a
Print f(10)=b
}
inner &Stat1(), 10, 20
inner &Stat1(), 30, 40
inner &Stat1(), 50, 60
Print Stat1(10)=30
For This { ' a block for temporary definitions
function New Stat1(x) {
static k=10
=k*x*2
k++
}
Rem modules ?
Print Stat1(10)=80
inner &Stat1(), 140, 160
}
Print Stat1(10)=50
inner &Stat1(), 90, 100
// we expect 410 411
Rem inner &Stat2(), 410, 411
// this is the wrong art
// we get 21 22
inner &Stat2(), 21, 22
Print Stat2(10)=411
Clear ' clear variables from this namespace and static variables too.
So now we want to use Module Inner without using Static variables but with the same functionality
We make two groups, Set_A and Set_B with a private k (for the "static") and a function Stat1(). Also we make a class Stub1 which provide objects with weak references to where we want to reference; Also we provide another Stub which not only get the weak reference (to use the private variable k) but also change the function like we on previous example. The stubs produced groups which return values (and take one parameter). This is good for using a Sta1 group which used as Sta1() function. The problem is that we can't pass this kind of function by reference (we can pass by reference the object or a function of object) so the stabs have functions fun to used for passing a function reference to Inner module
The scope of this second example is to use the Inner module without altering the code of that module. You can see that the number of objects are four. We need two k values for the level of outer modules and two more k values for the Inner module. The first program which use Static variables, uses 3 values. two on the outer module, and one for inner module (because the creation of function has always ordinal number 1, so always get he backet one)
Scope of this text is to ring a bell inside you that you have to know all of the "agreement" between you and the implementation of the interpreter. You don't have to get "AS IS" as is. You have to know what is (the Interpreter) by example. Do not relay to texts, get the "agreement" by example.
group SET_A {
private:
k=1
public:
function Stat1(x) {
=.k*x
.k++
}
}
group Set_B {
private:
k=400
public:
function Stat1(x) {
=.k+x
.k++
}
}
Class Stub1 {
private:
weakobj$
public:
Value (x){
link weak .weakobj$ to &m
=m.Stat1(x)
}
function fun(x) {
=Eval(This, x)
}
Class:
Module Stub1(.weakobj$) {
}
}
Class Stub2 {
private:
weakobj$
public:
Value (x) {
link weak .weakobj$ to &m
' k is private, this is way to get a reference from a private
' We have to know the position of variable
' k is the first variable.
read from m, k
=k *x*2
k++
}
function fun(x) {
=Eval(This, x)
}
Class:
Module Stub2(.weakobj$) {
}
}
InnerA=Set_A
InnerB=Set_B
Stat1=Stub1(&Set_A)
Stat2=Stub1(&Set_B)
print Stat1(10)=10
print Stat1(10)=20
print Stat2(10)=410
module inner (&f(), a, b) {
print f(10)=a
print f(10)=b
}
Let Stat1=Stub1(&InnerA)
inner &Stat1.fun(), 10, 20
inner &Stat1.fun(), 30, 40
inner &Stat1.fun(), 50, 60
Let Stat1=Stub1(&Set_A)
print Stat1(10)=30
' no need block for temporary use
let Stat1=Stub2(&SET_A)
print Stat1(10)=80
let Stat1=Stub2(&InnerA)
inner &Stat1.fun(), 140, 160
let Stat1=Stub1(&SET_A)
print Stat1(10)=50
Let Stat1=Stub1(&InnerA)
Let Stat2=Stub1(&InnerB)
inner &Stat1.fun(), 90, 100
inner &Stat2.fun(), 410, 411
Let Stat2=Stub1(&SET_b)
print Stat2(10)=411
As we see we have to use Let to assign a new object to a group which is a "property". The Let do two thing Push value from Right of sign "=" and then perform a Read for the variable at the left. Let used if we want to evaluate the right part before the left part of assigment. The left part may have an array and expresions for finding the array item to be used. So using the Let A(x+10)=MysteryFunction() we execute the MysteryFunction first and then the x+10 expression.
global x
dim a(10)=1
function alfa(k) {
if k>3 then x<=9
=k
}
x<=1
Print a(x),x ' 1 1
Let a(x)=alfa(5)
Print a(x), x ' 5 9
x<=1
Print a(x), x ' 1 1
a(x)=alfa(5)
x<=1
Print a(x), x ' 5 1
Last example use only one stub for the function with double value (*2). This is more simple. The class stub provide onle the fun(). The returned group isn't a "property" which return a value.
group SET_A {
private:
k=1
public:
function Stat1(x) {
=.k*x
.k++
}
}
group Set_B {
private:
k=400
public:
function Stat2(x) {
=.k+x
.k++
}
}
Class Stub {
private:
weakobj$
public:
function fun(x) {
link weak .weakobj$ to &m
read from m, k
=k *x*2
k++
}
Class:
Module Stub(.weakobj$) {
}
}
InnerA=Set_A
InnerB=Set_B
print Set_A.Stat1(10)=10
print Set_A.Stat1(10)=20
print Set_B.Stat2(10)=410
module inner (&f(), a, b) {
print f(10)=a
print f(10)=b
}
inner &InnerA.Stat1(), 10, 20
inner &InnerA.Stat1(), 30, 40
inner &InnerA.Stat1(), 50, 60
print Set_A.Stat1(10)=30
' no need block for temporary use
Temp=Stub(&SET_A)
print Temp.fun(10)=80
Temp=Stub(&InnerA)
inner &Temp.fun(), 140, 160
print Set_A.Stat1(10)=50
inner &InnerA.Stat1(), 90, 100
inner &InnerB.Stat2(), 410, 411
print Set_B.Stat2(10)=411
2) Simple Function
A simple function is a static part of a module or function. Can be at the callers code but executed like it is at module's code. Here the example has the Inner module which call the simple() function.
Simple functions can't use names of ready made functions like cos, because @cos() call the actual cos() function (we can make a general function with name cos()).
Simple functions haven't own namespace, they use the caller's namespace so we need Local statement to make local variables.
Simple functions haven't own stack of values. A simple function is faster than a general one. The first time Interpreter looking for function from the end and save the position in a list. So each next call is faster than the first one.
If interpreter find simple Function definition then treat this as End. (the same for Sub definition).
simple.k=1
simple.k2=10
Print @simple(10)=100
Print @simple(10)=200
module Inner (&simple.k){
Print @simple(10)=1000
Print @simple(10)=1100
}
Inner &simple.k2
function simple(x)
=100*simple.k
simple.k++
end function
Example with a simple function in a group's module:
group alfa {
private:
k=1
public:
module something {
Print @simple(10)
Print @simple(10)
function simple(x)
=100*.k
.k++
end function
}
}
alfa.something
3) Lambda Function
A lambda function is internal an object with two things: A block of code (the function) and a list of closures. A closure is local to lambda, and is like global if we use recursion. Because a lambda is an anonymus function, we use lambda() for calling itself.
There is lambda$ for string type results, although the newer Interpreter can use functions and variables for strings without using $. The partial application on the top is like this.
The example bellow use k like a global for internal use. The a has one lambda function. When executed return the result from another lambda, which start with a closure k with value 0. we pass the parameter x and the function do the recursion and return the result
a=lambda (x)->{
=lambda k=0 (x as long) -> {
if x<1 then exit
k+=10
=lambda(x-1)+x/k
}(x)
}
Print a(10)=0.55
Print a(8)=0.44
Here is the advanced example of the "static" replacer. We use a stub also, because we want to send a function by reference (which a lambda can do this) but we want to change the result. So we use a stumb to compose a function with a weak reference to the actual lambda function which hold the hidden "closure" (there is no way to read the closure, it is a value type. We have to put a closure as object type, but not show here something like this). The only way to store the inner value is to get a copy of the lambda to another lambda. Here we do that with iSta1 and iStat2 and nStat1
The code is more compact
Stat1=lambda k=1 (x) ->{
=k*x
k++
}
Stat2=lambda k=400 (x) ->{
=k+x
k++
}
iStat1=Stat1
iStat2=Stat2
Print Stat1(10)=10
Print Stat1(10)=20
Print Stat2(10)=410
module inner (&f(), a, b) {
Print f(10)=a
Print f(10)=b
}
inner &iStat1(), 10, 20
inner &iStat1(), 30, 40
inner &iStat1(), 50, 60
Print Stat1(10)=30
For This { ' a block for temporary definitions
stub=lambda (w$)->{
=lambda w$ (x) -> {
link weak w$ to f
=2*f(x)
}
}
Print stub(&Stat1)(10)=80
nStat1=stub(&iStat1)
inner &nStat1(), 140, 160
}
Print Stat1(10)=50
inner &iStat1(), 90, 100
inner &iStat2(), 410, 411
Print Stat2(10)=411