Closed mdbergmann closed 6 months ago
These constructs were part of the original E compiler and I've only briefly played with them so I'm not sure how much insight I can provide. There is an AmigaE Facebook group https://www.facebook.com/share/2dSdayHNMFrRYKof/ and although it's not a particularly active or large group, Wouter van Oortmerssen who originally designed and wrote AmigaE is a member of that group so he might be able to assist. No idea if he will respond or not though
Thanks
Yeah, thanks. I looked at this but my questions remain.
ok i had a look at this for you.
because you are using local variables it is by complete luck that this works really. if you make ACCU and IT global variables it works fine.
you are declaring accu and it as local variables here
DEF result, accu, it
result := reduce([1,2,3,4,5], 0, `accu+it)
PrintF('reduce result: \d\n', result)
assertTrue(result = 15, 'Is not 15')
the code to calculate accu+it is compiled using references to the local variables declared here and then you pass a reference to that code into the reduce function it expects to be able to find the accu and it variables on the stack at a particular offset that was correct for the parent function.
by setting tmp you just hit the correct address by sheer luck. if you want do do what you are doing and pass the quoted code into a different function you really need to avoid using local variables within the quoted code as they wont be in the right place on the stack anymore. you might get away with it if you declared the exact same local variables in both routines but i wouldnt risk it.
this code works fine...
DEF accu, it
PROC reduce(lst, initialValue, fun)
DEF tmp, len, i
len := ListLen(lst)
accu := initialValue
FOR i := 0 TO len-1
it := ListItem(lst, i)
accu := Eval(fun)
ENDFOR
ENDPROC accu
PROC assertTrue(value,msg)
IF Not(value)
Throw(1,msg)
ENDIF
ENDPROC
PROC main() HANDLE
DEF result
result := reduce([1,2,3,4,5], 0, `accu+it)
PrintF('reduce result: \d\n', result)
assertTrue(result = 15, 'Is not 15')
EXCEPT
WriteF('fail \s\n',exceptioninfo)
ENDPROC
and to answer your question about the map list function. Maplist iterates over the provided list and performs an operation on each of those items but then returns a new list. You pass in the new list in the first parameter.
So in the example in the user manual
MapList({x},[1,2,3,4,5],r,
x*x) results in: [1,4,9,16,25]`
x should be a variable created as a list to hold the results.
I'm not sure why it has to be passed in as {x} - i guess thats just a quirk of the way the language works.
Cool, thank you for looking into this.
The problem with global vars is of course that i.e. the reduce
function here should be a library function sitting in another module that doesn't know about the global variable.
In MapList
I was actually thinking that r
holds the result list (maybe I'm wrong) and {x}
somehow maybe works around the problem with the global/local variables that you were talking about?
this works because it pulls in the globals from the module
reduce.e
OPT MODULE
EXPORT DEF accu,it
EXPORT PROC reduce(lst, initialValue, fun)
DEF tmp, len, i
len := ListLen(lst)
accu := initialValue
FOR i := 0 TO len-1
it := ListItem(lst, i)
accu := Eval(fun)
ENDFOR
ENDPROC accu
main.e
MODULE '*reduce'
PROC assertTrue(value,msg)
IF Not(value)
Throw(1,msg)
ENDIF
ENDPROC
PROC main() HANDLE
DEF result
result := reduce([1,2,3,4,5], 0, `accu+it)
PrintF('reduce result: \d\n', result)
assertTrue(result = 15, 'Is not 15')
EXCEPT
WriteF('fail \s\n',exceptioninfo)
ENDPROC
OK, that's a good idea. I'm used to multi-threading, in which case this would of course be a problem because not thread-safe. But on Amiga it's no problem, or?
Yes that is a valid point. If you tried to use it from two threads at the same time it could cause issues
It's not an ideal solution. It's a bit strange passing in a piece of code that references variables they aren't obvious what they do since they are hidden away in the module code
I wonder if there's a better way, I'll have a think on it the built in functions don't have the same issues because they are not a separate procedure call so they don't have a problem the local variables.
I'm a little torn between the simplicity of a quoted expression and a function pointer as a 'lambda'. Quoted expressions, as they are right now, are simple but seem error prone, because the behavior is not fully clear. On the other hand function pointers do the right thing, but this requires to define a PROC.
like:
PROC myAddReduceFun(accu, it)
ENDPROC accu+it
reduce([1,2,3,4], 0, {myAddReduceFun})
In that regard it would be awesome being able to define anonymous functions as something like:
reduce([1,2,3,4], 0, &(accu, it => accu+it) -> or some such syntax
Sorry to spam the ticket reporting system. I don't really know where to discuss AmigaE coding. So please let me know where is a better place (anyone reading here, maybe open Discussions?).
I've again played a bit with AmigaE lately and thought I'd add some higher-level functions/PROCs, i.e. for 'reduce/fold'.
What I came up with is this:
Called like so using a quoted expression:
In the reduce function the
tmp := accu
is necessary, otherwise it won't work. But I do not understand why. Can someone shed some light?I've also seen some built-in function like MapList use a variable pointer (
{x}
), but I'm not sure how this is used in the implementation and why is it needed. My variant works with convention thataccu
andit
must be used.