Σάββατο 25 Φεβρουαρίου 2023

Revision 17, Version 12 - Monad new example

Revision 17 has some fixes.  

We can make date type variables assigning a string for day & ime stamp:

global a as date="25/2/23 12:30:45"
print type$(a)
list

date  a[5]="27/10/23 12:30:45"
print type$(a[0])
print a[0]


dim b(10) as date="27/10/23 12:30:45"
print b(3)
list


https://rosettacode.org/wiki/Monads/Maybe_monad

  • Construct a Maybe Monad by writing the 'bind' function and the 'unit' (sometimes known as 'return') function for that Monad (or just use what the language already has implemented)
  • Make two functions, each which take a number and return a monadic number, e.g. Int -> Maybe Int and Int -> Maybe String
  • Compose the two functions with bind

So this program make a monad/Maybe class which have a flag say "havevalue" when we construct it with e value. We can bind functions (in the example two functions), which  can return result or "none" if the private [val] has no value (so havevalue would be false).

We can put an integer (one time at the construction). We can get a copy and make new variables. We can't change the inner variable.


Class maybe {
private:
variant [val]="none"
boolean haveValue
public:
property val {
value // val return the [val] value
}
function bind(f) {
m=This // we can read private because bind is function of same class as m
if m.haveValue Then m.[val]=f(m.[val])
=m // copy (not pointer)
}
Operator "=" (z as maybe) {
if z.havevalue xor .havevalue then
push false
else
Push z.[val]=.[val]
end if
}
Function unit() {
variant k
if match("G") then  // so we can read maybe class
read g as maybe // fail if not maybe class
if g.havevalue then push g.val
end if
Read ? k
m=This
if not type$(k)="Empty" then
integer v=k ' fail if k can't convert to integer
m.[val]=v
m.haveValue=true
else  // so now we can produce "none" from an object which isn't "none"
m.[val]="none"
m.haveValue=false
end if
=m // copy (not pointer)
}
class:
// after class: all are temporary for the constuction phase
// module with class name is the contructor
// the object constracted before enter this module
// but we can change it. So we return a new one.
module maybe {
// so the constructor is the same as the .unit
// ![] pick argument list and place to unit()
// optionally we can skip the call if we have empty argument list
if not empty then
this=.unit(![])
end if
}
}
none=maybe()
decrement =lambda (x as integer)->x-1%
triple =lambda (x as integer)->x*3%
variant emptyvariant
// 3 and 4 are double, 5 is integer type
SetA=(3,4,none,5%, emptyvariant, none.unit())
k=each(SetA)
document doc$
While K
m1=none.unit(array(K)) // pick element k^
m2=m1.bind(decrement).bind(triple)
m3=maybe(m2)
      doc$=m1.val+" -> "+m2.val+" m2=m3 -> "+if$(m2=m3->"True", "False")+{
      }
End While
Try ok {
m5=maybe("Hello")
}
Print not ok // true , "Hello" not accepted
report doc$
clipboard doc$


This is the version with pointers to groups (Advanced). A group may have one of three state according to the life cycle. A named or unfloat group life end like a local variable. Each member (including functions) are bound to module where we make it, as individual entities (so we can pass by reference). A float group has no name, and maybe has sort life (just as a return value), or maybe has a position in a container like an array, a list or a stack. So the life of a float group depends from the life of its container. A pointer to group, maybe is a weak reference, so the actual life group isn't depend of the pointer's life, or maybe is a real pointer and the life of group which the pointer points depend from the number of pointers who points to it, so when the last pointer deleted or point to some other group, the pointed group deleted. The float groups and the groups which  a pointer hold (a real pointer) are not bound to the module where we use them, except when we use them. To pass a member by reference, from a float group, can be done if we use This objectGroup { } structure which bound temporary the object to that point and we do whatever we want, like it is a named group, although we don't have a name, we use This or dot. We can open more than one (ten at the most) and we can use more dots as prefix to address the 2nd and others groups in the For object {} structure including any folding For object structure. There is a special case when we make a pointer from a float group (from an array item), which make the float group inside array a pointer (from a just float group).

