Τρίτη, 19 Σεπτεμβρίου 2017

Variadic Functions in M2000 plus calling mechanism in M2000.

Because M2000 functions and modules read arguments using a Read command (or more as we can see later), we can make Variadic Functions (accepts a variable number of arguments).


Function Sum {
      n=stack.size
      total=0
      If n=0 then Error "I want one parameter at least"
      for i=1 to n {
            read k
            total+=k
      }
      =total
}

Print Sum(1,2,3,4), sum(10,20)

In functions there is no Return command. Return done at the end of function's code, or by using Exit, or Break, or Error, or when an Error occur.  So char "=" at the left position means "assign value to function". Here we have no "variable" with name as Function. We have an accumulator for this function, so we can store only values.
Type of function can be one number/object or string/object, but we know if we can store string if name has $ as last character. So Sum can't return a string value.

Simple functions are not first class in M2000, but lambda functions are.
We can pass a function by reference (add these lines to previous example):

Module CheckReference (&S()) {
      Print S(1,2,3,4), S(10,20)
}

CheckReference &Sum()

Also notice that Modules get stack from parent, so if we want to pass dynamic number of arguments, we have to include a guard as last argument, or a count indicator at first argument.


Lets see another example. Modules can't passed by reference (later we can see what is a reference for a function). But modules have a decoration mechanism, which pass a module in a module, and that "External" module is locked for changes. So a Module Sum {} inside CheckDecoration can't change external Sum. Another aspect for Modules and Functions, is that variable scope is local to modules and functions, unless we make some globals. Local variables shadow globals with same name. All variables, local and global, erased when module or function terminate execution.

Module Sum {
      Read n \\ here we need a count indicator
      total=0
      If n=0 then Error "I want one parameter at least"
      for i=1 to n {
            read k
            total+=k
      }
      \\ modules can return values in stack
      Push total
}

Sum 4, 1,2,3,4
\\ Number read the top value, if not exist or if not a numeric type then we get an error
Print Number ,
Sum 2, 10,20
Print Number

Module CheckDecoration {
      Sum 4, 1,2,3,4
      Print Number ,
      Sum 2, 10,20
      Print Number
}

CheckDecoration ; Sum



Now lets look another example, where we have a module in a group (object for M2000). We do the decoration of zeta, using a Alfa.Beta module, and when this module called "knows" that is a member of Alfa, so can read B and change it. So it seams that decoration can be used for reference module, if module is part of a group.



Group Alfa {
      B=100
      Module Beta {
            Print .B
            .B++
      }
}

Alfa.Beta
Module zeta {
      module something {
            \\ nothing
      }
      something
}

zeta ; something as Alfa.Beta
Print Alfa.B \\ 102
zeta   \\ do nothing



So now we have to look what is a reference to function. Interpreter do something easy, just take code (source code) of function and wrap it in {}, and export a string.  A reference in M2000 is a string (a weak reference), and for a function is the source, plus the weak reference if function is a member of object (group).
Look this example. We call a function like a module. Which means that we pass parent stack to function. So when we call Beta argument "hello" take place to stack and this stack passed to Call a$. Insert a Print a$ before or after call to see the source of Function AnyName

Function AnyName {
      Print "Function AnyName", letter$
}
\\ we can call a function as a module
Call AnyName("hello")

Module Beta {
      Read a$
      Call a$
}

Beta &AnyName(), "hello"

Module Beta {
      Read &M()
      Call M()
}

Beta &AnyName(), "hello"



We can use the source of a function, through function() (or function$() for string return value). So we can write anonymous functions (not a lambda type)

Print Function("{read X,Y:=X**Y+100}", 10,2)

Function AnyName (x,y) {
      =x*100-4*y+20
}
\\ any identifier with & as first char, is a string with value the actual name of variable
a$=&AnyName()
Print Function(&AnyName(), 10,2)
Print Function(a$, 10,2)

 So anyone can say that a function can live in a string. But only as code. It is easy to use strings as functions, so before lambda functions enter the M2000 language, was the only way to simulate, but without closures.

Now here is the lambda function (we can assign one in a variable):

