1. Pass by Reference
- Posted by dcuny Dec 18, 2014
- 3620 views
I'm writing an application that has multiple digital filters. Calling the filter:
- Calculates a filtered result, and
- Updates the filter's history.
That is, two different things are being modified here - there's a result returned from the function call, and as a side-effect, a data structure gets updated.
My first approach was to write the filter as a namespace, and try to treat them like an object. Only.. namespaces aren't really objects, and things got ugly fast.
My second approach was to write something like:
function do_filter( sequence theFilter, atom theInput ) atom theOutput = theFilter[2] * theFilter[1] * theInput theFilter[2] = theFilter[1] theFilter[1] = theInput return theOutput end function atom theResult = do_filter( theFilter, theInput )
Of course, this doesn't work because Euphoria only passes parameters by value and not by reference. I started remembering why I'd worked on Py.
So what to do with the result of the filter?
One solution would be to pass a sequence back from the function, which is really clunky:
function do_filter( sequence theFilter, atom theInput ) atom theOutput = theFilter[2] * theFilter[1] * theInput theFilter[2] = theFilter[1] theFilter[1] = theInput return {theFilter, theOutput} end function sequence theResult = do_filter( theFilter, theInput ) theFilter = theResult[1] atom theOutput = theResult[2]
Yes, it's the worst filter ever. I don't have the exact code in front of me, but it gets to the essence of the problem.
Another option is to store the value return inside theFilter, which is only slightly less clunky:
enum FILTER_HIST_1, FILTER_HIST_2, FILTER_RESULT function do_filter( sequence theFilter, atom theInput ) atom theOutput = theFilter[FILTER_HIST_1] * theFilter[FILTER_HIST_2] * theInput theFilter[FILTER_HIST_2] = theFilter[FILTER_HIST_1] theFilter[FILTER_HIST_1] = theInput theFilter[FILTER_RESULT] = theOutput return theFilter end function -- pass the filter data and value to filter to the filtering routine theFilter = do_filter( theFilter, theInput ) -- save the filtered value atom result = theResult[FILTER_RESULT]
But it raises the question: why do I have to jump through these hoops at all? Why can't OpenEuphoria let me pass values by reference like just about any other language?
- David
2. Re: Pass by Reference
- Posted by jimcbrown (admin) Dec 18, 2014
- 3611 views
I'm writing an application that has multiple digital filters. Calling the filter:
- Calculates a filtered result, and
- Updates the filter's history.
That is, two different things are being modified here - there's a result returned from the function call, and as a side-effect, a data structure gets updated.
<snip>
But it raises the question: why do I have to jump through these hoops at all? Why can't OpenEuphoria let me pass values by reference like just about any other language?
- David
As I recall, eumem was written explicitly for this use case.
Maps (called dictionaries by Python iirc) use eumem internally precisely for this reason - they need to emulate PBR.
See http://openeuphoria.org/docs/std_eumem.html#_5543_ram_space for details...
3. Re: Pass by Reference
- Posted by DerekParnell (admin) Dec 18, 2014
- 3509 views
I'm writing an application that has multiple digital filters. Calling the filter:
- Calculates a filtered result, and
- Updates the filter's history.
That is, two different things are being modified here - there's a result returned from the function call, and as a side-effect, a data structure gets updated.
Using the eumem.e library, one can emulate this less-than-safe programming practice. When using this library, you acquire a reference to some 'global' memory object and can pass that reference to routines.
include sys/eumem.e object aFilter = eumem:malloc({0,0}) -- Initialze new structure with a couple of zero values. function do_filter( object theFilter, atom theInput ) atom theOutput = ram_space[theFilter][2] * ram_space[theFilter][1] * theInput ram_space[theFilter][2] = ram_space[theFilter][1] ram_space[theFilter][1] = theInput return theOutput end function atom theResult = do_filter( aFilter, theInput )
But it raises the question: why do I have to jump through these hoops at all? Why can't OpenEuphoria let me pass values by reference like just about any other language?
Because its more well behaved than other languages.
4. Re: Pass by Reference
- Posted by _tom (admin) Dec 18, 2014
- 3519 views
Why can't OpenEuphoria let me pass values by reference like just about any other language?
One "theme" of Euphoria is the lack of surprise...pass by reference, and routine parameters that change their arguments can result in surprises. Every change to an object is very deliberate; that means it is easier to debug.
OOEU does have pass by reference as an experiment. The result was only a small increase in speed.
_tom
5. Re: Pass by Reference
- Posted by dcuny Dec 18, 2014
- 3518 views
As I recall, eumem was written explicitly for this use case.
Thanks, Jim.
You could be right, but the documentation is entirely opaque to me. It looks like it's doing something roughly equivalent to declaring a globally visible heap. If that's the case, how is it much different from:
global sequence GLOBAL_MEMORY = {} -- get the next handle (index) to the global sequence global function malloc() GLOBAL_MEMORY &= 0 return length(GLOBAL_MEMORY) end function -- get the value stored in the global sequence global function get(integer index) return GLOBAL_MEMORY[index] end function -- set a value stored in a global sequence global procedure set(integer index, object value) GLOBAL_MEMORY[index] = value end procedure
But it doesn't answer the question why OpenEuphoria support pass by reference.
- David
6. Re: Pass by Reference
- Posted by jimcbrown (admin) Dec 18, 2014
- 3514 views
You could be right, but the documentation is entirely opaque to me. It looks like it's doing something roughly equivalent to declaring a globally visible heap. If that's the case, how is it much different from:
Actually, you just rewrote eumem from scratch.
7. Re: Pass by Reference
- Posted by DerekParnell (admin) Dec 18, 2014
- 3588 views
But it doesn't answer the question why OpenEuphoria support pass by reference.
If one passes a reference to an item to a routine, without actually looking at the receiver's source code, can the coder know if the passed item will be modified or not?
If a language knows, without a doubt, that a passed item cannot be modified by the called routine, then many optimisation possibilities open up for it.
If you want to see this philosophy in action, take a look at the D Programming Language. That modern language does support PBR but that is not the default. The default is that declared items are read-only, even when passed by reference. This language forces the coder to make extremely explicit, any intention to modify items passed to routines. The resulting object code can be very optimised when the compiler knows about this.
In general, it is a safer practice to pass by value, and much less costly to maintain programs written this way. The performance of PBR is not generally an issue but in those situations where it is a factor, you can revert to less safe coding practices (using 'global' memory).
8. Re: Pass by Reference
- Posted by DerekParnell (admin) Dec 18, 2014
- 3515 views
Actually, you just rewrote eumem from scratch.
Well.... the library functionality is a little bit more sophisticated than that, but that is it in essence.
9. Re: Pass by Reference
- Posted by dcuny Dec 18, 2014
- 3504 views
If one passes a reference to an item to a routine, without actually looking at the receiver's source code, can the coder know if the passed item will be modified or not?
And..?
That's the case with anything else. Without looking at the code you can't tell if a global value is being altered, either. Yet OpenEuphoria allows it. And that's actually the (really dirty) workaround being suggested here.
If a language knows, without a doubt, that a passed item cannot be modified by the called routine, then many optimisation possibilities open up for it.
Yep. The purpose of a language isn't to be optimized, but to be useful.
In general, it is a safer practice to pass by value, and much less costly to maintain programs written this way. The performance of PBR is not generally an issue but in those situations where it is a factor, you can revert to less safe coding practices (using 'global' memory).
IMNSHO, Euphoria is one of the most unsafe languages I've ever coded in.
It crashes on errors... by design.
It took forever for Robert to be convinced that something like crash_routine() even needed to exist.
The core datatype - the sequence - is inherently unsafe. It's trivial to get an index out of bounds error. (I speak from experience!)
I'd argue that Euphoria is unsafe by design.
- David
10. Re: Pass by Reference
- Posted by petelomax Dec 18, 2014
- 3490 views
One solution would be to pass a sequence back from the function, which is really clunky:
function do_filter( sequence theFilter, atom theInput ) atom theOutput = theFilter[2] * theFilter[1] * theInput theFilter[2] = theFilter[1] theFilter[1] = theInput return {theFilter, theOutput} end function sequence theResult = do_filter( theFilter, theInput ) theFilter = theResult[1] atom theOutput = theResult[2]
how about
{theFilter,theOutput} = do_filter(theFilter,theInput)
But it raises the question: why do I have to jump through these hoops at all? Why can't OpenEuphoria let me pass values by reference like just about any other language?
If a language knows, without a doubt, that a passed item cannot be modified by the called routine, then many optimisation possibilities open up for it.
Phix actually supports PBR, but ONLY for local values:
global sequence gtable global atom gthing {gtable,gthing} = filter(gtable,gthing) -- NOT PBR procedure p() sequence ltable atom lthing {ltable,lthing} = filter(ltable,lthing) -- pbr end procedure
Technically, it isn't pbr, but reference counts of 1, and works by making the locals unassigned over the call, which is perfectly reasonable since they are going to get overwritten as soon as it returns. When I say "NOT PBR", I am in fact referring to internal behaviour and copy-on-write semantics that impact performance, rather than functionality. If you wanted pbr optimisation effects on gtable,gthing you would need something a bit messy:
ltable = gtable lthing = gthing gtable = {} gthing = 0 {ltable,lthing} = filter(ltable,lthing) {gtable,gthing} = {ltable,lthing}
which is only needed, of course, if you insist on having those nasty globals in the first place, and indeed cared about a few copy-on-writes.
IMNSHO, Euphoria is one of the most unsafe languages I've ever coded in.
It crashes on errors... by design.
The core datatype - the sequence - is inherently unsafe. It's trivial to get an index out of bounds error. (I speak from experience!)
I'd argue that Euphoria is unsafe by design.
And I would totally agree with you, but for completely different reasons. I would in fact shout it from the rooftops as one of the BEST IDEAS EVER. Compared to C++ just quietly going wrong, anyway.
Pete
PS: disclaimer: the above expresses how it is designed to work, rather than any assurance it is available and working like that right now.
11. Re: Pass by Reference
- Posted by jaygade Dec 18, 2014
- 3484 views
The Euphoria way would be to use a global or file-level variable for large sequences that need to be manipulated in place. Just look at the source of the interpreter itself.
Not that that's a bad thing, it just doesn't scale beyond a certain point -- admittedly, a pretty large point.
Google's Go language does pass by value on everything, but has pointers, and slices are pointers to arrays. So with pointers and slices you can manipulate large data structures in place.
Then again, golang folks are as adamant of its features and/or lack of other features as we Euphorians are. *shrug*
The question is whether you are willing to perform your task in an idiomatic way or try to force the language to doing things your way.
EDIT Just like me and this damned creole markup. Grr.
12. Re: Pass by Reference
- Posted by dcuny Dec 19, 2014
- 3471 views
And I would totally agree with you, but for completely different reasons. I would in fact shout it from the rooftops as one of the BEST IDEAS EVER. Compared to C++ just quietly going wrong, anyway.
But that's not an equivalent comparison. In something like C where you've got a bad pointer, you have no idea what went wrong.
The interpreted version of Euphoria not only knows that something has gone wrong, but it knows what has gone wrong. The program can even provide that information to itself so if could decide how to recover.
One of the problems with Euphoria is that it isn't balanced. It crashes at the drop of a hat (by design), but doesn't offer any good mechanisms to recover from the error.
The thing is, you can recover from those errors. The try/catch mechanism is a great example. I can't imagine why Euphoria doesn't have some equivalent mechanism.
If Euphoria balanced crashing with a powerful recovery mechanism, that would be great.
But it doesn't, which means that unless you write perfect code, it's going to crash.
Similarly, I like the fact that Euphoria offers me a very flexible data structure, so I don't have to resort to malloc and accidentally trashing memory.
But why does so much of the remainder of the language have to remain so inexpressive? Sure, I can implement structures by using a global sequence, and then pass in an index. But Euphoria (as a language) doesn't support that. So I could write:
-- all structures are stored in a global sequence called STRUCT STRUCT[myFilter][FREQUENCY] = frequency
Gah... That's horrible. Once upon a time, I ended up writing my own macro language for Euphoria just so I wouldn't have to write zillions and zillions of indexes.
Or I could write:
procedure set_struct(integer structureNumber, integer attributeIndex, object value) STRUCT[structureNumber][attributeIndex] = value end procedure -- Yay! Now it's even *more* expensive, since it needs a function call! set_struct(myFilter, FREQUENCY, frequency)
A slightly better version might be:
-- put it in a local so it doesn't need so many indexes... structure filter = STRUCT[myFilter] -- work with it normally filter[FREQUENCY] = frequency -- but don't forget to save it back again! STRUCT[myFilter] = filter
which saves me a bit of writing, but is still a hot mess.
It seems to me that Euphoria ends up being a "simple" language, but requires a lot of extra work for what comes "for free" in other languages.
- David
13. Re: Pass by Reference
- Posted by jimcbrown (admin) Dec 19, 2014
- 3449 views
But that's not an equivalent comparison. In something like C where you've got a bad pointer, you have no idea what went wrong.
The interpreted version of Euphoria not only knows that something has gone wrong, but it knows what has gone wrong. The program can even provide that information to itself so if could decide how to recover.
Right, but the translated version probably wouldn't have any of this information. (The shrouded version is somewhere in between these two extremes.)
One of the problems with Euphoria is that it isn't balanced. It crashes at the drop of a hat (by design), but doesn't offer any good mechanisms to recover from the error.
The thing is, you can recover from those errors. The try/catch mechanism is a great example. I can't imagine why Euphoria doesn't have some equivalent mechanism.
If Euphoria balanced crashing with a powerful recovery mechanism, that would be great.
But it doesn't, which means that unless you write perfect code, it's going to crash.
Agreed. Adding something like a try/catch mechanism is on the TODO list for this very reason.
Similarly, I like the fact that Euphoria offers me a very flexible data structure, so I don't have to resort to malloc and accidentally trashing memory.
But why does so much of the remainder of the language have to remain so inexpressive? Sure, I can implement structures by using a global sequence, and then pass in an index. But Euphoria (as a language) doesn't support that.
It does with eumem.... again that's the whole point of eumem. Why is using eumem (or your superior rewrite with its getter/setter routines) so much more of a problem compared to built-in PBR or pointers?
It seems to me that Euphoria ends up being a "simple" language, but requires a lot of extra work for what comes "for free" in other languages.
Agreed on the try/catch part. Disagree on the PBR part... but if you really want to add PBR support to Euphoria itself, I don't know of anything stopping you. (If done cleverly, you could add PBR support just by modifying the parser, removing the need to change any underlying C code.)
I imagine that, if done in the right style (ala D, so PBR is not the default and made very explicit), a proof-of-concept feature could be merged into the mainlane pretty easily, without much objection.
Since we already have eumem, which is "good enough", I don't see a compelling reason to work on PBR as a core language feature. But if someone provided a patch with the hard work already done, then it'd be a different story.
14. Re: Pass by Reference
- Posted by dcuny Dec 19, 2014
- 3435 views
The question is whether you are willing to perform your task in an idiomatic way or try to force the language to doing things your way.
I understand that Euphoria don't have pass by reference. I think I've got a pretty good grasp on the "idiomatic" way of doing it in Euphoria.
I also understand that pass by reference is somehow "bad" or "unsafe", but it's not clear why this is considered to be the case.
This is especially confusing, since I use it all the time in various other programming languages.
Consider if Euphoria did allow passing by reference, using a '*' character in the parameter list of the declaration:
procedure swap(object *a, object *b) object tmp = a a = b b = a end procedure
It's clear in the declaration that the routine swap expects to be able to modify a and b. So you don't actually have to read the code to see what it's going to do.
Having implemented pass by reference in Py, I should note an obvious caveat: you can only pass assignable values. For example:
-- not legal swap( 1, 2 )
The reason for the limitation was because behind the scenes, Py was basically doing the same sort of thing I was complaining about not wanting to write:
procedure swap(object a, object b) object tmp = a a = b b = a return {a, b} end procedure object_result = swap( 1, 2 ) 1 = result[1] -- not legal 2 = result[2] -- also not legal object_ = 0 -- deref
Pure Euphoria, with the parser handling all the nasty details.
Of course, if the parser is smart enough, it can simply just not emit code for non-assignable expressions.
Here's another example:
function foo(integer *a, integer b) a = 23 return 0 end function integer a = 11 integer b = foo(a, 11)
This would be transformed to something more like:
function foo(integer a, integer b) foo = 23 return {0, foo} end function integer a = 11 object_result = foo(a, 11) a = result[2] integer b = result[1] result_ = 0 -- deref
So it's certainly something that Euphoria is capable of, and it's not even a "dangerous" as a goto, since it doesn't break any existing rules.
- David
15. Re: Pass by Reference
- Posted by dcuny Dec 19, 2014
- 3459 views
It does with eumem.... again that's the whole point of eumem. Why is using eumem (or your superior rewrite with its getter/setter routines) so much more of a problem compared to built-in PBR or pointers?
I don't think my rewrite is any better... I just couldn't grok how eumem was supposed to work from the docs, and have been too busy flaming away to pull up any examples.
Since we already have eumem, which is "good enough", I don't see a compelling reason to work on PBR as a core language feature. But if someone provided a patch with the hard work already done, then it'd be a different story.
I truly appreciate the work that's gone into maintaining and extending Euphoria. But it's frustrating to see that some of the stupid stuff that drove me away from the language is still there. I just don't want to get sucked into that timesink again... But we'll see.
- David
16. Re: Pass by Reference
- Posted by Steady Dec 19, 2014
- 3417 views
Interesting discussion.
I agree with David Cuny that PBR is essential in any language.
I also think that GOTO, ON… GOTO, GOSUB, ON…GOSUB are needed and can be used nicely.
You have just changed everything to IF…THEN…ELSE…ELSIF, or DO….WHILE which also would be equally catastrophic if used incorrectly.
In defense of these statements I can say that the languages (BASIC and EUPHORIA) were developed in the days of KB and MB of memory. In these days of GB memory and TB drives, the approach should be to get your C, D and other derivative languages right, because you put heavy reliance on these in your compilation and DLL usage.
BTW, David, I do not see any reason for you to abandon WxBasic. It was a good effort and you should bring it to the level of Wxwidgets 3.0.2
17. Re: Pass by Reference
- Posted by _tom (admin) Dec 19, 2014
- 3404 views
I'm writing an application that has multiple digital filters. Calling the filter:
- Calculates a filtered result, and
- Updates the filter's history.
Euphoria 4.1 lets you do this:
include std/sequence.e include std/console.e function do_filter( sequence theFilter, atom theInput ) atom theOutput = theFilter[1] * theFilter[2] * theInput theFilter[2] = theFilter[1] theFilter[1] = theInput return { theFilter, theOutput } end function atom theInput = 1 sequence theFilter = {2,3} atom theResult = 0 printf(1, "START\n\t input: %d", theInput ) printf(1, "\n\t filter: %d %d", theFilter ) printf(1, "\n\t result %d", theResult ) { theFilter, theResult } = do_filter( theFilter, theInput ) printf(1, "\n\nFINISH\n\t input: %d", theInput ) printf(1, "\n\t filter: %d %d", theFilter ) printf(1, "\n\t result %d", theResult ) --START -- input: 1 -- filter: 2 3 -- result 0 -- --FINISH -- input: 1 -- filter: 1 2 -- result 6
The idea is that in Euphoria you can always identify the exact line where an object changes its value.
_tom
18. Re: Pass by Reference
- Posted by jimcbrown (admin) Dec 19, 2014
- 3378 views
I'm writing an application that has multiple digital filters. Calling the filter:
- Calculates a filtered result, and
- Updates the filter's history.
Euphoria 4.1 lets you do this:
{ theFilter, theResult } = do_filter( theFilter, theInput )
The idea is that in Euphoria you can always identify the exact line where an object changes its value.
_tom
IIRC, the idea for the desequencing operation (a "poor man's multiple return value") originally came from dcuny.
19. Re: Pass by Reference
- Posted by mattlewis (admin) Dec 19, 2014
- 3397 views
But it doesn't answer the question why OpenEuphoria support pass by reference.
I started working on doing this before OpenEuphoria was a thing. I think PBR allows some very important features and optimizes some important cases. I'd really like to see it be possible. I made some progress, but I recall getting stuck on some things.
The really short answer is that it's a difficult problem to make it work and not break lots of stuff.
Matt
20. Re: Pass by Reference
- Posted by dcuny Dec 19, 2014
- 3394 views
BTW, David, I do not see any reason for you to abandon WxBasic. It was a good effort and you should bring it to the level of Wxwidgets 3.0.2
I wish I had the time! Basically, I put a bunch of cool stuff I saw in other languages into wxBasic.
But I found that I ended up spending all my time working on tools, but never having time to use them.
Unfortunately, no one stepped up to take over the coding, so it's languished since then.
The reality is that I never would have written wxBasic if wxLua had been written first. Since there are nice scripting languages to fill the "scripting language for wxWidgets" gap, I don't feel that bad about it. But it is a sweet little language (only slightly biased opinion here).
- David
21. Re: Pass by Reference
- Posted by Steady Dec 21, 2014
- 3230 views
BTW, David, I do not see any reason for you to abandon WxBasic. It was a good effort and you should bring it to the level of Wxwidgets 3.0.2
I wish I had the time! Basically, I put a bunch of cool stuff I saw in other languages into wxBasic.
But I found that I ended up spending all my time working on tools, but never having time to use them.
Unfortunately, no one stepped up to take over the coding, so it's languished since then.
The reality is that I never would have written wxBasic if wxLua had been written first. Since there are nice scripting languages to fill the "scripting language for wxWidgets" gap, I don't feel that bad about it. But it is a sweet little language (only slightly biased opinion here).
- David
Although I have not used WxLua, your acceptance of it is good enough for me. However, the fact is that from a "Branding" point of view, everybody knows about "BASIC" language - the older generation knows it as "BASIC" and the younger generation knows it as "VISUAL-BASIC"; therefore, WxBasic would be a good language to work with - the promotion work has already been done for you.
22. Re: Pass by Reference
- Posted by jmduro Dec 22, 2014
- 3202 views
David's wxBasic is still maintained by a german team on www.wxbasic.net. I just downloaded a 64 bits Windows version to test it.
Regards
Jean-Marc
23. Re: Pass by Reference
- Posted by dcuny Dec 23, 2014
- 3136 views
Yes, I believe that Ralf Peters is the person currently maintaining that version.
- David
24. Re: Pass by Reference
- Posted by dcuny Dec 26, 2014
- 3041 views
I'm slowly grinding my way through my Java application, porting it to Euphoria. The mapping of between the two is pretty straightforward, but a bit tedious.
The "attributes" of a class can be declared via enum:
public enum attrib_alpha, attrib_beta, attrib_gamma, element_count
and then a constructor declared along the lines of:
public function new( ... ) -- create an instance sequence this = malloc(0, element_count-1) -- initialization code goes here this[attrib_alpha] = rnd() this[attrib_beta] = rnd() this[attrib_gamma] = rnd() return malloc(this) end function public function my_method(integer this_handle) -- get the instance sequence this = ram_space[this_handle] -- change an attribute this[attribute_alpha] = rnd() -- update the object ram_space[this_handle] = this -- return some result return ram_space[this_handle] end function
If I didn't mind being clunky, I could even code this as:
public function my_method(integer this_handle) -- change an attribute ram_space[this_handle][attribute_alpha] = rnd() -- return some result return ram_space[this_handle][attribute_alpha] end function
Calling the routine is along the lines of:
include "my_class.e" -- create an instance of my_class integer instance = my_class:new() -- call a method of my_class integer result = my_class:my_method(instance, integer new_alpha)
I can access class attributes like so:
-- this is equivalent to instance.attrib_alpha ? ram_space[instance][my_class:attrib_alpha]
This (in theory) would work fairly well for classes without inheritance. It would obviously work much nicer if the interpreter handled this stuff automatically. I need play with this some more to see how well it would work for wxWidgets. In the mean time, I'll continue converting my code from Java and see what sort of snags I run into.
- David
25. Re: Pass by Reference
- Posted by Shian_Lee Jan 03, 2015
- 2951 views
I think that programming style fits to specific personality; Personally I really don't like 'Pass by Reference' - because it's less maintainable and very confusing. local and global variables I use with caution, when really necessary, with full documentation.
I think that Euphoria is very safe if you're a disciplined programmer. But I/O routines must not trigger a fatal error - instead they should return an error value. I don't see why an I/O error should crash a program, it's ridiculous.
David's 'filters' program can be written very easily in Euphoria, in many ways, as long as you are a disciplined and consistent programmer.
I've never learned programming officially. Yet I took the job from professional programmers because the programs I wrote were maintainable. My last boss realised very quick that maintainable programs are saving him a lot of money.
For writing maintainable programs, I try to avoid any advanced or confusing feature, such as 'Pass by Reference', overriding identifiers, etc.
26. Re: Pass by Reference
- Posted by dcuny Jan 04, 2015
- 2947 views
David's 'filters' program can be written very easily in Euphoria, in many ways, as long as you are a disciplined and consistent programmer.
Please clarify what you mean by "disciplined and consistent."
I've outlined the ways that I understand this can be done in Euphoria. None of these ways seem particular "very easily" done, especially in comparison with other languages. Rather, the solutions seem rather clunky to me.
- David
27. Re: Pass by Reference
- Posted by jimcbrown (admin) Jan 04, 2015
- 2920 views
None of these ways seem particular "very easily" done, especially in comparison with other languages. Rather, the solutions seem rather clunky to me.
Not sure I agree with you here. The difference between
ref a = b
and
ram_space[a] = b
is largely just syntax sugar. A good preprocessor can convert the former to the latter, in fact my preprocessor (Dredge) did something just like this. Considering v4's builtin support for hooking into preprocessors, this sort of syntax sugar should be very easy to add to the main language itself - if only someone could find the time to write one....
28. Re: Pass by Reference
- Posted by Shian_Lee Jan 04, 2015
- 2891 views
David's 'filters' program can be written very easily in Euphoria, in many ways, as long as you are a disciplined and consistent programmer.
Please clarify what you mean by "disciplined and consistent."
I've outlined the ways that I understand this can be done in Euphoria. None of these ways seem particular "very easily" done, especially in comparison with other languages. Rather, the solutions seem rather clunky to me.
- David
David, please don't compare Euphoria to other languages.
Any language has its plus and minus - the overall advantage is the important, not small things, such as using equal() instead of =.
In order to protect the plus of Euphoria, in order to keep Euphoria rapid, easy-to-learn, easy-to-use, readable, powerful - we must protect the minus as well.
i.e. in order to protect the simplicity of Euphoria - we must protect its minus which is pass by value.
About "disciplined and consistent.":
From my point of view, the entire C language is clunky. Also BASIC is clunky whenever you have to convert between types. The entire 'Assembly' language is clunky.
...So few lines of "clunky" Euphoria code are forgivable. Especially when these few lines are able to replace many lines of a clunky language such as C.
By being consistent in your coding style - even the most clunky language looks great. Just take a look at Assembly code: some programs are looking so terrible that it hurts the eyes; other programs are looking beautiful like art.
To make a program looking beautiful like art, it needs time, self discipline, and consistent coding style. This kind of programs are readable, maintainable, and with less bugs.
29. Re: Pass by Reference
- Posted by jimcbrown (admin) Jan 04, 2015
- 2887 views
Any language has its plus and minus - the overall advantage is the important, not small things, such as using equal() instead of =.
In order to protect the plus of Euphoria, in order to keep Euphoria rapid, easy-to-learn, easy-to-use, readable, powerful - we must protect the minus as well.
i.e. in order to protect the simplicity of Euphoria - we must protect its minus which is pass by value.
If I am understanding this correctly, you are stating that:
In order to protect the positive attributes of Euphoria - we must protect the negative attributes of Euphoria as well..
Also, you define 'simplicity' of Euphoria as a positive attribute, but 'pass by value' as a negative attribute.
Please correct me if I have misunderstood something here.
30. Re: Pass by Reference
- Posted by Shian_Lee Jan 04, 2015
- 2889 views
If I am understanding this correctly, you are stating that:
In order to protect the positive attributes of Euphoria - we must protect the negative attributes of Euphoria as well..
Also, you define 'simplicity' of Euphoria as a positive attribute, but 'pass by value' as a negative attribute.
Please correct me if I have misunderstood something here.
Don't get me wrong:
Simplicity is the power of Euphoria, as well as Pass by Value! which also makes Euphoria rapid and powerful from my point of view.
Pass by Value is a pure anti-bug weapon.
It just that other programmers might think different then me.
31. Re: Pass by Reference
- Posted by jimcbrown (admin) Jan 04, 2015
- 2868 views
Don't get me wrong:
Simplicity is the power of Euphoria, as well as Pass by Value! which also makes Euphoria rapid and powerful from my point of view.
Pass by Value is a pure anti-bug weapon.
It just that other programmers might think different then me.
Yep. I feel that simple is good, but too simple is bad. In other words, you can have too much of a good thing. All things in moderation, etc.
In the past, RapidEuphoria was too simple, and that held it back. OpenEuphoria has taken steps to improve the balance, but I don't think anyone could reasonably disagree that there's still room for additional improvements.
Earlier, I didn't quite agree that PBR was one of these things, seeing that it was so easily emulated. Looking at dcuny's attempts to wrap wxWidgets in an object-oriented way though, I'm starting to move towards PBR being a necessary feature for OO. (Sure, you can get away with emulated PBR, but the lack of necessary syntax sugar makes things clumsier and introduces more room for programmer error.)
32. Re: Pass by Reference
- Posted by Shian_Lee Jan 04, 2015
- 2886 views
Yep. I feel that simple is good, but too simple is bad. In other words, you can have too much of a good thing. All things in moderation, etc.
I think that global/local variable is much more safe attitude then 'Pass by Reference'.
'Pass by Reference' quickly gets out of control. while global/local variable is much more clear and easy to be documented.
...I could guess that as soon as OOP will arrive to Euphoria - things will get messy. I truly wish that there is a way to implement OOP in Euphoria, without destroying the best parts of it - such as 'Pass by Value'.
33. Re: Pass by Reference
- Posted by jimcbrown (admin) Jan 04, 2015
- 2882 views
I think that global/local variable is much more safe attitude then 'Pass by Reference'.
'Pass by Reference' quickly gets out of control. while global/local variable is much more clear and easy to be documented.
My point is that they're functionally identical, so if PBR gets out of control, PBR emulation by global variables will get out of control in precisely the same way.
But I don't think that PBR (true or by emulation with global variables) would get out of control anyways. As I said originally, it's all in the moderation:
Yep. I feel that simple is good, but too simple is bad. In other words, you can have too much of a good thing. All things in moderation, etc.
34. Re: Pass by Reference
- Posted by petelomax Jan 04, 2015
- 2892 views
Earlier, I didn't quite agree that PBR was one of these things, seeing that it was so easily emulated. Looking at dcuny's attempts to wrap wxWidgets in an object-oriented way though, I'm starting to move towards PBR being a necessary feature for OO. (Sure, you can get away with emulated PBR, but the lack of necessary syntax sugar makes things clumsier and introduces more room for programmer error.)
It depends on the implementation. Given a C function
void thing(int *i) {...}Then invoking it using
thing(&count)is fine in my book, but should the compiler take
thing(count)and auto-correct it for you with some kind of implicit type cast, it becomes hideously awful.
Instead of
thing(&i,&j,&k)I will always prefer
{i,j,k} = thing(i,j,k)It expresses the intent much more explicitly and is much more flexible, for instance you could preserve i,j,k and discard the updated values
Pete
35. Re: Pass by Reference
- Posted by dcuny Jan 04, 2015
- 2855 views
I will always prefer
{i,j,k} = thing(i,j,k)
Why not
i,j,k = thing(i,j,k)
The first example implies you are trying to build a sequence, while the second looks like an assignment.
Plus, it matches the syntax of existing languages (such as Python) which already support this feature.
- David
36. Re: Pass by Reference
- Posted by petelomax Jan 04, 2015
- 2865 views
I will always prefer
{i,j,k} = thing(i,j,k)
Why not
i,j,k = thing(i,j,k)
The first example implies you are trying to build a sequence, while the second looks like an assignment.
To be honest I can't really remember, but it was all thrashed out several years ago. I'll try me best:
Firstly, the {i,j,k} = <expr> syntax has already been implemented1. Given the way Eu is based on sequences, it somehow "fits". Otherwise, no real reason, except perhaps that
i,j,k = {1,2,3}
starts to get misleading, and then while
i,j,k = 1,2,3
seems better, it implies it would be OK to code
return 1,2,3
which is genuinely misleading. That absolutely has to return {1,2,3}, otherwise you've suddenly got issues with
if ... then return 1,2 else return 1,2,3
and, given there is only one eax/rax, subsequent implications for the system stack, call_back(), etc.
HTH,
Pete
1 Bear in mind I can only be authoritative about Phix, however I think it is also true for OpenEuphoria.
37. Re: Pass by Reference
- Posted by mattlewis (admin) Jan 04, 2015
- 2862 views
Firstly, the {i,j,k} = <expr> syntax has already been implemented1.
1 Bear in mind I can only be authoritative about Phix, however I think it is also true for OpenEuphoria.
Yes. This has been implemented for 4.1.
Given the way Eu is based on sequences, it somehow "fits". Otherwise, no real reason, except perhaps that
i,j,k = {1,2,3}
starts to get misleading
Yes, it simply seemed more euphoric, IIRC. Or maybe no one thought to have naked LHS sequences like that. It's been a couple of years at this point.
which is genuinely misleading. That absolutely has to return {1,2,3}, otherwise you've suddenly got issues with
if ... then return 1,2 else return 1,2,3
and, given there is only one eax/rax, subsequent implications for the system stack, call_back(), etc.
I'm sure that even if we'd gone with that syntax, we'd have to have turned it into a sequence. Probably better not to start removing stuff like that. Then the language starts to look like ruby, or something.
Matt
38. Re: Pass by Reference
- Posted by DerekParnell (admin) Jan 04, 2015
- 2850 views
Why not
i,j,k = thing(i,j,k)
The current syntax gives a significant visual clue to the person reading the code that the author is expecting to receive a set of values. The enclosing braces are superfluous in a strictly syntactical sense, but we must remember that source code is firstly designed for humans to comprehend rather than parsers. By using the brace device, it highlights to people the intention/expectation of the author. I acknowledge that using braces is almost arbitrary, as some other type of device could have been chosen, but it does feel Euphoria-like though.
By the way, I cannot see any argument based on "we should do it like XXX does it" has a lot of intrinsic merit. Needless to say, Euphoria's syntax should consistently be using the look & feel of Euphoria - I know that this is almost a nothing statement, but the Euphoria style is hard to define though easy to see.
39. Re: Pass by Reference
- Posted by ryanj Jan 05, 2015
- 2852 views
Personally, i have no problem with not having pass by reference. There are times when i would use it if it was available, but since it isn't, i just find other ways to do what i want. I actually like how Euphoria doesn't have lots of different ways of doing things with "syntax sugar", but has the power to emulate many things that other languages do, such as OOP for example. Even if it takes a little bit of extra typing, i like the fact that i am able to see exactly what the code is doing because it is all written out in sequences, slices, elements, enums, etc. I can clearly see when something is a reference to an index vs. an ID, and i can see exactly how the sequence stores everything, and i know nothing else is sneaking behind my back to do something.
As an example, in my Redy environment, i use pseudo-OOP all the time. I don't need to pass by reference, all the data is right there, in sequences. All i need to do is search for an ID to find the index of whatever object i want to access, and pass that idx to a routine if i want to. I can use enum to define valid values for certain types of data and i can create routines such as create and destroy and other routines to modify the object "properties" however i want to. I don't have to learn about polymorphism and stuff like that or follow tons of OOP rules and syntax to do whatever OOP kind of stuff i want to do. I'm not even sure how my code compares to how you would do things in an OOP languages, and i don't really care. I just follow some basic rules to access sequences with complete control and understanding of what is going on.
40. Re: Pass by Reference
- Posted by ryanj Jan 05, 2015
- 2816 views
Not that OOP necessarily has anything to do with pass by reference, i'm just saying that the way sequences currently work in Euphoria seem to eliminate the need for more complex concepts, and yet can emulate them in a simple syntax if needed rather than depending on a complex syntax design to provide them.
41. Re: Pass by Reference
- Posted by dcuny Jan 05, 2015
- 2923 views
By the way, I cannot see any argument based on "we should do it like XXX does it" has a lot of intrinsic merit. Needless to say, Euphoria's syntax should consistently be using the look & feel of Euphoria - I know that this is almost a nothing statement, but the Euphoria style is hard to define though easy to see.
The statement has value, but I usually start with the opposite question: why should I force other people to learn a new syntax? It's not that should never happen, but all things being equal, there should be a good reason for doing so.
I apologize for being "late to the party" on this, but consider the two options:
function foo() return 1, 2, 3, 4 end function a, b, c = foo()
vs.
function foo() return {1, 2, 3} end function {a, b, c, d} = foo()
Obviously, the first can be verified as incorrect by the parser.
The second can only be determined to be wrong at runtime, and will result in a crash.
Unlike indexes, there's no way to check for this error - you can't see how many values a function is returning. So the use of this syntax is intrinsically unsafe, and there's no way that I see to make it safe.
Am I missing something here?
- David
42. Re: Pass by Reference
- Posted by petelomax Jan 05, 2015
- 2811 views
Obviously, the first can be verified as incorrect by the parser.
The second can only be determined to be wrong at runtime, and will result in a crash.
It would probably not be difficult to collect min/max/unknown lengths of returned sequences, so as long as you don't build return values using append() etc, it should be just as easy to check (at parse time).
you can't see how many values a function is returning.
You can do this:
function foo() return {1, 2, 3} end function sequence check = foo() if length(check)>=4 then {a, b, c, d} = check end if
Pete
43. Re: Pass by Reference
- Posted by DerekParnell (admin) Jan 05, 2015
- 2803 views
The statement has value,
Of course, but only when seen in light of other things. It doesn't stand on its own; it has no intrinsic value. It needs supporting arguments.
but I usually start with the opposite question: why should I force other people to learn a new syntax?
So that every programming language is not the same. For instance, which syntax ought we base Euphoria on? There are lots of options - each with their own flavor of syntax.
It's not that should never happen, but all things being equal, there should be a good reason for doing so.
Yes! A good reason is why we should be doing anything. Both in following the syntax pattern of another language or when striking out in new ways.
I apologize for being "late to the party" on this, but consider the two options:
function foo() return 1, 2, 3, 4 end function a, b, c = foo()
vs.
function foo() return {1, 2, 3} end function {a, b, c, d} = foo()
Obviously, the first can be verified as incorrect by the parser.
The second can only be determined to be wrong at runtime, and will result in a crash.
Unlike indexes, there's no way to check for this error - you can't see how many values a function is returning. So the use of this syntax is intrinsically unsafe, and there's no way that I see to make it safe.
Am I missing something here?
Yes, you might be missing out on something here.
Firstly, it is not intrinsically (sorry - that word again) an error or mistake to ignore some returned function values. It is also not necessarily a mistake to present more variables for assignment than are needed. The parser cannot guess the intention of the code writer.
Euphoria's implementation does this ...
- When given more right-hand-side (RHS) values than there are LHS variables, the extra values are silently ignored.
- When given more LHS variables than there are RHS values, the extra variables are silently ignored.
- It allows omitted variables in the LHS set, which permits the coder to ignore selected returned values.
This implementation gives capabilities and responsibility to the coder. We are on our way to avoid treating coders as children, in need of protection from themselves.
And this brings us back to the original topic ... PBR, when used responsibly, is as safe as any other coding paradigm. So why not allow it in Euphoria?
On the plus side, PBR can make some coding simpler to write, and can improve the performance of some algorithms. It could also make the porting of code from other languages to Euphoria easier.
On the down side, PBR will make optimization harder to do and probably make some optimization choices impossible, thus as a side-effect, reducing the current performance of applications.
PBR is never necessary. The current pass-by-value can achieve the required results. Though sometimes this would require more code to be written and may also hamper performance of some things.
The most compelling reason to disallow it in Euphoria is it would transform the language into something that is no longer Euphoria. So that would be a judgment call - an matter of opinion.
Another point, is that the current implementation of Euphoria would have to have a massive and major rewrite due to all the pass-by-value assumptions built into the back-end and the generated IL code. So even if we agreed to allow PBR, it might be impractical to implement it now. Unless someone wants to start a new implementation from scratch.
44. Re: Pass by Reference
- Posted by Shian_Lee Jan 05, 2015
- 2753 views
Not that OOP necessarily has anything to do with pass by reference, i'm just saying that the way sequences currently work in Euphoria seem to eliminate the need for more complex concepts, and yet can emulate them in a simple syntax if needed rather than depending on a complex syntax design to provide them.
I could not say it better then ryanj.
Isn't it the BEST part of Euphoria?
45. Re: Pass by Reference
- Posted by Shian_Lee Jan 05, 2015
- 2766 views
Another point, is that the current implementation of Euphoria would have to have a massive and major rewrite due to all the pass-by-value assumptions built into the back-end and the generated IL code. So even if we agreed to allow PBR, it might be impractical to implement it now. Unless someone wants to start a new implementation from scratch.
This is really comforting!
That's the best news I've heard lately.
46. Re: Pass by Reference
- Posted by Steady Jan 05, 2015
- 2754 views
Not that OOP necessarily has anything to do with pass by reference,
I would have thought that OOP has a lot to do with pass by reference, because OOP is somewhat a result of larger and larger sets of data available as monolithic sequences or graphics or what-have-you. You are talking about Objects and Objects are easily referenced by pointers. The reasonable time efficient method of passing a variable for working on was to do a PBR and let the function access the data.
47. Re: Pass by Reference
- Posted by Steady Jan 05, 2015
- 2734 views
..... I usually start with the opposite question: why should I force other people to learn a new syntax? It's not that should never happen, but all things being equal, there should be a good reason for doing so.
I apologize for being "late to the party" on this, but consider the two options:
function foo() return 1, 2, 3, 4 end function a, b, c = foo()
vs.
function foo() return {1, 2, 3} end function {a, b, c, d} = foo()
Obviously, the first can be verified as incorrect by the parser.
The second can only be determined to be wrong at runtime, and will result in a crash.
Unlike indexes, there's no way to check for this error - you can't see how many values a function is returning. So the use of this syntax is intrinsically unsafe, and there's no way that I see to make it safe.
Am I missing something here?
- David
First of all Euphoria is promoted as a language that handles sequences. At the time of its original implementation there were very few languages addressing the problem of manipulating and working on sequences and Euphoria and APL were notable exceptions. So the sequence handling ability and emphasis of Euphoria should be emphasised to the newcomer so he knows he is undertaking an excursion into manipulating sequence and he chooses euphoria with open Eyes.
Then it becomes obvious that he should not be encouraged (in the examples he can access) or in the manual to
return 1, 2, 3, 4
and do instead
retvar = 1, 2, 3, 4 return retvar
The next statement or function that wants to use retvar will be forced to look at its size. (BTW retvar would be previously defined as a sequence that would eventually be available.) In any case somebody else has already answered David Cuny that Euphoria allows a mismatch in size of sequence or number of variables by dropping a value or a varname.
48. Re: Pass by Reference
- Posted by Spock Jan 05, 2015
- 2727 views
The most compelling reason to disallow it in Euphoria is it would transform the language into something that is no longer Euphoria. So that would be a judgment call - an matter of opinion.
No. The language could remain exactly the same, just the implementation change. If the parser analysed the program and determined PBR opportunites it could then reshape the appropriate calls. Perhaps even more radically, ref-counting could/should be dropped as well and certain memory allocations/deallocations done on the outside, by the caller.
Sure, it would require a massive rewrite but the performance gain could be similarly massive.
Oh. Too over the top?
Spock
49. Re: Pass by Reference
- Posted by dcuny Jan 07, 2015
- 2684 views
Euphoria's implementation does this ...
- When given more right-hand-side (RHS) values than there are LHS variables, the extra values are silently ignored.
- When given more LHS variables than there are RHS values, the extra variables are silently ignored.
- It allows omitted variables in the LHS set, which permits the coder to ignore selected returned values.
Sorry for the late response - I've been busy with other stuff.
Thanks, this clarifies a lot.
- David
50. Re: Pass by Reference
- Posted by jaygade Jan 08, 2015
- 2647 views
To be clear, Euphoria DOES pass by reference but it copies on write. So there is a performance advantage there.
As for manipulating large data structs, in Euphoria you currently have to implement them as file-level or global variables for efficiency.
One thought that I've had for implementing a new language is that functions should be pass by reference/copy on write, but procedures should be pass by reference/modify in place.
51. Re: Pass by Reference
- Posted by Spock Jan 08, 2015
- 2616 views
To be clear, Euphoria DOES pass by reference but it copies on write. So there is a performance advantage there.
As for manipulating large data structs, in Euphoria you currently have to implement them as file-level or global variables for efficiency.
One thought that I've had for implementing a new language is that functions should be pass by reference/copy on write, but procedures should be pass by reference/modify in place.
Question:
For every write (eg, x[i] = n) does the internal routine check to see if "x" has a refcount [and if it has make a fresh copy] before setting the data?
Spock
52. Re: Pass by Reference
- Posted by jimcbrown (admin) Jan 08, 2015
- 2587 views
To be clear, Euphoria DOES pass by reference but it copies on write. So there is a performance advantage there.
Agreed. Right now, this is just an implementation detail, done for speed. (Unless there are bugs) the effect is to emulate pass by value. It's functionally the same, but faster.
As far as the user is concerned, it's pass by value - just a faster pass by value.
But, since we are already doing pass by reference behind the scenes, how much harder would it be to do expore true pass by reference to the users of the language?
Question:
For every write (eg, x[i] = n) does the internal routine check to see if "x" has a refcount [and if it has make a fresh copy] before setting the data?
Spock
I believe so, yes. Fortunately, the check a very quick operation - read one int (the refcount) and only if it's not equal to one, then make a fresh copy.
53. Re: Pass by Reference
- Posted by Spock Jan 08, 2015
- 2631 views
Question:
For every write (eg, x[i] = n) does the internal routine check to see if "x" has a refcount [and if it has make a fresh copy] before setting the data?
Spock
I believe so, yes. Fortunately, the check a very quick operation - read one int (the refcount) and only if it's not equal to one, then make a fresh copy.
The check might be quick but it's still overhead [that could be eliminated by some radical thinking]. For operations such as, eg sorting, the performance is bound to suffer somewhat.
If there were no refcounts but there was a smarter parser lots of write operations could, I'm sure, be inlined. An extreme example would be x[i] = n resolving to a couple of machine operations.
But this would mean .. a massive rewrite.
Nah, forget I said anything.
Spock
54. Re: Pass by Reference
- Posted by Shian_Lee Jan 09, 2015
- 2552 views
But, since we are already doing pass by reference behind the scenes, how much harder would it be to do expore true pass by reference to the users of the language?
What for?
Performances are not the only consideration here. Readability, Maintainability, Simplicity, Minimalism, Reliability are also.
55. Re: Pass by Reference
- Posted by mattlewis (admin) Jan 09, 2015
- 2535 views
But this would mean .. a massive rewrite.
Yes, it would.
Matt
56. Re: Pass by Reference
- Posted by jimcbrown (admin) Jan 09, 2015
- 2576 views
But this would mean .. a massive rewrite.
Yes, it would.
Matt
Doesn't Phix already do something similar to this?
57. Re: Pass by Reference
- Posted by petelomax Jan 09, 2015
- 2554 views
Doesn't Phix already do something similar to this?
Yes, though quite a few bugs have been fixed in that area but never released, and the next one is still some months off.
When it is a local variable, since that can never be seen/referenced by the code being called,
local = somefunc(...,local,...)
the lhs gets set to <unassigned> over the call. Similar handling exists for
local[i][j] = somefunc(...,local[i][j],...)
though in that case the element gets set to 0, and there is also multiple assignment to consider.
There is a pointless benchmark/test case I have somewhere showing a 400-fold performance gain, though to be honest real-world gains are likely to be extremely rare, and tiny.
Pete
58. Re: Pass by Reference
- Posted by dcuny Jan 12, 2015
- 2494 views
It's taken some time to get back to my original code, and I'm starting to rewrite it to use functions to return multiple values.
It's verified my initial concern: the syntax is unsafe.
As a counter-example to the idea that a sequence is "more Euphoria-like", have a look at parameter lists. In theory, we could pass all the parameter lists as an implied sequence:
function foo() -- assign parameters to the arglist {p1, p2, p3} = arglist end function
But this isn't how Euphoria works. One of the reasons for this is because it's important to match the number of parameters in the function call.
The parser knows how many values are expected to be passed in, and if they don't match it's an error. The parser knows this because the syntax of a function call parameter list reflects the intent.
Similarly, if we want to support multiple parameters coming back from a function, the syntax should support the intent.
Further, it's not "simple" to have functions returning single values use one syntax, and functions returning multiple values and have a different syntax.
It's necessary, but it doesn't really reflect the intent.
As another counter-example example, consider the for loop:
for i = 1 to 10 do -- do stuff end for
Everything the for loop does could be done with a while structure. But because it's a common idiom, not only does Euphoria have it, but it doesn't even require you to declare the variable outside the loop.
So while Euphoria has a tendency towards minimalism, that doesn't require that there be no amenities in the language.
I also think the syntax in the function call should match the return statement, and not "silently discard" the "extra" values. Instead, it should explicitly specify the values to be dropped:
a,,c = foo()
Without this, the interpreter would have to guess what the intent of this is:
-- did the user forget that foo() returns multiple values? a = foo()
I'm not arguing that the new syntax be removed:
{a, b, c, ... } = ...
This is a very useful feature.
Behind the scenes this can be implemented as exactly the same functionality.
But by making the syntax explicit, the result is a lot safer, because the syntax reflects the intent, and catches bonehead errors.
- David
59. Re: Pass by Reference
- Posted by Ekhnat0n Jan 12, 2015
- 2447 views
Let me summarize the discussion:
Both passing by value AND by reference would be a valuable addition to Euphoria
why not write code named:
PassByVal(any code)
AND
PassByRef(any code). And again like my question in the strengths of Euphoria:
Why not ask Robert Craig about his opinion?
60. Re: Pass by Reference
- Posted by Steady Jan 12, 2015
- 2439 views
Probably you have come up with the most rational solution.
If one recognises the fact the issues is not availability of memory, but the time taken and PBR would be a great benefit in speed.
Why not let the program itself be defined as functionPBR() and functionPBL()
61. Re: Pass by Reference
- Posted by mattlewis (admin) Jan 12, 2015
- 2464 views
But this isn't how Euphoria works. One of the reasons for this is because it's important to match the number of parameters in the function call.
The parser knows how many values are expected to be passed in, and if they don't match it's an error. The parser knows this because the syntax of a function call parameter list reflects the intent.
Except that we do have optional parameters now, so there is some flexibility.
I also think the syntax in the function call should match the return statement, and not "silently discard" the "extra" values. Instead, it should explicitly specify the values to be dropped:
I'm a little confused by this stuff. You're using the "naked" syntax here, which we don't use. In our implemented syntax, there's a big hint, I think, that you're discarding stuff. IOW:
a = foo()
Nothing being discarded (unless the user does it explicitly later).
{a,?,c} = foo()
This time, b got discarded.
{a} = foo()
Everything after a is gone. We don't know how much by looking, but the braces are a hint that we're getting a sequence back and only keeping the first element (which could be all there is).
Without this, the interpreter would have to guess what the intent of this is:
That, I think, is a major drawback to your proposed "naked" syntax.
Behind the scenes this can be implemented as exactly the same functionality.
But by making the syntax explicit, the result is a lot safer, because the syntax reflects the intent, and catches bonehead errors.
Now I'm really confused. The naked stuff seems to make things implicit. It seems pretty explicit the way it is right now. I suspect I'm not getting what you're trying to say.
Matt
62. Re: Pass by Reference
- Posted by dcuny Jan 12, 2015
- 2437 views
Now I'm really confused. The naked stuff seems to make things implicit. It seems pretty explicit the way it is right now. I suspect I'm not getting what you're trying to say.
I think you're looking at it from the other direction. I'm referring to the return statement in the function, not the assignment. That is, if a function foo() is supposed to return 3 values, and I write:
s = foo()
instead of:
{s} = foo()
then the parser should return a compile error.
But as currently designed, there's no way the compiler can do this, because it doesn't know foo() is supposed to return multiple values.
I'm suggesting that in the same way a function will expect a certain number of parameters (and parameter counting is good), there should be a reciprocal feature if a function returns multiple values.
There's been no need for this in the past, because a function could only return a single value. In my mind, that's still the case, and we just have a nice way of "unwrapping" those parameters.
I'm suggesting making the number of values coming back from a function should be part of the language.
With the new syntax, it's not necessary, but I think it has value.
Did that make sense?
- David
63. Re: Pass by Reference
- Posted by mattlewis (admin) Jan 12, 2015
- 2440 views
Now I'm really confused. The naked stuff seems to make things implicit. It seems pretty explicit the way it is right now. I suspect I'm not getting what you're trying to say.
I think you're looking at it from the other direction. I'm referring to the return statement in the function, not the assignment.
Yes, I didn't even consider the return statement, since there were none in your examples.
I'm suggesting making the number of values coming back from a function should be part of the language.
With the new syntax, it's not necessary, but I think it has value.
Did that make sense?
Yep, I'm tracking now. Not convinced of the value of this yet, however...
Matt
64. Re: Pass by Reference
- Posted by dcuny Jan 12, 2015
- 2468 views
Yep, I'm tracking now. Not convinced of the value of this yet, however...
Sorry. I had some examples, but they got edited out.
Here's my logic: Euphoria still doesn't support functions passing back multiple values. A function can still only pass back a single value.
What's been added is a syntax that makes it much easier to unpack a sequence. And this is a good thing.
If I write:
function foo(integer a, integer b, integer c) return 12 end function ? foo()
and try to run it, this is the result I get:
<0236>:: 'foo' needs 3 arguments ? foo()The compiler is able to clearly identify an mismatch between the two.
Now consider the following:
function foo(sequence this, integer update_value) integer prior_value = this[1] this[1] = update_value return {this, prior_value} end function sequence this = {50} this = foo(this, 11) ? this
The intent of the function is to replace the prior value in the first position, and return the old value. Since it's emulating pass by reference, it also returns the sequence. However, there's a logic error, so this ends up storing the both the updated sequence and the prior value.
The problem is that the parser doesn't understand the intent. Only I (the developer) knows if the function intended to return a sequence, or a list of values.
This goes back to my argument that just because something can be done minimally, that doesn't mean it should be done that way.
Consider instead this (hypothetical) example:
function foo(sequence this, integer update_value) integer prior_value = this[1] this[1] = update_value return this, prior_value end function sequence this = {50} integer prior_value this = foo(this, 11) ? this
Unlike the prior declaration of foo(), it's clear by looking at the function that the routine is intended to return multiple values, and not a single sequence. The hypothetical parser could return an error:
<xxxx>:: 'foo' returns 2 values this = foo(this, 11)That's why I keep hammering on the word intent. Otherwise, the functions aren't really returning multiple values. We've just provided a means for Euphoria to emulate the behavior. But emulation isn't the same as actually doing it, and the job of "making it work" falls on the developer.
Another example: Euphoria doesn't need type checking. Sure, it helps the interpreter generate faster code. But it also stops you from passing bad datatypes to routines and variables. It's so common, we take it for granted.
There's also a symmetry to the use of the comma, in the same way that:
{a, b} = {b, a}
there's a strong mapping between the use of commas in the return and the assignment:
function foo() return 1, 2, 3 end function a, b, c = foo()
By adding this syntax to Euphoria, you're no longer emulating the behavior, and you get the full benefit: the parser can catch logical errors because it sees what was written didn't follow the intent.
It also (happily) happens to follow the syntax of other language (Lua, Python) which have functions that return multiple values.
- David
65. Re: Pass by Reference
- Posted by jimcbrown (admin) Jan 12, 2015
- 2461 views
Here's my logic: Euphoria still doesn't support functions passing back multiple values. A function can still only pass back a single value.
Agreed.
What's been added is a syntax that makes it much easier to unpack a sequence. And this is a good thing.
Preaching to the choir.
If I write:
function foo(integer a, integer b, integer c) return 12 end function ? foo()
and try to run it, this is the result I get:
<0236>:: 'foo' needs 3 arguments ? foo()The compiler is able to clearly identify an mismatch between the two.
Though you could have optional parameters like so:
function foo(integer a = 1, integer b = 2, integer c = 3) return 12 end function ? foo()
and try to run it, this is the result you get:
0
Unlike the prior declaration of foo(), it's clear by looking at the function that the routine is intended to return multiple values, and not a single sequence. The hypothetical parser could return an error:
<xxxx>:: 'foo' returns 2 values this = foo(this, 11)
Actually, it'd be easy enough to require, and have the parser enforce, when desequencing is used with a function, that the return statement of the function be of the form "return {a, b, ...}" where the curly braces are not optional. (So you wouldn't be able to desequence a function that had "return repeat(0, 10)" for example.) Then on the other side, count out the number of values that are being desequenced in the LHS, and require any values that are being skipped to be explicitly represented with a question mark. So you'd have to have "{this, ?} = foo(this, 11)" or else it's an error. That takes care of the logic errors from "{this} = foo(this, 11)" ...
But what of "this = foo(this, 11)" ? This is how that logic error could be detected: if the function is ever used once by a desequencing operation, the parser keeps track of that and at the end goes back and spits outs an error on "this = foo(this, 11)" if it sees "{that, ?} = foo(that, 12)" somewhere else. Only if the function is never used by a desequencing call do we then allow "this = foo(this, 11)" to be valid.
That's why I keep hammering on the word intent. Otherwise, the functions aren't really returning multiple values. We've just provided a means for Euphoria to emulate the behavior. But emulation isn't the same as actually doing it, and the job of "making it work" falls on the developer.
By adding this syntax to Euphoria, you're no longer emulating the behavior, and you get the full benefit: the parser can catch logical errors because it sees what was written didn't follow the intent.
You're right - it is easier to just have the developer specify intent and let the parser enforce it. But I think we can still keep it within desequencing:
function foo(sequence this, integer update_value) integer prior_value = this[1] this[1] = update_value return using desequencing with size 2 values {this, prior_value} -- we can come up with a better syntax than this end function object spare sequence this = {50} this = foo(this, 11) -- parser blows up here since we aren't using desequencing, but we stated that we intended the caller of this function must use it. {this} = foo(this, 11) -- parser blows up here since it can tell that there's a size mismatch {this, ?} = foo(this, 11) -- parser allows this {this, spare} = foo(this, 11) -- parser allows this
It also (happily) happens to follow the syntax of other language (Lua, Python) which have functions that return multiple values.
That's a really good point. Maybe we can just make the changes to the parser to allow multiple return values - so it looks the same as the other languages and the parser can check intent - but have it implemented behind-the-scenes by replacing it with the appropriate sequence creation and desequencing calls (once the parser has validated everything of course)...
66. Re: Pass by Reference
- Posted by dcuny Jan 13, 2015
- 2514 views
Maybe we can just make the changes to the parser to allow multiple return values - so it looks the same as the other languages and the parser can check intent - but have it implemented behind-the-scenes by replacing it with the appropriate sequence creation and desequencing calls (once the parser has validated everything of course)...
My thought exactly. No run-time cost, and the parser catches stupid mistakes I make.
Plus, the syntax for assignments looks consistent.
I believe there's some sort of pack and unpack
You might have noticed I've glossed over a couple of points:
- How does this behaves with routine_id? I assume that you can't determine this ahead of time, so you simply have to implement this as it currently is, and hope for the best... Unless someone's got a better idea.
- How do you declare the number of values returned? I assume the parser keeps track of the count, and complains if they don't balance.
- There's no way to declare a return type. Then again Euphoria doesn't currently declare a return type, which is an odd omission:
function foo(integer x) returns integer -- I'd expect Euphoria would do something like this return 20 end function
The main syntactic difference is that most languages appear to use the underscore {_} character rather than {?}.
- David
67. Re: Pass by Reference
- Posted by jimcbrown (admin) Jan 13, 2015
- 2418 views
How does this behaves with routine_id? I assume that you can't determine this ahead of time, so you simply have to implement this as it currently is, and hope for the best... Unless someone's got a better idea.
Agreed. If we are using desequencing behind the scenes, then getting the routine_id() of a function returning multiple values will just work, but you'd get a sequence back from call_func() and you'd have to manually desequence it.
How do you declare the number of values returned? I assume the parser keeps track of the count, and complains if they don't balance.
Works for me.
There's no way to declare a return type. Then again Euphoria doesn't currently declare a return type
Not sure why we need one....
The main syntactic difference is that most languages appear to use the underscore {_} character rather than {?}.
But _ is a valid variable name since 4.0.0
68. Re: Pass by Reference
- Posted by dcuny Jan 13, 2015
- 2388 views
But _ is a valid variable name since 4.0.0
Shows how long I've been out of the picture.
I'm just noting that {_} is the "don't care" value for other languages, that's all. It's not a request to change things (yet )
- David
69. Re: Pass by Reference
- Posted by ghaberek (admin) Jan 13, 2015
- 2385 views
You're right - it is easier to just have the developer specify intent and let the parser enforce it. But I think we can still keep it within desequencing:
function foo(sequence this, integer update_value) integer prior_value = this[1] this[1] = update_value return using desequencing with size 2 values {this, prior_value} -- we can come up with a better syntax than this end function object spare sequence this = {50} this = foo(this, 11) -- parser blows up here since we aren't using desequencing, but we stated that we intended the caller of this function must use it. {this} = foo(this, 11) -- parser blows up here since it can tell that there's a size mismatch {this, ?} = foo(this, 11) -- parser allows this {this, spare} = foo(this, 11) -- parser allows this
It seems like this might be a better syntax.
function foo(sequence this, integer update_value) integer prior_value = this[1] this[1] = update_value return (2){this, prior_value} end function
It can be read aloud as "return two values, this and prior_value."
-Greg
70. Re: Pass by Reference
- Posted by mattlewis (admin) Jan 13, 2015
- 2378 views
there's a strong mapping between the use of commas in the return and the assignment:
function foo() return 1, 2, 3 end function a, b, c = foo()
By adding this syntax to Euphoria, you're no longer emulating the behavior, and you get the full benefit: the parser can catch logical errors because it sees what was written didn't follow the intent.
I think I'd like there to be something else when returning like this to highlight that something different is happening. Maybe square brackets:
function foo() return [1, 2, 3] end function [a, b, c] = foo()
Some additional wrinkles. At first glance, I don't see an obvious way to use such a function in an expression or as an argument for another function. Obviously, returning a sequence means the user needs to know what's coming. This isn't necessarily a deal breaker. Though maybe this is where we add subscripting and slicing for literals / expressions:
function foo() return [1, 2, 3] end function ? foo()[1..2] -- { 1, 2 }
This is maybe a little confusing, since it sort of duplicates the existing de-sequence operator for LHS expressions.
Matt
71. Re: Pass by Reference
- Posted by petelomax Jan 13, 2015
- 2439 views
Hmmm. Just some random thoughts:
There is a limit to how far you can or should mollycoddle the developer, but let's not get into a fight over that.
Not all functions return a fixed number/length of results. Some, of course, return an integer on error and a sequence on success.
One thing about
s = foo() {x,y,z} = s
is that it can give extra debugging info.
If we say s = foo() generates an error, what about {s} = {foo()} and s = {fee(),foo()} and g(foo()) and return foo() and... ?
OpenEuphoria (wrongly, imo) also allows foo(), ie implicit discard of all results, without any warning. In Phix I use {} = foo() as the explicit discard.
Regarding extra decorations on function definitions, may I suggest:
function f() with multiple_returns
or
function f() with forced_desequence
or
return[2] {new,old} end function
or even
return[2..8] <result built with append etc> end function
any of which means there would be no extra errors or warnings for any legacy code, a definite plus point.
FYI, Phix (already) currently does this:
function f2() return {1,2} end function integer a -- a = f2() -- ^ type error (storing sequence in atom)
Needless to say, Phix already deduces function return types and therefore I am not as keen on explicit return types as some might be.
Were those thoughts random enough for you?
Pete
72. Re: Pass by Reference
- Posted by petelomax Jan 13, 2015
- 2360 views
It seems like this might be a better syntax.
function foo(sequence this, integer update_value) integer prior_value = this[1] this[1] = update_value return (2){this, prior_value} end function
It can be read aloud as "return two values, this and prior_value."
Unfortunately the (round brackets) make it unnecessarily hard, since return (2) is a valid statement (and {this,prior} could be the start of a multiple assignment).
Just after you posted I suggested return[2] or return[2..8] (which could be read as "return at least two and at most eight values"), but I'll accept you pipped me to it.
Pete
73. Re: Pass by Reference
- Posted by dcuny Jan 13, 2015
- 2346 views
I think I'd like there to be something else when returning like this to highlight that something different is happening. Maybe square brackets:
I disagree for a number of reasons. (Of course, that doesn't mean I'm right ).
First, there's no need for adding square brackets. The syntax is already distinct enough for humans and compilers to recognize. Language like Lua and Python already demonstrate that.
Second, it breaks the existing idiom. We already have:
x = foo()
Adding a comma extends the existing syntax:
x, y = foo()
Whereas using the square brackets forces the user to use a new syntax without any obvious value.
After all, if you wrote by mistake:
x = foo()
At compile time, the parser is going to throw an error like:
Error: Return count mismatch: foo() returns 2 values, but only assigns 1 x = foo()So it's not going to be a very subtle bug.
Third, it uses an existing feature of syntax in an unrelated way. The square brackets already have a semantic meaning in Euphoria. Now you're overloading them with a new, unrelated meaning.
Hrm... I wonder why Creole things this paragraph needs to be indented.
Anyhoo, I think I'm going to take back my statement about how call_func() should operate. Given that the parser doesn't let the code compile if there's a mismatch, and call_func() throws an exception if there's a parameter length mismatch, I think the parameter count match should be enforced at runtime - that is, the left and right sides should be equal.
- David
74. Re: Pass by Reference
- Posted by dcuny Jan 13, 2015
- 2353 views
Just after you posted I suggested return[2] or return[2..8] (which could be read as "return at least two and at most eight values"), but I'll accept you pipped me to it.
The number of values returned will be enforced by the compiler. That is, if you wrote:
function foo(integer x) if x = 0 then return 1, 2 else return 3 end if
You should get an error along the lines of:
Error: Return count mismatch. foo() expects 2 return values, not 1 return 3So the declaration of the number of values doesn't need to be declared, it just needs to be consistent.
If you want a variable number of parameters, the de-sequencing operation should be used instead. Yes, I know: Euphoria allows optional parameters.
But above all, I'd stay away from using the square brackets. This looks like an index:
return[2]
and this looks like a slice:
return[2..8]
- David
75. Re: Pass by Reference
- Posted by mattlewis (admin) Jan 13, 2015
- 2325 views
First, there's no need for adding square brackets. The syntax is already distinct enough for humans and compilers to recognize. Language like Lua and Python already demonstrate that.
I agree there's no need. Right now, it strikes me as a good idea because it's helping the person understand that something different is going on.
Second, it breaks the existing idiom. We already have:
x = foo()
Adding a comma extends the existing syntax:
x, y = foo()
Whereas using the square brackets forces the user to use a new syntax without any obvious value.
I'm seeing the square brackets as something more like the desequencing operation. And makes it more obvious to me that we're expecting multiple return values. Again, I like the emphasis. I'm not a fan of the current trend of (what seems to me) sneaky syntax that does a lot more than is obvious. This is a relatively mild example, but still.
Third, it uses an existing feature of syntax in an unrelated way. The square brackets already have a semantic meaning in Euphoria. Now you're overloading them with a new, unrelated meaning.
Yes, this is where I probably am closest to agreeing with you. I just wanted something different from sequence brackets. Parentheses were obvious non-starters.
Anyhoo, I think I'm going to take back my statement about how call_func() should operate. Given that the parser doesn't let the code compile if there's a mismatch, and call_func() throws an exception if there's a parameter length mismatch, I think the parameter count match should be enforced at runtime - that is, the left and right sides should be equal.
Yes, I don't see any other way. Well, in some cases we could detect the target function and deal with it right away, but we'd still need to do something at runtime when we can't.
Matt
76. Re: Pass by Reference
- Posted by dcuny Jan 13, 2015
- 2346 views
Well, we agree on the fundamentals... Now all I need to do is beat you into submission about the particulars.
I've used the multiple return value in other languages (including wxBasic), and I've never felt the need to add some sort of extra syntax around it.
No other languages that return multiple values seem to feel this is necessary.
Consider these examples:
-- same syntax, whether takes 0 or multiple parameters a = foo() a = sin(10) a = max(10, 20) function foo0() function foo1(integer a) function foo2(integer a, integer b) integer a integer a, b sequence s1 = {} sequence s2 = {a} sequence s3 = {a, b} {a} = s {a, b} = s
The addition of square brackets breaks this consistency:
return a return [a, b] a = foo1() [a, b] = foo2()
So that's the fundamental issues I've got. IMNSHO...
- It breaks established syntax
- It doesn't add any safety that the compiler doesn't already give
- It implies that there's some sort of special tuple object delimited by square braces
- It prevents using square brackets for some other special tuple in the future
Basically, if the language is going to make me do extra work (which the brackets do), I (Mr. Lazy Developer) expect to get some benefit for it.
Wasn't the prior point that that language shouldn't "mollycoddle" the developer? (Although, personally, I want my programming language to do as much heavy lifting as possible).
- David
77. Re: Pass by Reference
- Posted by mattlewis (admin) Jan 14, 2015
- 2319 views
Well, we agree on the fundamentals... Now all I need to do is beat you into submission about the particulars.
I've used the multiple return value in other languages (including wxBasic), and I've never felt the need to add some sort of extra syntax around it.
I've returned multiple values in euphoria, and I've never felt the need to not pass them as a sequence.
Seriously, though, putting my Raymond Chen hat on and starting this feature at -100 points, I'm not seeing the need for another way to pass and receive multiple values.
Wasn't the prior point that that language shouldn't "mollycoddle" the developer? (Although, personally, I want my programming language to do as much heavy lifting as possible).
I think someone was saying something like that, but I don't think it was me.
Matt
78. Re: Pass by Reference
- Posted by dcuny Jan 14, 2015
- 2296 views
I think someone was saying something like that, but I don't think it was me.
Sorry, I didn't mean to attribute it to you. I was just too lazy to look up who had written it.
As for starting at -100, I tried to address that with a prior post. We've got many things in Euphoria that we could (conceptually) do without, such as for loops, and (thanks for the addition) forward references.
While these can be done without adding new features, they make Euphoria a much more pleasant language to use because Euphoria keeps track of those details for you.
I despised having to use routine_id() and call_routine() to simulate forward references. Again, being able to simulate something is not the same as actually having the feature.
It continues to baffle me why anyone would want to do the work of the compiler. To me, "simple" doesn't mean "poor" - it means "elegant", "consistent" and "not difficult to use". A sequence is a wonderful thing, but having a hammer doesn't mean everything is a nail, even if it can be hit by a hammer.
- David
79. Re: Pass by Reference
- Posted by mattlewis (admin) Jan 14, 2015
- 2298 views
While these can be done without adding new features, they make Euphoria a much more pleasant language to use because Euphoria keeps track of those details for you.
Yeah. I'm just not feeling it yet for this particular thing. YMMV
Matt
80. Re: Pass by Reference
- Posted by useless_ Jan 14, 2015
- 2286 views
On this return thing, i agree with David's syntax using no brackets or parentheses, but using a comma. Matt's way using brackets makes sense only if you mentally apply a loopy self-referential way of looking at it.
[a, b] = foo(2)
foo(2)[{a},{b}] = foo(2)
or something like that. I could be wrong.
I suspect this is because of how one must look at the guts of Eu, while the rest of us look at the outside of Eu. But Matt, as the mere simple-minded users of the language, we humbly request the interface to the guts be how we keep asking it to be. (I am not joking, i would lose some part of me if i had to do what you do on the insides of Euphoria. We are just different people, doing different things differently.) This is basically so we can keep our minds on the application, and not how what we want must be expressed in the programming language. It's a bigger jump between human and programming language than between most human languages (at least to me). David's comma is merely glossing over the intricate details and presenting the way some of us inherently automatically would do it when our minds are on the application instead of the programming.
So, i request the comma way of writing the returned values. Also, not crashing when doing
a,b,c = return(1,2) ; a=1, b=2, c becomes some default value
would be nice too, after all, we can do it the other way 'round
procedure foo(sequence a, atom b, object c = 0)
useless
81. Re: Pass by Reference
- Posted by dcuny Jan 14, 2015
- 2248 views
So, i request the comma way of writing the returned values. Also, not crashing when doing
a,b,c = return(1,2) ; a=1, b=2, c becomes some default value
Actually, it shouldn't even compile.
Hrm... Obviously, this is a bit more work for forward references, too. Not a big deal, but not to be forgotten about.
Because the compiler ensures the counts match, the only situation where a crash should be possible would be:
a, b, c = call_function( routine_id("foo"), {})
I'd initially agreed that this shouldn't be an error, but I no longer think that's the case.
Mainly, I figure that if you're trying to read more values back than a routine returns, it's a pretty strong hint that perhaps something is wrong.
After all, the parser treats it as a fatal error.
You've still go the de-sequencing operator to fall back to if you want to handle multiple values. But my assumption is that a routine should return a fixed number of parameters.
If you're just trying to avoid having Euphoria throw an exception, I figure better exception handling is the path to take.
Could you give an example of where you think you might encounter this use case?
- David
82. Re: Pass by Reference
- Posted by useless_ Jan 14, 2015
- 2239 views
So, i request the comma way of writing the returned values. Also, not crashing when doing
a,b,c = return(1,2) ; a=1, b=2, c becomes some default value
Actually, it shouldn't even compile.
Well, that is true at the moment, yes.
Could you give an example of where you think you might encounter this use case?
- David
Just off the top of my head:
,v = value("1234")
(even defaulted_value() and get() can return a two-part sequence)
i,,, = int_to_bytes(999) -- i want just the lowest byte
useless
83. Re: Pass by Reference
- Posted by Spock Jan 14, 2015
- 2249 views
..
Because the compiler ensures the counts match, the only situation where a crash should be possible would be:
a, b, c = call_function( routine_id("foo"), {})
I'd initially agreed that this shouldn't be an error, but I no longer think that's the case.
Mainly, I figure that if you're trying to read more values back than a routine returns, it's a pretty strong hint that perhaps something is wrong.
After all, the parser treats it as a fatal error.
You've still go the de-sequencing operator to fall back to if you want to handle multiple values. But my assumption is that a routine should return a fixed number of parameters.
If you're just trying to avoid having Euphoria throw an exception, I figure better exception handling is the path to take.
Could you give an example of where you think you might encounter this use case?
- David
Just my 2c. If the requirement for multiple return values were driven largely by returning error/status codes then the suggestion (of mine in the Try/Catch thread) of using an implicit error variable would reduce a lot of this need. Therefore, that seems to simplify the issue to mere desequencing.
Spock
84. Re: Pass by Reference
- Posted by petelomax Jan 14, 2015
- 2205 views
But my assumption is that a routine should return a fixed number of parameters.
... RIGHT ... then you and I have very different views!
As a quick example, get_operand() can return:
- P_REG, size, 1..8|16
- P_VAR, size, N, type
- P_MEM, size, scale, index, base, offset
- P_LBL, lblidx
- P_IMM, atom
And you can pass (a combination of) those P_XXX as permitted types to get_operand(), so (sometimes) you know what you'll get back.
Admittedly, in practice, I use dummy sizes of 0, but also nested subsequences, for which your plain comma syntax could never work.
So, finally, it's a "noo, I'm out" from me.
Pete
PS: In Phix, you can say {a,b[1],{c[2..3],{d,?,e[4]}}} = f() no problem, if you want to. Pick a compile-time error out of that.
85. Re: Pass by Reference
- Posted by dcuny Jan 14, 2015
- 2241 views
- P_REG, size, 1..8|16
- P_VAR, size, N, type
- P_MEM, size, scale, index, base, offset
- P_LBL, lblidx
- P_IMM, atom
With disparate values, why would you want to use multiple returns here? Wouldn't this make more sense (ignoring keywords, of course):
sequence result = get_operand() switch result[1] do case P_REG then { op, size, flags } = result case P_VAR then { op, size, n, type } = result case P_MEM then { op, size, scale, index, base, offset } = result case P_LBL then { op, lblidx } = result case P_IMM then { op, atom } = result end case
When different kinds of things are returned, using a sequence and the de-sequence operation makes sense. I'm certainly not advocating getting rid of that.
PS: In Phix, you can say {a,b[1],{c[2..3],{d,?,e[4]}}} = f() no problem, if you want to. Pick a compile-time error out of that.
No compile time error to be had, if f() is returning a single value. No runtime error, either... Assuming f() is returning a sequence.
On the other hand, if you'd written:
a,b[1],{c[2..3],{d,?,e[4]}} = f()
and f() doesn't returns two values, you've got a compile time error.
- David
86. Re: Pass by Reference
- Posted by dcuny Jan 14, 2015
- 2194 views
Just my 2c. If the requirement for multiple return values were driven largely by returning error/status codes then the suggestion (of mine in the Try/Catch thread) of using an implicit error variable would reduce a lot of this need. Therefore, that seems to simplify the issue to mere desequencing.
Error flags aren't the primary driver.
But you're right in that it helps separate the error code from the return value.
That's only one example where it's nice to be able to return multiple values, instead of having to resort to globals.
Since Euphoria already supports this via the de-sequencing operator, we're mainly discussing (delicious) semantic sugar.
- David
87. Re: Pass by Reference
- Posted by petelomax Jan 15, 2015
- 2194 views
With disparate values, why would you want to use multiple returns here? Wouldn't this make more sense (ignoring keywords, of course):
sequence result = get_operand() switch result[1] do case P_REG then { op, size, flags } = result case P_VAR then { op, size, n, type } = result case P_MEM then { op, size, scale, index, base, offset } = result case P_LBL then { op, lblidx } = result case P_IMM then { op, atom } = result end case
Yes, but you can also, and indeed in places the current code actually does, do something like this
{ op, size, reg } = get_operand(P_REG)
or this
{ op, size, scale, index, base, offset } = get_operand(P_MEM)
In Phix, you get a fatal error if there aren't enough values, whereas OpenEuphoria just quietly leaves some values as-is, and on both, no messages if there are too many.
If anything, slightly tongue in cheek, it would be nice for
{ P_REG, size, reg, $ } = get_operand(P_REG) { P_MEM, size, scale, index, base, offset, $ } = get_operand(P_MEM)
to check, because P_REG/P_MEM are constants, that res[1] is the expected value and deliver a fatal runtime crash if not, and (from the ",$") check that the exact number of values was returned.
Pete
88. Re: Pass by Reference
- Posted by mattlewis (admin) Jan 15, 2015
- 2213 views
{ op, size, scale, index, base, offset } = get_operand(P_MEM)
In Phix, you get a fatal error if there aren't enough values, whereas OpenEuphoria just quietly leaves some values as-is, and on both, no messages if there are too many.
Not true (about openeuphoria) unless I'm misunderstanding you:
-- test.ex without inline function foo() return {1, 2, 3} end function object a, b, c, d {a, b, c, d } = foo()
$ eui test test.ex:10 subscript value 4 is out of bounds, reading from a sequence of length 3 test.ex:10 Segmentation fault (core dumped)
It's so much of an error that you get a segfault! Hmm....need to look into that.
Matt
89. Re: Pass by Reference
- Posted by dcuny Jan 15, 2015
- 2241 views
Just off the top of my head:
,v = value("1234")
(even defaulted_value() and get() can return a two-part sequence)
Sorry, I must not have been clear about this.
Similar to the de-sequencing operation, you don't need to use the value - you just need to have the count match.
So I would expect the following to be legal if value passed back two values:
?,v = value("1234")
But it doesn't - it returns a sequence. Changing how value() behaves at this point would break a lot of code, so I don't see an easy way to do this without the de-sequence operator.
What I'm asking for is a level of compile-time safety. Obviously, there's no way to know the number of parameters that are being passed back with a sequence until runtime. I assume that with 4.0 you are (supposed to) be able to handle this with:
{?,v} = value("1234")
Similarly:
i,,, = int_to_bytes(999)
should be valid as well... If int_to_bytes actually passed back two values.
The distinction I'm trying to make is that a function has the option of passing back multiple values. You'd only do this for the purpose of additional compile-time safety.
Or maybe I'm misunderstanding that you'd like the comma syntax with backward compatibility? For that, I'll go back to my initial point: you're still only returning a single value under the old system.
- David
90. Re: Pass by Reference
- Posted by petelomax Jan 15, 2015
- 2143 views
In Phix, you get a fatal error if there aren't enough values, whereas OpenEuphoria just quietly leaves some values as-is, and on both, no messages if there are too many.
Not true (about openeuphoria) unless I'm misunderstanding you:
Ah, I based that comment on:
Firstly, it is not intrinsically (sorry - that word again) an error or mistake to ignore some returned function values. It is also not necessarily a mistake to present more variables for assignment than are needed. The parser cannot guess the intention of the code writer.
Euphoria's implementation does this ...
- When given more right-hand-side (RHS) values than there are LHS variables, the extra values are silently ignored.
- When given more LHS variables than there are RHS values, the extra variables are silently ignored.
- It allows omitted variables in the LHS set, which permits the coder to ignore selected returned values.
This implementation gives capabilities and responsibility to the coder. We are on our way to avoid treating coders as children, in need of protection from themselves.
Pete
91. Re: Pass by Reference
- Posted by petelomax Jan 15, 2015
- 2151 views
What I'm asking for is a level of compile-time safety. Obviously, there's no way to know the number of parameters that are being passed back with a sequence until runtime.
The distinction I'm trying to make is that a function has the option of passing back multiple values. You'd only do this for the purpose of additional compile-time safety.
... I think this must have got lost in all the shouting:
The second can only be determined to be wrong at runtime, and will result in a crash.
It would probably not be difficult to collect min/max/unknown lengths of returned sequences, so as long as you don't build return values using append() etc, it should be just as easy to check (at parse time).
I took a couple of minutes to check, and in my own particular parlance:
- DoRoutineDef() has to initialise a few new static vars/flags
- DoReturn() has to check opstack for opMkSq,N or an all-literal-const-sequence and set those flags/lengths accordingly
- DoRoutineDef() has to save those vars/flags permanently in the symtab, and post-validate any prior fwd calls
- MultipleAssignment() checks opstack for a return var, and either
- logs the forward call for DoRoutineDef() to later process, or
- validates things there and then
As I see it, the only sticky points are
firstly that one = f() is just a plain (sequence) assignment and gets no validation whatsoever, but anything with the {one,...} = f() syntax gets checked, and
secondly that any return not of the return {...} syntax precludes any and all checks on calls-with-multiple-assignment to that particular routine.
Third and last, {} = f() does not trigger a mismatch, as it is an explicit discard.
Pete
92. Re: Pass by Reference
- Posted by dcuny Jan 15, 2015
- 2159 views
I'd only been considering the case
<MULTIPLE_ASSIGNMENT> = FUNCTION()
If FUNCTION() doesn't return multiple values, the compiler knows it's an error.
Of course, there's a nasty exception to this:
<MULTIPLE_ASSIGNMENT> = call_func()
This obviously can't be caught at compile time, so would need some sort of runtime check to make sure things are balanced.
Anything of the more general form:
<MULTIPLE_ASSIGNMENT> = <EXPRESSION>
is illegal at compile time, because there's no <EXPRESSION> that can return multiple values (yet).
Am I misunderstanding something here?
- David
93. Re: Pass by Reference
- Posted by petelomax Jan 15, 2015
- 2058 views
Am I misunderstanding something here?
You are assuming that return 1,2,3 and a,b,c=f() has been accepted while I am assuming it has been rejected. I still don't feel the benefits have been proved, or have been so vaguely described (or more often perhaps using you cannot when I think you can and therefore not counted) that I don't think I could even list them, at least not distinct from a still-{}-populated world. Admittedly my last post had a missing implied "if you adhere to this style, maybe we could have some useful compile-time messages" theme, sorry about that. Plus, the fact there would be two semantically different forms of multiple assignment, that might happen to be implemented identically under the hood, might just be a little too difficult for me. I've only just grasped the full extent of the call_func() issue and yes that gets horribly messy because it has to drag all this stuff out of the parser and right into the runtime/translator, for very little benefit.
Pete
94. Re: Pass by Reference
- Posted by dcuny Jan 16, 2015
- 2018 views
You are assuming that return 1,2,3 and a,b,c=f() has been accepted while I am assuming it has been rejected.
I'm only assuming they are still being discussed. Is there an official accept/reject committee I should know about?
I may sound impatient, but I know there are often compelling architectural reasons for not doing things. I'm trying to make sure people understand my motivations (not always easy, since we approach these things from different goals and backgrounds), and writing like this is really not an optimal way to do things.
Plus, the fact there would be two semantically different forms of multiple assignment, that might happen to be implemented identically under the hood, might just be a little too difficult for me.
I'm going to keep on being pedantic about not calling de-sequencing different from multiple assignments.
As you note, it also might be too soon to even start thinking about adding what I'm calling multiple assignments, given that there are likely to still be consequences and issues of de-sequencing that still being discovered.
I've only just grasped the full extent of the call_func() issue and yes that gets horribly messy because it has to drag all this stuff out of the parser and right into the runtime/translator, for very little benefit.
One disadvantage I've come from is that I've come from making two languages (Py and wxBasic) that both had this feature... But it was built into the both languages from the beginning, so there are a lot more hoops that need to be jumped through when you're adding things into an existing language.
And yes, call_func() adds quite a bit of wrinkles to things.
I'm going to give some thought into whether the goals I've been espousing can be accomplished in other ways.
- David
95. Re: Pass by Reference
- Posted by jimcbrown (admin) Jan 16, 2015
- 2035 views
You are assuming that return 1,2,3 and a,b,c=f() has been accepted while I am assuming it has been rejected.
I'm only assuming they are still being discussed. Is there an official accept/reject committee I should know about?
Actually, there is a dev team, but before outright rejecting anything we typically take a vote... (which hasn't happened yet afaik).
OTOH, features can still be discussed in the future, after a rejection... even if something is rejected the first time around, the additional discussion might lead to it being accepted the second time around.
I may sound impatient, but I know there are often compelling architectural reasons for not doing things. I'm trying to make sure people understand my motivations (not always easy, since we approach these things from different goals and backgrounds),
I'm glad you're making the effort (and I think you're quite good at it).
and writing like this is really not an optimal way to do things.
Erm, is there a better way to do it?
I still don't feel the benefits have been proved, or have been so vaguely described (or more often perhaps using you cannot when I think you can and therefore not counted) that I don't think I could even list them, at least not distinct from a still-{}-populated world. Admittedly my last post had a missing implied "if you adhere to this style, maybe we could have some useful compile-time messages" theme, sorry about that.
Well, dcuny was the one who pointed out that if we adhere to Lua-style multiple assignment, then we'd have some useful compile-time messages - so we'd fail faster (parser/compile time error instead of a run-time error or worst of all, a potentially hard to catch logic bug), and perhaps it'd be easier on the coder (less time required to scrutinize all those desequencing calls to make sure no one is doing anything silly).
I did point out that it's possible to get the same benefit in a different way, but, OTOH I find dcuny to be very persuasive...
Plus, the fact there would be two semantically different forms of multiple assignment, that might happen to be implemented identically under the hood, might just be a little too difficult for me.
I'm going to keep on being pedantic about not calling de-sequencing different from multiple assignments.
If it were added, multiple assignment would probably be implemented under the hood in OE this way - via desequencing - so the only difference would need to be at the parser level. There's still complications with call_func() perhaps, but see below..
I've only just grasped the full extent of the call_func() issue and yes that gets horribly messy because it has to drag all this stuff out of the parser and right into the runtime/translator, for very little benefit.
If desequencing simply mandated that the LHS and the RHS must have the same number of elements, or else it's a run-time error, then simply using desequencing with call_func() would be able to catch most of the errors that the parser's multiple assignment scanner would. Hmm.
96. Re: Pass by Reference
- Posted by petelomax Jan 16, 2015
- 2001 views
I did point out that it's possible to get the same benefit in a different way
ditto
I've only just grasped the full extent of the call_func() issue and yes that gets horribly messy because it has to drag all this stuff out of the parser and right into the runtime/translator, for very little benefit.
If desequencing simply mandated that the LHS and the RHS must have the same number of elements, or else it's a run-time error, then simply using desequencing with call_func() would be able to catch most of the errors that the parser's multiple assignment scanner would. Hmm.
If desequencing were just the same as multiple assignment then yes, but it's not.
I take it for granted that in desequencing (playing with sequences) this remains perfectly valid
p = {1,2,3} {i,j} = p
However, with multiple assignments (function results)
{i,j} = call_func(...) -- (oi, there be 3 values there, mate) p = call_func(...) -- (oi, where's me multiple assignment)
Then whether it is desequencing (as above) or the semantically different multiple assignment (function results) must be dynamically determined; whether the routine being invoked contains a return 1,2,3 or a return {1,2,3} has to be known at run-time to determine whether it is an error or not. That is the bit I am calling an awful lot of work for very little gain.
Pete
97. Re: Pass by Reference
- Posted by jimcbrown (admin) Jan 16, 2015
- 2068 views
I've only just grasped the full extent of the call_func() issue and yes that gets horribly messy because it has to drag all this stuff out of the parser and right into the runtime/translator, for very little benefit.
If desequencing simply mandated that the LHS and the RHS must have the same number of elements, or else it's a run-time error, then simply using desequencing with call_func() would be able to catch most of the errors that the parser's multiple assignment scanner would. Hmm.
If desequencing were just the same as multiple assignment then yes,
I guess this depends on the implementation.
I take it for granted that in desequencing (playing with sequences) this remains perfectly valid
p = {1,2,3} {i,j} = p
Let's take that for the sake of argument.
However, with multiple assignments (function results)
{i,j} = call_func(...) -- (oi, there be 3 values there, mate)
It's ugly (and I mean really ugly), but it's possible to make the desequencing operator aware that the value comes from a call to call_func(), and behave differently in that case. So then,
{i,j} = p -- ok, ignore the third {i,j} = call_func(...) -- run-time error function identity(object o) return o end function {i,j} = identity(call_func(...)) -- a trap! no run-time error!!!
p = call_func(...) -- (oi, where's me multiple assignment)
You got me. This one I don't know how to handle....
Then whether it is desequencing (as above) or the semantically different multiple assignment (function results) must be dynamically determined; whether the routine being invoked contains a return 1,2,3 or a return {1,2,3} has to be known at run-time to determine whether it is an error or not. That is the bit I am calling an awful lot of work for very little gain.
I hadn't even thought of this. I was thinking of ways to do it without having to dynamically determine the difference at run-time (by having it all turn into desequencing at run-time, and then adding some special cases to desequencing to make it look more like multiple assignment).
98. Re: Pass by Reference
- Posted by petelomax Jan 16, 2015
- 2151 views
and writing like this is really not an optimal way to do things.
Erm, is there a better way to do it?
It might be nice to have something a bit more like Rosetta Code whereby there's a main page that only a few people have the rights to update that summarises the proposal, examples, counter-proposals, objections, etc, with all the discussion (that anyone can join in) on a separate tab. Integrated into the forum, of course (ducks).
Pete
99. Re: Pass by Reference
- Posted by petelomax Jan 17, 2015
- 2150 views
-- test.ex without inline function foo() return {1, 2, 3} end function object a, b, c, d {a, b, c, d } = foo()
$ eui test test.ex:10 subscript value 4 is out of bounds, reading from a sequence of length 3 test.ex:10 Segmentation fault (core dumped)
It's so much of an error that you get a segfault! Hmm....need to look into that.
ticket #911 raised
Pete
100. Re: Pass by Reference
- Posted by newphil82 Jan 22, 2015
- 1979 views
I'm a comparative beginner. So, apologies; perhaps this is unhelpful and too elementary?? (If so, PLEASE delete it!) But recently I wanted a "beginner's" way of passing-by-reference, without doing anything dire.
Suppose I put some significant data into a sequence called, for example, 'datbank', and defined some constants, say THISWEEK = 1, NEXTWEEK = 2, ... whatever makes sense. I could pass by value as usual using, say, datbank[THISWEEK][details] as one of the params.
To pass by reference I'd just pass THISWEEK or NEXTWEEK as integers - and update datbank inside the procedure if required. A single procedure could then do similar updates for either week? - PF