1. Pass by Reference

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

new topic     » topic index » view message » categorize

2. Re: Pass by Reference

dcuny said...

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...

new topic     » goto parent     » topic index » view message » categorize

3. Re: Pass by Reference

dcuny said...

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 ) 
dcuny said...

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. smile

new topic     » goto parent     » topic index » view message » categorize

4. Re: Pass by Reference

dcuny said...

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

new topic     » goto parent     » topic index » view message » categorize

5. Re: Pass by Reference

jimcbrown said...

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

new topic     » goto parent     » topic index » view message » categorize

6. Re: Pass by Reference

dcuny said...

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.

new topic     » goto parent     » topic index » view message » categorize

7. Re: Pass by Reference

dcuny said...

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).

new topic     » goto parent     » topic index » view message » categorize

8. Re: Pass by Reference

jimcbrown said...

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.

new topic     » goto parent     » topic index » view message » categorize

9. Re: Pass by Reference

DerekParnell said...

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.

DerekParnell said...

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.

DerekParnell said...

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

new topic     » goto parent     » topic index » view message » categorize

10. Re: Pass by Reference

dcuny said...

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) 
dcuny said...

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?

DerekParnell said...

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.

dcuny said...

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.

new topic     » goto parent     » topic index » view message » categorize

11. Re: Pass by Reference

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.

new topic     » goto parent     » topic index » view message » categorize

12. Re: Pass by Reference

petelomax said...

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

new topic     » goto parent     » topic index » view message » categorize

13. Re: Pass by Reference

dcuny said...

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.)

dcuny said...

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.

dcuny said...

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?

dcuny said...

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.

new topic     » goto parent     » topic index » view message » categorize

14. Re: Pass by Reference

jaygade said...

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

new topic     » goto parent     » topic index » view message » categorize

15. Re: Pass by Reference

jimcbrown said...

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. blink

jimcbrown said...

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

new topic     » goto parent     » topic index » view message » categorize

16. Re: Pass by Reference

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

new topic     » goto parent     » topic index » view message » categorize

17. Re: Pass by Reference

dcuny said...

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

new topic     » goto parent     » topic index » view message » categorize

18. Re: Pass by Reference

_tom said...
dcuny said...

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.

new topic     » goto parent     » topic index » view message » categorize

19. Re: Pass by Reference

dcuny said...

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

new topic     » goto parent     » topic index » view message » categorize

20. Re: Pass by Reference

Steady said...

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). blink

- David

new topic     » goto parent     » topic index » view message » categorize

21. Re: Pass by Reference

dcuny said...
Steady said...

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). blink

- 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.

new topic     » goto parent     » topic index » view message » categorize

22. Re: Pass by Reference

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

new topic     » goto parent     » topic index » view message » categorize

23. Re: Pass by Reference

Yes, I believe that Ralf Peters is the person currently maintaining that version.

- David

new topic     » goto parent     » topic index » view message » categorize

24. Re: Pass by Reference

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

new topic     » goto parent     » topic index » view message » categorize

25. Re: Pass by Reference

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.

new topic     » goto parent     » topic index » view message » categorize

26. Re: Pass by Reference

Shian_Lee said...

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

new topic     » goto parent     » topic index » view message » categorize

27. Re: Pass by Reference

dcuny said...

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....

new topic     » goto parent     » topic index » view message » categorize

28. Re: Pass by Reference

dcuny said...
Shian_Lee said...

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.

new topic     » goto parent     » topic index » view message » categorize

29. Re: Pass by Reference

Shian_Lee said...

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.

new topic     » goto parent     » topic index » view message » categorize

30. Re: Pass by Reference

jimcbrown said...

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.

new topic     » goto parent     » topic index » view message » categorize

31. Re: Pass by Reference

Shian_Lee said...

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.)

new topic     » goto parent     » topic index » view message » categorize

32. Re: Pass by Reference

jimcbrown said...

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'.

new topic     » goto parent     » topic index » view message » categorize

33. Re: Pass by Reference

Shian_Lee said...

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:

jimcbrown said...

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.

new topic     » goto parent     » topic index » view message » categorize

34. Re: Pass by Reference

jimcbrown said...

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

new topic     » goto parent     » topic index » view message » categorize

35. Re: Pass by Reference

petelomax said...

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

new topic     » goto parent     » topic index » view message » categorize

36. Re: Pass by Reference

dcuny said...
petelomax said...

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.

new topic     » goto parent     » topic index » view message » categorize

37. Re: Pass by Reference

petelomax said...

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.

petelomax said...

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.

petelomax said...

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