A=lambda (x) -> {
      =lambda x -> {
            =x
            x++
      }
}
M=A(10)
Print M(), M() ' 10  11
\\ store to N
N=M
Print M(), M() ' 12  13
\\ restore from N
M=N
Print M(), M() ' 12 13
\\ N is a lambda too
Print N(), N() ' 12  13
As we see, A is a lambda function which return a function. So M=A(10) make a function with a closure, the x. And x is mutable, so we can change it with x++. Look at lambda definition, after lambda and before -> (or before parameter list if defined, but we can include a Read command inside body of function), we put X without value (we can put some other variables, with values if we want, as "static" to object, lambda is an object)
Interpreter works fast with lambda definitions. Make an object with a list of closures, as copy of them, and a function definition (as a string). There are two interfaces, one which lambda has no parenthesis, so then we get a copy, and the other, with parenthesis and then we use the function.If we have to use the function, Interpreter first produce a function with an auto name, then make variables (here X) as part of that name, so it is local to to function, then execute function (passing any arguments), in a new stack, and after the exit, all variables collected to object. So new value for X lives in object, and not in list of "current" variables. Object N and M has each a X variable.
Look examples here Using Lambda functions to get data
and here Use of Lambda functions in M2000 Interpreter

So until now we have see Functions and Modules that can be passed to Modules, functions by reference, and modules as decorations, and Lambda functions which can take closures of any type of variables, including lambda too and are first citizens, they can copied to variables, or returned from functions.
Also we can see that variables in a module and function can be local or be group variables, if module or function is a member of group. Also there are global variables. Global variables and group variables have the same assign symbol <=. Group variables outside a member of group use assign symbol =.


Global X=100
Print X \\100
Group M {
      X=10
      Module A {
            Print X
      }
      Module B {
            Print .X, X
            X<=30
            .X<=20
            X=500 \\ this is a local X with value 500, and now shadow the global one
            Print .X, X
            .A   \\ we can call module A because B and A are members of M
      }
}

Print M.X \\ 10
M.B    \\ 10   100
             \\ 20  500
             \\ 30    module M.A found X as global
Print X \\ now global is 30


When two modules are members in a group, can look each other using This and a dot before the name, or a dot only. So .A is This.A and .X<=20 is This.X<=20.
Because X is public member (by default all members of a group are public), we can alter using M.X=100  and this is an assign from external code (not in Group).
But what happen if two modules are not member of a Group, but member of a module, or a function? Then we can't call each other, or here from K we can't call Z.

Module K {
      \\ error. Interpreter can't find Z
     Z
}
Module Z {
      Print "I am Z"
}
K  \\ error

When Event-driven programing included to M2000 then a new call "invented", the Call Local. When we call a function (as a module, but with new stack), with Call Local then this function take the namespace of calling module. So we call a local part of module as a subroutine, and we can see everything "parent" module see.


Function K {
     Z
}
Module Z {
      Print "I am Z"
}
Call Local K()
Call Local "K()"
A$="K()"
Call Local A$
As we can see, we can call K() like is the parent module (or function).

So now we can see the last calling to a local function passed by reference. Before we pass &K() to module testMe we make a second function from Lazy$(). Lazy$() used for making expressions for lazy evaluations, but can be used for making special functions. So when we call Me() then function K executed as parent module, so this is why Z can be called.

 
Function K {
     Z
}
Module Z {
      Print "I am Z"
}
Module testMe (&Me()){
     Call Me()
}
testme lazy$(&K())

Or we can make some other calls too, using Call Local:

Function K {
     Call Local Z()
}
Function Z {
      Print "I am Z"
      M
}
Module M {
      Print "I am M"
}
Module testMe (&me()){
     Call Me()
}
testme lazy$(&K())


Call Local is the way for calling a form "module's" code. When we click in Hello form event Hello.Click() fired. In M2000 we don't connect events. Any event  that has no "connection" can't fired again (if we activate again a form, then the bad list cleared, so system try to see if something defined after and then connected properly)
In the example we see that M is visible by Hello.Click (but it isn't if we use Hello.Click using a simple Call Hello.Click()).


Declare Hello Form
With Hello, "Title","Hello World", "top", 3000, "left", 2000
M=100
Function Hello.Click {
      Print "ok", M
      M++
      Refresh
}
Method Hello, "Show", 1
Declare Hello Nothing



Or we can use an external event to display all events in form (except those events with argument by reference)



Declare Hello Form Event ALFA
With Hello, "Title","Hello World", "top", 3000, "left", 2000
M=100
Function Hello.Click {
      Print "ok", M
      M++
}
Function Alfa {
      Read a$
      Print a$
      Try {
            Call Local a$
      }
      Try {Refresh}
}
Event ALFA New Lazy$(&Alfa())
Method Hello, "Show", 1
Declare Hello Nothing