Programmatically the "open" or "non floating" or named group is just a list of members, and all members exist as separate members of module (or if we make it as global, at global level, but the life of them ends at the module's exit). The float group has all the members on it. The pointer, the true one, has a pointer to the float group, and the field IamAPointer with True value. The pointer which isn't true, it is a weak reference has the name (with prefix the qualifier for the lexical scope), so a pName=>X convert to ReferencedName.X. The private members have a special character which not allow to find it at normal search, so if X is private ReferencedName.X not exist. Search is very quick, using internal Hash list.

When we pass a pointer of group to a module or function then we place just a pointer, and at the function/module we can control the way we want that pointer to use: If we don't use type, the default is "as is", a pointer. If we use "as group" or "as typeA" we get a copy as a group. If we use "as pointer" or "as *typeA" we get the pointer. If we pass a float group we can't use the pointer.

So this works:

class Alfa {
x=10
}
b->Alfa()
module z(k as pointer) {
? k=>x
}
z b

But this can't work:

class Alfa {
x=10
}
b=Alfa()
module z(k as pointer) {
? k=>x
}
z b

we have to use:

z pointer(b)

We can't constrain the input using type:

module z(k as *Alfa) {
? k=>x
}
z pointer(b)

In the example bellow we use pointers but in the Unit and in the Operator we use Groups, so the pointer converted to Group (as a copy). See the "as maybe" this convert to group if get a pointer. Is the same if we pass a non pointer type group. (see the first implementation of Class maybe).


We use ->(m) to get the real pointer (the ->m return the reference, the fake poiuter). The ->maybe() return real pointer from a float group, because the maybe() return a float group. If we pass something to maybe() as parameter then maybe we get the result of Unit() which return a pointer, but this converted to float group (a real pointer has a float group, so actual we unbox the pointer and get the float group), when assigned to a normal named group (which is This, at the constructor). This is an example to show this. The k object is a group like the b object (with one member x, of type double). We assign to k the pointer of b (a real pointer, we get a copy of b as float and get a pointer to that object). The variable k hold a group's list so the assign now is a merger (we don't change the set member of the k at the Class definition, so we get the default behavior of merging)

class Alfa {
x=10
}
b=Alfa()
b.x=1000
k=Alfa()
k.x=500
k=pointer((b))
Print k.x=1000
k.x=500
k=pointer((b))
Print k.x=1000

Basically  we need only one time to get a pointer as float group, and at the example below that happen in This=.Unit() and at reading "as maybe".  M2000 as the latest version 12, revision 18, don't behave the same if we use second assignment to k if the first one was done from fake pointer (a reference):

class Alfa {
x=10
}
b=Alfa()
b.x=1000
k=Alfa()
k.x=500
k=pointer(b) ' this is fake pointer, is a weak reference to b
Print k.x=1000
k.x=500
k=pointer(b) ' or k=pointer((b))
Print k.x=500 ' not changed

Maybe this should be fixed in later revisions.



Class maybe {
private:
variant [val]="none"
boolean haveValue
public:
property val {
value // val return the [val] value
}
function bind(f) {
m=This // we can read private because bind is function of same class as m
if m.haveValue Then m.[val]=f(m.[val])
->(m) // pointer
}
Operator "=" (z as maybe) {
if z.havevalue xor .havevalue then
push false
else
Push z.[val]=.[val]
end if
}
Function unit() {
variant k
if match("G") then  // so we can read maybe class
read g as maybe // fail if not maybe class
if g.havevalue then push g.val
end if
Read ? k
m=This
if not type$(k)="Empty" then
integer v=k ' fail if k can't convert to integer
m.[val]=v
m.haveValue=true
else  // so now we can produce "none" from an object which isn't "none"
m.[val]="none"
m.haveValue=false
end if
->(m) // pointer
}
class:
module maybe {
if not empty then
this=.unit(![])
end if
}
}
none->maybe()
decrement =lambda (x as integer)->x-1%
triple =lambda (x as integer)->x*3%
variant emptyvariant
SetA=(3,4,none,5%, emptyvariant, none=>unit())
k=each(SetA)
document doc$
While k
m1=none=>unit(array(k)) // pick element k^
m2=m1=>bind(decrement)=>bind(triple)
m3=maybe(m2)
      doc$=m1=>val+" -> "+m2=>val+" m2=m3 -> "+if$(m2=m3->"True", "False")+{
      }
End While
Try ok {
m5=maybe("Hello")
}
Doc$= (not ok)+{
}  // true , "Hello" not accepted
report doc$
clipboard doc$


Δεν υπάρχουν σχόλια:

Δημοσίευση σχολίου

You can feel free to write any suggestion, or idea on the subject.