new topic     » goto parent     » topic index » view message » categorize

38. Re: Pass by Reference

dcuny said...

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.

new topic     » goto parent     » topic index » view message » categorize

39. Re: Pass by Reference

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.

new topic     » goto parent     » topic index » view message » categorize

40. Re: Pass by Reference

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.

new topic     » goto parent     » topic index » view message » categorize

41. Re: Pass by Reference

DerekParnell said...

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

new topic     » goto parent     » topic index » view message » categorize

42. Re: Pass by Reference

dcuny said...

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).

dcuny said...

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

new topic     » goto parent     » topic index » view message » categorize

43. Re: Pass by Reference

dcuny said...

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.

dcuny said...

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.

dcuny said...

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.

dcuny said...

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.

new topic     » goto parent     » topic index » view message » categorize

44. Re: Pass by Reference

ryanj said...

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?

new topic     » goto parent     » topic index » view message » categorize

45. Re: Pass by Reference

DerekParnell said...

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.

new topic     » goto parent     » topic index » view message » categorize

46. Re: Pass by Reference

ryanj said...

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.

new topic     » goto parent     » topic index » view message » categorize

47. Re: Pass by Reference

dcuny said...

..... 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.

new topic     » goto parent     » topic index » view message » categorize

48. Re: Pass by Reference

DerekParnell said...

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

new topic     » goto parent     » topic index » view message » categorize

49. Re: Pass by Reference

DerekParnell said...

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

new topic     » goto parent     » topic index » view message » categorize

50. Re: Pass by Reference

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.

new topic     » goto parent     » topic index » view message » categorize

51. Re: Pass by Reference

jaygade said...

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

new topic     » goto parent     » topic index » view message » categorize

52. Re: Pass by Reference

jaygade said...

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?

Spock said...

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.

new topic     » goto parent     » topic index » view message » categorize

53. Re: Pass by Reference

jimcbrown said...
Spock said...

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

new topic     » goto parent     » topic index » view message » categorize

54. Re: Pass by Reference

jimcbrown said...

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.

new topic     » goto parent     » topic index » view message » categorize

55. Re: Pass by Reference

Spock said...

But this would mean .. a massive rewrite.

Yes, it would.

Matt

new topic     » goto parent     » topic index » view message » categorize

56. Re: Pass by Reference

mattlewis said...
Spock said...

But this would mean .. a massive rewrite.

Yes, it would.

Matt

Doesn't Phix already do something similar to this?

new topic     » goto parent     » topic index » view message » categorize

57. Re: Pass by Reference

jimcbrown said...

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

new topic     » goto parent     » topic index » view message » categorize

58. Re: Pass by Reference

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

new topic     » goto parent     » topic index » view message » categorize

59. Re: Pass by Reference

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?

new topic     » goto parent     » topic index » view message » categorize

60. Re: Pass by Reference

Ekhnat0n said...

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?

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()

new topic     » goto parent     » topic index » view message » categorize

61. Re: Pass by Reference

dcuny said...

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.

dcuny said...

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).

dcuny said...

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.

dcuny said...

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

new topic     » goto parent     » topic index » view message » categorize

62. Re: Pass by Reference

mattlewis said...

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

new topic     » goto parent     » topic index » view message » categorize

63. Re: Pass by Reference

dcuny said...
mattlewis said...

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. smile

dcuny said...

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

new topic     » goto parent     » topic index » view message » categorize

64. Re: Pass by Reference

mattlewis said...

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. smile

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

new topic     » goto parent     » topic index » view message » categorize

65. Re: Pass by Reference

dcuny said...

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.

dcuny said...

What's been added is a syntax that makes it much easier to unpack a sequence. And this is a good thing. smile

Preaching to the choir.

dcuny said...

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 

dcuny said...

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.

dcuny said...

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 
dcuny said...

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)...

new topic     » goto parent     » topic index » view message » categorize

66. Re: Pass by Reference

jimcbrown said...

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:

  1. 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.
  2. 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.
  3. 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

new topic     » goto parent     » topic index » view message » categorize

67. Re: Pass by Reference

dcuny said...

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.

dcuny said...

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.

dcuny said...

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....

dcuny said...

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

new topic     » goto parent     » topic index » view message » categorize

68. Re: Pass by Reference

jimcbrown said...

But _ is a valid variable name since 4.0.0

Shows how long I've been out of the picture. smile

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 blink)

- David

new topic     » goto parent     » topic index » view message » categorize

69. Re: Pass by Reference

jimcbrown said...

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

new topic     » goto parent     » topic index » view message » categorize

70. Re: Pass by Reference

dcuny said...

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

new topic     » goto parent     » topic index » view message » categorize

71. Re: Pass by Reference

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

new topic     » goto parent     » topic index » view message » categorize

72. Re: Pass by Reference

ghaberek said...

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

new topic     » goto parent     » topic index » view message » categorize

73. Re: Pass by Reference

mattlewis said...

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 blink).

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

new topic     » goto parent     » topic index » view message » categorize

74. Re: Pass by Reference

petelomax said...

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 3 
So 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

new topic     » goto parent     » topic index » view message » categorize

75. Re: Pass by Reference

dcuny said...

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.

dcuny said...

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.

dcuny said...

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.

dcuny said...

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

new topic     » goto parent     » topic index » view message » categorize

76. Re: Pass by Reference

Well, we agree on the fundamentals... Now all I need to do is beat you into submission about the particulars. blink

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

new topic     » goto parent     » topic index » view message » categorize

77. Re: Pass by Reference

dcuny said...

Well, we agree on the fundamentals... Now all I need to do is beat you into submission about the particulars. blink

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. tongue

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.

dcuny said...

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

new topic     » goto parent     » topic index » view message » categorize

78. Re: Pass by Reference

mattlewis said...

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. smile

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

new topic     » goto parent     » topic index » view message » categorize

79. Re: Pass by Reference

dcuny said...

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

new topic     » goto parent     » topic index » view message » categorize

80. Re: Pass by Reference

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

new topic     » goto parent     » topic index » view message » categorize

81. Re: Pass by Reference

useless_ said...

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

new topic     » goto parent     » topic index » view message » categorize

82. Re: Pass by Reference

dcuny said...
useless_ said...

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.

dcuny said...

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

new topic     » goto parent     » topic index » view message » categorize

83. Re: Pass by Reference

dcuny said...

..

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

new topic     » goto parent     » topic index » view message » categorize

84. Re: Pass by Reference

dcuny said...

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.

new topic     » goto parent     » topic index » view message » categorize

85. Re: Pass by Reference

petelomax said...
  • 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.

petelomax said...

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

new topic     » goto parent     » topic index » view message » categorize

86. Re: Pass by Reference

Spock said...

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

new topic     » goto parent     » topic index » view message » categorize

87. Re: Pass by Reference

dcuny said...

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

new topic     » goto parent     » topic index » view message » categorize

88. Re: Pass by Reference

petelomax said...
{ 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

new topic     » goto parent     » topic index » view message » categorize

89. Re: Pass by Reference

useless_ said...

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

new topic     » goto parent     » topic index » view message » categorize

90. Re: Pass by Reference

mattlewis said...
petelomax said...

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:

DerekParnell said...

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

new topic     » goto parent     » topic index » view message » categorize

91. Re: Pass by Reference

dcuny said...

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:

petelomax said...
dcuny said...

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

new topic     » goto parent     » topic index » view message » categorize

92. Re: Pass by Reference

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

new topic     » goto parent     » topic index » view message » categorize

93. Re: Pass by Reference

dcuny said...

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

new topic     » goto parent     » topic index » view message » categorize

94. Re: Pass by Reference

petelomax said...

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? blink

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.

petelomax said...

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.

said...

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

new topic     » goto parent     » topic index » view message » categorize

95. Re: Pass by Reference

dcuny said...
petelomax said...

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? blink

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.

dcuny said...

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).

dcuny said...

and writing like this is really not an optimal way to do things.

Erm, is there a better way to do it?

petelomax said...

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...

dcuny said...
petelomax said...

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..

petelomax said...

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.

new topic     » goto parent     » topic index » view message » categorize

96. Re: Pass by Reference

jimcbrown said...

I did point out that it's possible to get the same benefit in a different way

ditto

jimcbrown said...
petelomax said...

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

new topic     » goto parent     » topic index » view message » categorize

97. Re: Pass by Reference

petelomax said...
jimcbrown said...
petelomax said...

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.

petelomax said...

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.

petelomax said...

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!!! 
petelomax said...
p = call_func(...)      -- (oi, where's me multiple assignment) 

You got me. This one I don't know how to handle....

petelomax said...

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).

new topic     » goto parent     » topic index » view message » categorize

98. Re: Pass by Reference

jimcbrown said...
dcuny said...

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

new topic     » goto parent     » topic index » view message » categorize

99. Re: Pass by Reference

mattlewis said...
-- 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

new topic     » goto parent     » topic index » view message » categorize

100. Re: Pass by Reference

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

new topic     » goto parent     » topic index » view message » categorize

Search



Quick Links

User menu

Not signed in.

Misc Menu