1. Discuss changing the behaviour of append()

Currently this is illegal ...

sequence x 
 
x = append(1,2) 

We get the error message ...

first argument of append must be a sequence 
Would it be a bad thing to remove this restriction such that when appending to an atom, Euphoria would automatically build a new sequence? This would be similar to how concatenation works.

sequence x 
 
x = 1 & 2 -- No error. Creates {1,2} 

In my current code, I ran across this case and had to code an explicit test to get around it. This is 'ugly' and makes the code a bit slower and larger.

function F(object A, object B) 
sequence x 
    if atom(A) then 
        x = append({A}, B) 
    else 
        x = append(A, B) 
    end if 
    return x 
end function 
new topic     » topic index » view message » categorize

2. Re: Discuss changing the behaviour of append()

DerekParnell said...

Currently this is illegal ...

sequence x 
 
x = append(1,2) 

We get the error message ...

first argument of append must be a sequence 
Would it be a bad thing to remove this restriction such that when appending to an atom, Euphoria would automatically build a new sequence? This would be similar to how concatenation works.

Agreed. Let's do this.

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

3. Re: Discuss changing the behaviour of append()

This seems like an innocuous and intuitive change. You have my vote.

-Greg

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

4. Re: Discuss changing the behaviour of append()

A similar unnecessary restriction applies to prepend

Arthur

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

5. Re: Discuss changing the behaviour of append()

DerekParnell said...

Would it be a bad thing to remove this restriction such that when appending to an atom, Euphoria would automatically build a new sequence? This would be similar to how concatenation works.

Simple answer: not such a good idea.

The function append is:

   fn(seq, obj) -> seq 

The proposed change would require that append be

   fn(obj, obj) -> seq 
which makes the test part of append whether you want it or not.

It would seem that append is wanted most often when the first parameter is a sequence....

As to the code being 'ugly' ..

function F(object A, object B) 
sequence x 
    if atom(A) then 
        x = append({A}, B) 
    else 
        x = append(A, B) 
    end if 
    return x 
end function 

try

function F(object A, object B) 
    if atom(A) then 
        A = {A] 
    end if 
    return append(A, B) 
end function 

or, ditching the function

sequence x 
if atom(A) then 
    x = append({A},B) 
else 
    x = append(A,B) 
end if    
new topic     » goto parent     » topic index » view message » categorize

6. Re: Discuss changing the behaviour of append()

gimlet said...

The proposed change would require that append be

   fn(obj, obj) -> seq 
which makes the test part of append whether you want it or not.

The test is already part of append, and it triggers the error message "first argument of append must be a sequence". Instead of the error, the proposed change will convert the atom to a sequence first and proceed.

I like it.

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

7. Re: Discuss changing the behaviour of append()

oops, double post

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

8. Re: Discuss changing the behaviour of append()

Well - that is interesting,

You pass a sequence to append,
Append checks that it got a sequence.
Double work.

It seems to me that append is entitled to crash if you pass in an atom. That it actually checks before dying...

Leaving aside the convenience of append(1,2) as opposed to {1,2} or 1 & 2.

Isn't it true that if you don't know X is a sequence you shouldn't be appending to it?
Isn't this what types are about?

Isn't this to say that it is up to the caller to ensure that the parameters are of the correct type.

and

It is not up to the called function to validate its parameters.

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

9. Re: Discuss changing the behaviour of append()

gimlet said...

Well - that is interesting,

You pass a sequence to append,
Append checks that it got a sequence.
Double work.

IIUC this is only the case using the interpreter. The translator does not seem to check.

gimlet said...

It seems to me that append is entitled to crash if you pass in an atom. That it actually checks before dying...

Well, in general having a human readable error message is quite helpful. But for this to happen (and this generally doesn't happen with the translator btw), something needs to check somewhere.

gimlet said...

Leaving aside the convenience of append(1,2) as opposed to {1,2} or 1 & 2.

Isn't it true that if you don't know X is a sequence you shouldn't be appending to it?
Isn't this what types are about?

Isn't this to say that it is up to the caller to ensure that the parameters are of the correct type.

and

It is not up to the called function to validate its parameters.

Agreed, the language itself should do that validation. Which is what actually happens.

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

10. Re: Discuss changing the behaviour of append()

Jim,

If it is the case the translator doesn't check then it would make sense not to change append's behaviour.

Append is surely used > 99.9% in situations where a sequence is passed as parameter.

To account for the < 0.1% of cases where an atom is passed by changing the function does not make sense.

Every use of the function takes an (admittedly small) hit to allow an atom to be passed as if it were a sequence. There are probably more than a few functions which could benefit from this and a few more which could benefit from allowing sequences to be passed as atoms.

Probably a lot of these implicit conversions are stupid or unsafe.

In the long run it's your language and you can do with it as you like.

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

11. Re: Discuss changing the behaviour of append()

I believe among new users or infrequent users, if a user passes an atom as a first argument to append, it is probably because they are accidentally reversing the order of the parameters and not because they want to treat atoms as length-one sequences that contain that atom.

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

12. Re: Discuss changing the behaviour of append()

gimlet said...

Every use of the function takes an (admittedly small) hit to allow an atom to be passed as if it were a sequence. There are probably more than a few functions which could benefit from this and a few more which could benefit from allowing sequences to be passed as atoms.

If it is the case the translator doesn't check then it would make sense not to change append's behaviour.

Why? If it's for performance reasons then I'd disagree - usability comes first.

gimlet said...

Append is surely used > 99.9% in situations where a sequence is passed as parameter.

Right now it's 100% (not including errors).

gimlet said...

To account for the < 0.1% of cases where an atom is passed by changing the function does not make sense.

Sure it does! At least, I think there had better be a good reason if we're going to exclude a minority.

gimlet said...

Probably a lot of these implicit conversions are stupid or unsafe.

I don't follow.

gimlet said...

In the long run it's your language and you can do with it as you like.

Well, not mine personally. It's a community and a group decision. But, well, yeah...

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

13. Re: Discuss changing the behaviour of append()

SDPringle said...

I believe among new users or infrequent users, if a user passes an atom as a first argument to append, it is probably because they are accidentally reversing the order of the parameters and not because they want to treat atoms as length-one sequences that contain that atom.

Um, wow.

That's a pretty good argument.

It's better to fail early than introduce a logic error which may not be caught until much much later.

If you are right, then users could be confused as to why append is starting to act like prepend.

That said, I still think this is unlikely to happen. It'd be inconsistent with the order of parameters in the basic math operators (e.g. subtraction and division) and concatenation.

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

14. Re: Discuss changing the behaviour of append()

gimlet said...

Isn't it true that if you don't know X is a sequence you shouldn't be appending to it?

The situation that I needed it was when I was working with elements of a sequence. In other words, I was working with objects and I don't know if they are sequences or atoms unless I specifically check for it.

Ok, let's change the approach. If Euphoria didn't have an append function, and one was being proposed, why would the first parameter necessarily have to be a sequence? In terms of functionality, the difference between append and concatenate, is that append joins the second parameter as ONE object to the first parameter, and concatenate joins the second parameter as a set of zero or more objects to the first parameter. Why must append have a sequence as the first parameter and concatenate doesn't?

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

15. Re: Discuss changing the behaviour of append()

SDPringle said...

I believe among new users or infrequent users, if a user passes an atom as a first argument to append, it is probably because they are accidentally reversing the order of the parameters and not because they want to treat atoms as length-one sequences that contain that atom.

I was just going to say something similar, in particular str=prepend(ch,str) has been nicely caught for me a few times, but to be honest if I know I'm adding an atom, I'll use &, and if I'm adding a sequence, the extra type check helps not one tiny bit.

However, I would like to suggest appenda() [and prependa], see what people have to say about that. Or maybe appendany()/prependany() if you prefer.

Pete

Edit: "Why must append have a sequence as the first parameter and concatenate doesn't?"
That certainly hits the nail on the head. Apart from "trying to be helpful but not really managing it", I got no answer to that.

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

16. Re: Discuss changing the behaviour of append()

SDPringle said...

I believe among new users or infrequent users, if a user passes an atom as a first argument to append, it is probably because they are accidentally reversing the order of the parameters and not because they want to treat atoms as length-one sequences that contain that atom.

Sometimes, one just has to learn the API. For example, ...

   -- Ensure that the output has a new-line added. 
   puts(NEWLINE, STDOUT) 

Oops! Parameters are the wrong way around.

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

17. Re: Discuss changing the behaviour of append()

Derek,

I was aware of the (inconsistent?) behaviour of one element sequences. You can have append do what you want by calling append as:

     append(S[1..1], 2) -- coercing S[1] to a sequence 

Some clarification.

sequence S = {0,1,2,3} 
 
? length(S[1..0])     -- 0 
? sequence(S[1..0])   -- true 
? atom(S[1..0])       -- false 
 
? length(S[1..1])     -- 1 
? sequence(S[1..1])   -- true (slice coerces sequence type) 
? atom(S[1..1])       -- false 
 
? length(S[1])        -- 1 (odd!) 
? sequence(S[1])      -- false (odd!) 
? atom(S[1])          -- true 
 
? append(S[1..1],2)   -- {0,2} 
? append(S[1],2)      -- error 
new topic     » goto parent     » topic index » view message » categorize

18. Re: Discuss changing the behaviour of append()

gimlet said...

You can have append do what you want by calling append as:

     append(S[1..1], 2) -- coercing S[1] to a sequence 

While more 'elegant', it does have a performance aspect that I was also hoping to avoid.

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

19. Re: Discuss changing the behaviour of append()

I think the basic problem is that S[i] is not (fully) a sequence.

Every slice is a sequence,
S[i] has a length so it is a sequence,
Yet it can type as an atom.

It seems what you need here is an operator which 'converts' an atom to a sequence.

S[i] & {} -- would work as well as  
S[i..i]   -- but they are fixes 

but perhaps we need an explicit operator which minimises the cost of the operation.

I really don't think putting the burden on append is appropriate as you would need to do the same with other functions (prepend ..)
and
append has the right to expect a sequence as parameter
(otherwise why specify types at all?)

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

20. Re: Discuss changing the behaviour of append()

gimlet said...

S[i] has a length so it is a sequence,
Yet it can type as an atom.

Not so. length() simply returns 1 if the object is not a sequence. This change was made for the same reason we are discussing changing append()/prepend() - so to not fix them as well would be to leave the language in an inconsistent state.

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

21. Re: Discuss changing the behaviour of append()

DerekParnell said...

Currently this is illegal ...

sequence x 
 
x = append(1,2) 
sequence x 
 
x = 1 & 2 -- No error. Creates {1,2} 

Why not just use & then? What's the advantage of append(), anyway? I don't think i ever use it.

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

22. Re: Discuss changing the behaviour of append()

ryanj said...

Why not just use & then? What's the advantage of append(), anyway? I don't think i ever use it.

a = {1,2} & {3,4} 
-- and 
b = append({1,2},{3,4}) 

give different results:

a is {1,2,3,4}

b is {1,2,{3,4}}

Arthur

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

23. Re: Discuss changing the behaviour of append()

ArthurCrump said...

A similar unnecessary restriction applies to prepend

Arthur

To suggest the same for prepend was not such a good idea.

There is unlikely to be any confusion appending a new object to an atom, but prepend has its operands in reverse order so that

x = prepend(1,2) 

would give x={2,1} if it was allowed.
That could be confusing

Arthur

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

24. Re: Discuss changing the behaviour of append()

ArthurCrump said...
ryanj said...

Why not just use & then? What's the advantage of append(), anyway? I don't think i ever use it.

a = {1,2} & {3,4} 
-- and 
b = append({1,2},{3,4}) 

give different results:

a is {1,2,3,4}

b is {1,2,{3,4}}

Arthur

But you can easily simulate the behavior of append like this:

a = {1,2} & {{3,4}} 
-- and 
b = append({1,2},{3,4}) 

It's a good way to make up for the lack of an append= operator:

a &= {{3,4}} 
-- and 
b = append(b,{3,4}) 
new topic     » goto parent     » topic index » view message » categorize

25. Re: Discuss changing the behaviour of append()

ArthurCrump said...
ArthurCrump said...

A similar unnecessary restriction applies to prepend

Arthur

To suggest the same for prepend was not such a good idea.

There is unlikely to be any confusion appending a new object to an atom,

flwblink

ArthurCrump said...

but prepend has its operands in reverse order

Erm, let's just agree that the arguments to prepend are in the same order as append, but of course the result is (sort of) the other way round

ArthurCrump said...

so that

x = prepend(1,2) 

would give x={2,1} if it was allowed.
That could be confusing

Hmm. You lost me there. Why exactly is that any more confusing than

x = prepend({1},2) 

giving x={2,1}?

Pete

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

26. Re: Discuss changing the behaviour of append()

I think I've just realised where this is all going.

Given that length(atom) now returns 1, and I never thought I'd say this, but, it is only logical that atom[1] becomes valid.

Bit of a shocker, I agree, but there it is.

Pete

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

27. Re: Discuss changing the behaviour of append()

gimlet said...

I think the basic problem is that S[i] is not (fully) a sequence.

Every slice is a sequence,
S[i] has a length so it is a sequence,
Yet it can type as an atom.

It seems what you need here is an operator which 'converts' an atom to a sequence.

S[i] & {} -- would work as well as  
S[i..i]   -- but they are fixes 

Unfortunately, this is NOT the issue. I'll restate the problem using different words.

Given an object X, I want to create a sequence by adding another object Y, such that the last item in the newly created sequence is Y, and all the other items in X are unaltered.

Examples ...

X = 4      Y = {8,9}  --Result is {4, {8,9}} 
X = {4}    Y = {8,9}  --Result is {4, {8,9}} 
X = {4,5}  Y = {8,9}  --Result is {4, 5, {8,9}} 
X = {}     Y = {8,9}  --Result is {{8,9}} 

In all but the first case, the append() function will suffice. But with the first case I need to convert X to a sequence first. That is what I'm hoping to avoid doing; the 'manually' conversion. Instead I'd like Euphoria to do it automatically.

The only suggestion so far that works in each of the case above is the 'concatenate a sequence' trick, but that has a performance hit of creating a temporary sequence and then disposing of it after the operation has completed. Something I'd also like to avoid.

object X, Y 
X = 4      Y = {8,9}  X &= {Y} --Result is {4, {8,9}} 
X = {4}    Y = {8,9}  X &= {Y} --Result is {4, {8,9}} 
X = {4,5}  Y = {8,9}  X &= {Y} --Result is {4, 5, {8,9}} 
X = {}     Y = {8,9}  X &= {Y} --Result is {{8,9}} 

So, if the change is made to Euphoria's append()/prepend() functions to do this, programs that now currently fail in this situation will no longer fail. Is that okay?

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

28. Re: Discuss changing the behaviour of append()

DerekParnell said...
object X, Y 
X = 4      Y = {8,9}  X &= {Y} --Result is {4, {8,9}} 
X = {4}    Y = {8,9}  X &= {Y} --Result is {4, {8,9}} 
X = {4,5}  Y = {8,9}  X &= {Y} --Result is {4, 5, {8,9}} 
X = {}     Y = {8,9}  X &= {Y} --Result is {{8,9}} 

So, if the change is made to Euphoria's append()/prepend() functions to do this, programs that now currently fail in this situation will no longer fail. Is that okay?

This seems like a positive change.

Matt

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

29. Re: Discuss changing the behaviour of append()

DerekParnell said...
object X, Y 
X = 4      Y = {8,9}  X &= {Y} --Result is {4, {8,9}} 
X = {4}    Y = {8,9}  X &= {Y} --Result is {4, {8,9}} 
X = {4,5}  Y = {8,9}  X &= {Y} --Result is {4, 5, {8,9}} 
X = {}     Y = {8,9}  X &= {Y} --Result is {{8,9}} 

So, if the change is made to Euphoria's append()/prepend() functions to do this, programs that now currently fail in this situation will no longer fail. Is that okay?

These append()/prepend() are for operations with sequences (in pure 20 years old EU).
So, if you do not have some sequence to operate with, just do not use append()/prepend().

Use & and be happy :)
Regards


kinz

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

30. Re: Discuss changing the behaviour of append()

(Edit)

Nevermind -- I see my error.

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

31. Re: Discuss changing the behaviour of append()

jaygade said...
mattlewis said...
DerekParnell said...
object X, Y 
X = 4      Y = {8,9}  X &= {Y} --Result is {4, {8,9}} 
X = {4}    Y = {8,9}  X &= {Y} --Result is {4, {8,9}} 
X = {4,5}  Y = {8,9}  X &= {Y} --Result is {4, 5, {8,9}} 
X = {}     Y = {8,9}  X &= {Y} --Result is {{8,9}} 

So, if the change is made to Euphoria's append()/prepend() functions to do this, programs that now currently fail in this situation will no longer fail. Is that okay?

This seems like a positive change.

Matt

Maybe I'm not following very closely and misunderstanding. How in the first case would you concatenate to make a single-level sequence?

object X, Y 
X = 4      Y = {8,9}  -- what operation do I need to form {4, 8, 9}? 
X = {4, 5} Y = {8,9}  -- what operation do I need to form {4, 5, 8, 9}? 
X = "foo"  Y = "bar"  -- what operation do I need to form "foobar"? 

It's like this:

object X, Y 
X = 4      Y = {8,9}  X &= Y --Result is {4, 8, 9} 
X = 4      Y = {8,9}  X &= {Y} --Result is {4, {8,9}} 
X = {4}    Y = {8,9}  X &= Y --Result is {4, 8, 9} 
X = {4}    Y = {8,9}  X &= {Y} --Result is {4, {8,9}} 
X = {4,5}  Y = {8,9}  X &= Y --Result is {4, 5, 8, 9} 
X = {4,5}  Y = {8,9}  X &= {Y} --Result is {4, 5, {8,9}} 
X = "foo"  Y = "bar"  X &= Y --Result is "foobar" 
X = "foo"  Y = "bar"  X &= {Y} --Result is NOT "foobar" 

Basically, if you combine the concatenation operator with the new sequence operation, you've got a more expensive version of append.

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

32. Re: Discuss changing the behaviour of append()

I had to re-read the discussion. I thought that Derek was proposing a change to the '&' operator. I missed the extra set of braces.

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

33. Re: Discuss changing the behaviour of append()

I don't have any horses in this race, but that has never stopped me from cheering.

Basically Derek states he has a need in some code he is writing to append an object to another object and that the append function currently is designed as a sequence operator requiring a sequence as the first parameter (from "4.1.4.9 Other Operations on Sequences" in the manual).

DerekParnell said...

Currently this is illegal ...

sequence x 
 
x = append(1,2) 

We get the error message ...

first argument of append must be a sequence 

Then he states.

DerekParnell said...

... I ... had to code an explicit test to get around it. This is 'ugly' and makes the code a bit slower and larger.

function F(object A, object B) 
sequence x 
    if atom(A) then 
        x = append({A}, B) 
    else 
        x = append(A, B) 
    end if 
    return x 
end function 

His suggestion is to replace the append sequence function with an append object function which is essentially the F function that he discribed as "This is 'ugly' and makes the code a bit slower and larger". This of course saddles all OpenEuphoria programmers with this 'ugly', slower, larger functionality leaving his code no better off (except the code is hidden in the language now).

When I don't have any horses in a race I sometimes switch which horse I am cheering for (I cheer the underdog if there appears to be any chance).

In my opinion, one of the strengths of the Euphoria language is not having overloaded functions or multiple functions that all do the same basic thing but take different data types as parameters.

Viewed from that perspective, Derek's function becomes the appropriate append function which will work for all data types.

But the question is will this change break someone's program. Could someone have depended on the append function to detect when the first parameter is an atom and to stop the program in that case.

I guess that depends on when/how the error is currently detected. Can one call append(obj1, obj2) and when obj1 has a sequence assigned to it the function works and when an atom is assigned it fails? Or does the processor realize (say in a first pass) that obj1 is defined as an object variable not a sequence and throws the error irregardless of what is ultimately assigned to the variable?

In the second scenario changing horses will have no ill affects since the error detection would have resulted in the programmer changing obj1 into a sequence variable in order to get the program to run.

But in the first scenario it is possible someone's code will be affected if they choose to migrate their program to using the newest version of OpenEuphoria. When a large program written several years ago gets update to a newer version of a language, the initial assumptions and dependancies aren't always remembered (may not even be the same programmer). So this makes this kind of a change possibly serious. If some programmer was foolish enough to depend on the append function to detect corrupt data, the down stream data could become further corrupted when the program starts "working" instead of aborting.

Of course other languages handle this by producing thousands of pages of release notes with a one liner buried somewhere in the middle that reads something like: The append function has been modified (see manual for details).

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

34. Re: Discuss changing the behaviour of append()

mindwalker said...

Can one call append(obj1, obj2) and when obj1 has a sequence assigned to it the function works and when an atom is assigned it fails?

Yes. To further clarify, OpenEuphoria compiles append(1,2) cleanly but fails at run-time.

mindwalker said...

But the question is will this change break someone's program. Could someone have depended on the append function to detect when the first parameter is an atom and to stop the program in that case.

The proposed change cannot break a working program. Theoretically it might make some bugs ever-so-slightly harder to detect, however when appending a sequence it is no help whatsoever, which means it isn't a sensible thing to rely on to help debug programs anyway, at least for the "got yer arguments back-to-front" problems.

Pete

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

35. Re: Discuss changing the behaviour of append()

Here is my first draft at documenting the proposed changes.

_tom


Sequence Manipulations

The length function "counts top level items and returns the count as a length value." Nested items are not part of the top level count. (Counting is as good as measuring a length. To get the width of a road count the number of steps you take to get across.)

? length( {} ) 
    --> 0 
? length( 4 ) 
    --> 1 
? length( {4,5,"hello"} ) 
    --> 3 
Length Example Note
0 {} No items to count; length zero
1 a One potential item, an atom; length one
1 {x} One item, an object; length one
3 {x,x,x} Three items; length three

You can argue that an atom (a number or character) has no length. It is convenient to recognize that an atom adds one to the length of a sequence after a prepend, append, or concatenation.

You can not use length to distinguish an atom from a sequence.

There are various ways to increase the length of an object:

  • The coarse-grained functions (prepend, append) increase length by exactly one.
  • The fine-grained operator (&) increases length by the length of the object added.

In a few cases a function or the concatenation operator can be used to produce identical results. However, there are significant differences most of the time.

Number and text data behave the same way.

Length Increasing Functions

The prepend function "adds one entire object to the head of the target object." The append function "adds one entire object to the tail of the target object."

Coarse-grained means "the entire object is added as one item."

It is idiomatic in Euphoria to write these functions so the first argument is the target and the second argument is what gets added.

To change a target variable you have to explicitly assign the new value to it:

x = prepend(x, new_item ) 
 
x = append(x, new_item  ) 
Coarse-Grained Prepend Append
Target Argument
Entire Object
To Head To Tail
atom
5
{}

? prepend(5, {})
-->  {{}, 5} 

? append(5, {})
-->     {5, {}} 
6

? prepend(5, 6)
-->   {6, 5} 

? append(5, 6)
-->     {5, 6} 
{6}

? prepend(5,{6} )
--> {{6}, 5} 

? append(5, {6})
-->     {5, {6}} 
{6,7}

?   prepend(5, {6,7})
--> {{6,7}, 5} 

? append(5, {6,7})
-->     {5, {6,7}} 
sequence
{4,5}
{}

? prepend({4,5}, {})
-->   {{}, 4,5} 

?   append({4,5}, {})
-->        {4,5,  {}} 
6

? prepend({4,5}, 6)
-->    {6, 4,5} 

? append({4,5}, 6)
-->      {4,5,  6} 
{6}

? prepend({4,5},{6})
-->  {{6}, 4,5} 

? append({4.5}, {6})
-->      {4,5,  {6}} 
{6,7}

?  prepend({4,5}, {6,7})
--> {{6,7}, 4,5} 

? append({4,5}, {6,7})
-->      {4,5,  {6,7}} 

Length Increasing Operator &

The concatenation operator & "increases the length of the target object by the length of the object added."

Fine-grained means "all items are added."

Fine-Grained Concatenate Concatenate
Target Argument
All Items
To Head To Tail
atom
5
  {}

? {} & 5
-->    5 

?   5 & {}
--> 5 
6

?  6 & 5
--> {6,5} 

?    5 & 6
--> {5,6} 
{6}

?  {6} & 5
-->   {6,5} 

?    5 & {6}
--> {5,6} 
{6,7}

? {6,7} & 5
-->  {6,7,5} 

?   5 & {6,7}
--> {5,6,7} 
sequence
{4,5}
  {}

? {} & {4,5}
-->    {4,5} 

?   {4,5} & {}
--> {4,5} 
6

?  6 & {4,5}
-->  {6,4,5} 

?   {4,5} & 6
--> {4,5,6} 
{6}

?  {6} & {4,5}
-->    {6,4,5} 

?   {4,5} & {6}
--> {4,5,6} 
{6,7}

? {6,7} & {4,5}
-->   {6,7,4,5} 

?    {4,5} & {6,7}
-->  {4,5,6,7} 

Idiomatic Concatenation

You can combine the concatenation & operator with the assignment = statement.

object x=5, y=6 
x &= y 
? x 
--> {5,6} 
 
x &= y    -- short form 
x = x & y -- long form  

One use for the &= shortcut is to make concatenation & behave like an append function:

object x, y 
x &= {y} 
Target Argument Result
x y Concatenation Append

x -->

x &= {y}

x = append(x,y)
atom
5
{}

x --> {5,{}}
6

x --> {5,6}
{6}

x --> {5,{6}}
{6,7}

x --> {5,{6,7}}
sequence
{4,5}
{}

x --> {4,5,{}}
6

x --> {4,5,6}
{6}

x --> {4,5,{6}}
{6,7}

x --> {4,5,{6,7}}

Legacy Notes

Early versions (O[ 3.1 and before) of length did not recognize the length of an atom as one.

Early versions (O[ 4.0 and before) of prepend and append would not allow the target to be an atom.

See Also

insert, splice, remove

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

36. Re: Discuss changing the behaviour of append()

DerekParnell said...

In my current code, I ran across this case and had to code an explicit test to get around it. This is 'ugly' and makes the code a bit slower and larger.

function F(object A, object B) 
sequence x 
    if atom(A) then 
        x = append({A}, B) 
    else 
        x = append(A, B) 
    end if 
    return x 
end function 

Derek, this case shows that there was some wrong logic in your "current code". So, your code was 'ugly', not your new function F() which corrected your mistake.

And that your subtle logic bug was fixed thanks to old good append() feature.

Or, maybe, you just forgot how append() works? smile


Regards


kinz

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

37. Re: Discuss changing the behaviour of append()

kinzz said...

Derek, this case shows that there was some wrong logic in your "current code".

And that your subtle logic bug was fixed thanks to old good append() feature.

Okay, what was the wrong logic bug?

kinzz said...

So, your code was 'ugly', not your new function F() which corrected your mistake.

It's still slower, as F() has to make the check for a sequence and then append() will that check a second time. Why not consolidate these two into a single check?

kinzz said...

Or, maybe, you just forgot how append() works? smile

Let's remember, length() works on atoms. It's append() (and prepend()) that's inconsistent here.

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

38. Re: Discuss changing the behaviour of append()

kinzz said...

Derek, this case shows that there was some wrong logic in your "current code". So, your code was 'ugly', not your new function F() which corrected your mistake.

And that your subtle logic bug was fixed thanks to old good append() feature.

Or, maybe, you just forgot how append() works? smile

Seriously !?

No. Your assumptions about my code are once again wrong. Here is the actual function I'm writing...

-- This routine appends to an item at an arbitrary depth. 
-- Params: 
--     S   The sequence that holds the item which will be appended to. 
--     I   A list of indexes that point to the specific item in S to append to. 
--     X   The object that will be appended to the targeted item. 
 
function deep_append(sequence S, sequence I, object X) 
 
    if length(I) <= 1 then 
        if length(I) = 1 then 
            -- Using concatenation of a sequence here to get around appending to an atom. 
            S[I[1]] &= {X} 
        end if 
        return S 
    end if 
     
    integer n = I[1] 
    
    return S[1 .. n - 1] & {deep_append(S[n], I[2 .. $], X)} & S[n + 1 .. $] 
end function 

The above function works as specified, however I'd hoped for better performance.

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

39. Re: Discuss changing the behaviour of append()

Tom:

I think your documentation is a bit verbose. I like your simple explanation that append() and prepend() return an object whose length is exactly one greater than their first operand, whereas the concatenation operator & returns an object whose length is the sum of the lengths of its two operands.

The "fine grained" and "coarse grained" bits are confusing. Just call & the "concatenation operator". Then give a few (three) examples for each append(), prepend(), and &.

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

40. Re: Discuss changing the behaviour of append()

jimcbrown said...
kinzz said...

Derek, this case shows that there was some wrong logic in your "current code".

And that your subtle logic bug was fixed thanks to old good append() feature.

Okay, what was the wrong logic bug?

jimcbrown, see please 38.Re:...
Do you see the normal append() function call from Derek's new deep_append() function?

Sorry, I do not see...

This is the second Derek's logic bug - he just doesn't demonstrate that his wrong code, and I can say nothing about his first logic bug.

Sophisma or paralogismos.

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

41. Re: Discuss changing the behaviour of append()

DerekParnell said...

Seriously !?

Yes, seriously, sorry...

There is no the normal append() call in your function now...

That bug was fixed thanks to old good append().
No?

Regards


kinz

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

42. Re: Discuss changing the behaviour of append()

kinzz said...
jimcbrown said...
kinzz said...

Derek, this case shows that there was some wrong logic in your "current code".

And that your subtle logic bug was fixed thanks to old good append() feature.

Okay, what was the wrong logic bug?

jimcbrown, see please 38.Re:...
Do you see the normal append() function call from Derek's new deep_append() function?

Sorry, I do not see...

I don't see the call to normal append() from deep_append(), but I also fail to see the logic bug.

That said, the existing code could be made to work with append like so:

-- This routine appends to an item at an arbitrary depth. 
-- Params: 
--     S   The sequence that holds the item which will be appended to. 
--     I   A list of indexes that point to the specific item in S to append to. 
--     X   The object that will be appended to the targeted item. 
 
function deep_append(sequence S, sequence I, object X) 
 
    if length(I) <= 1 then 
        if length(I) = 1 then 
	    if atom(S[I[1]]) then -- append() is also making this check, why do we have to make this check twice??? 
            S[I[1]] &= {X} -- having to make a new sequence also slows down the atom case 
	    else 
            S[I[1]] = append(S[I[1]], X) 
	    end if 
        end if 
        return S 
    end if 
 
    integer n = I[1] 
 
    return S[1 .. n - 1] & {deep_append(S[n], I[2 .. $], X)} & S[n + 1 .. $] 
end function 

Look how much nicer it is when append() can do atoms too!

-- This routine appends to an item at an arbitrary depth. 
-- Params: 
--     S   The sequence that holds the item which will be appended to. 
--     I   A list of indexes that point to the specific item in S to append to. 
--     X   The object that will be appended to the targeted item. 
 
function deep_append(sequence S, sequence I, object X) 
 
    if length(I) <= 1 then 
        if length(I) = 1 then 
            S[I[1]] = append(S[I[1]], X) 
        end if 
        return S 
    end if 
 
    integer n = I[1] 
 
    return S[1 .. n - 1] & {deep_append(S[n], I[2 .. $], X)} & S[n + 1 .. $] 
end function 

[/quote]

kinzz said...

This is the second Derek's logic bug -

You've failed to point out a single logic bug, let alone two.

kinzz said...

and I can say nothing about his first logic bug.

Because it doesn't exist?

kinzz said...

he just doesn't demonstrate that his wrong code,

Sophisma or paralogismos.

I don't see append() working on atoms being a sophismata. After all, append() already works with atoms as the second parameter!

If you really believe Derek's logic bugs fall into the category of sophisma, you should at least be able to articulate the "odd consequences" or ambiguous interpretation of true/false value here.

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

43. Re: Discuss changing the behaviour of append()

petelomax said...
mindwalker said...

But the question is will this change break someone's program. Could someone have depended on the append function to detect when the first parameter is an atom and to stop the program in that case.

The proposed change cannot break a working program. Theoretically it might make some bugs ever-so-slightly harder to detect, however when appending a sequence it is no help whatsoever, which means it isn't a sensible thing to rely on to help debug programs anyway, at least for the "got yer arguments back-to-front" problems.

Pete

Since append currently works the way I thought I remembered it did, I disagree Pete with your statement. The proposed change could break a working program.

For example a program could be designed to read in a text file, one line at a time, where each line in the file is supposed to start with a sequence followed by 3 atoms. Think customer information followed by an item number, item count and the item price. The program might be intended to add the item number to the end of the sequence and then compute the total charge and add that to the sequence and then store that sequence to a data base as a transaction.

That program make use of an included, company developed, generic file read function designed to read files containing text strings that represent euphoria sequences and text strings that represent individual atoms. That function could return a sequence of objects based on what was read (some sequences and some atoms).

Of course someone using this generic read function would want to verify the returned line items were the right type and order. And a program such as the one I propose might be designed to just stop if the input doesn't follow the right format (the concept of "do no more harm" and let a human figure out how to fix the problem).

So some "slick" programmer may have realized that the first append would abort if the first object in the line wasn't a sequence. So said programmer may have skipped the separate sequence test and depended on the append function working as documented.

Five years of running this program every hour 24x7x365 and only 3 aborts when the input file was corrupted one way or another and the company decides to stop using Euphoria 2.4 and move to the latest and greatest OpenEuphoria.

At this point, the previously working as designed program would be broken. It would no longer be detecting and aborting if the first line item wasn't a sequence. That is unless the programmer migrating this just happens to remember/realize that the append function was providing this sequence check and modifies the program to now do its own check instead.

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

44. Re: Discuss changing the behaviour of append()

jimcbrown said...

I don't see the call to normal append() from deep_append(), but I also fail to see the logic bug.

Simple logic bug. The code was wrong, not the normal append(). Why append(), if code was wrong?

Do you see my logic now? I say about the discussion logic here.

jimcbrown said...
kinzz said...

This is the second Derek's logic bug -

You've failed to point out a single logic bug, let alone two.

See please above once more.

jimcbrown said...
kinzz said...

and I can say nothing about his first logic bug.

Because it doesn't exist?

Yes, Derek uses & now. He changed his primary code. He changed the code logic. See please my recomendation in 29.Re:

kinzz said...

Use & and be happy :)

Do you see sophisma or paralogismos in discussion now?

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

45. Re: Discuss changing the behaviour of append()

kinzz said...
jimcbrown said...

I don't see the call to normal append() from deep_append(), but I also fail to see the logic bug.

Simple logic bug. The code was wrong, not the normal append(). Why append(), if code was wrong?

Do you see my logic now? I say about the discussion logic here.

Ok, I see the reasoning.

But, I disagree - append() is wrong, not the code.

kinzz said...

Yes, Derek uses & now. He changed his primary code. He changed the code logic.

Yes, he was forced to. Because append() is wrong.

kinzz said...

See please my recomendation in 29.Re:

kinzz said...

Use & and be happy :)

Do you see sophisma or paralogismos in discussion now?

Yes, indeed. You make a good point here.

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

46. Re: Discuss changing the behaviour of append()

mindwalker said...

Since append currently works the way I thought I remembered it did, I disagree Pete with your statement. The proposed change could break a working program.

So some "slick" programmer may have realized that the first append would abort if the first object in the line wasn't a sequence. So said programmer may have skipped the separate sequence test and depended on the append function working as documented.

This is a particularly expensive way of doing that test, as you can't catch or trap exceptions like this. The error would abort the entire program. One would need to use multiple processes and have a master program that detected when the subprogram crashed and take appropriate action at that point. And of course, if the subprogram was halfway thru a giant list of records, that time is wasted as it'll have to start again from the begining.

Much simpler to just do the check manually in the first place.

mindwalker said...

Five years of running this program every hour 24x7x365 and only 3 aborts when the input file was corrupted one way or another and the company decides to stop using Euphoria 2.4 and move to the latest and greatest OpenEuphoria.

At this point, the previously working as designed program would be broken. It would no longer be detecting and aborting if the first line item wasn't a sequence. That is unless the programmer migrating this just happens to remember/realize that the append function was providing this sequence check and modifies the program to now do its own check instead.

So if we used petelomax's suggestion of appenda()/prependa(), then there would be no problem here?

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

47. Re: Discuss changing the behaviour of append()

kinzz said...

Do you see the normal append() function call from Derek's new deep_append() function?

Sorry, I do not see...

This is the second Derek's logic bug - he just doesn't demonstrate that his wrong code, and I can say nothing about his first logic bug.

You sir, are have a very effective trolling skill.
I didn't show you my failing code because I assumed you had the required Euphoria skills to see when the use of append() would cause the issue first described. My mistake.

Can you point out where there are any logic errors in this function or discussion of mine, please.

The code I posted earlier was the working code. My first attempt at this function used the append() function but that failed at times, due to it trying to append to an atom.

Instead of ...

S[I[1]] &= {X} 

my original function had

integer n = I[1] 
S[n] = append(S[n], X) 

This fails when the item at S[n] is an atom. I then changed my code to explicitly test for that condition ...

integer n = I[1] 
if atom(S[n]) then 
    S[n] = append({S[n]}, X) 
else 
    S[n] = append(S[n], X) 
end if 

At which point I posted my original question. I have now changed my function to use the concatenation operator to get around the shortcoming in append(), but my suggestion about changing the append() function's behaviour still stands.

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

48. Re: Discuss changing the behaviour of append()

mindwalker said...

Since append currently works the way I thought I remembered it did, I disagree Pete with your statement. The proposed change could break a working program.

For example a program could be designed to read in a text file, one line at a time, where each line in the file is supposed to start with a sequence followed by 3 atoms. ...

One of the better techniques in designing applications, is to always provide validity checking on data that comes from outside the program, as opposed to data that is generated by the program. It would be better, IMO, to validate the text file data before using that data. The application then can either fail gracefully or come to some other arrangement.

By the way, data that is generated by an application should be subject to 'logic' testing during the testing/debugging stages of an application's development, and not during live run times.

Thus from my point of view, I see that my suggested change would not break working programs but would break non-working programs smile

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

49. Re: Discuss changing the behaviour of append()

jimcbrown said...

So if we used petelomax's suggestion of appenda()/prependa(), then there would be no problem here?

Ugh! No, please.

We would have append()/prepend() when the first argument must only be a sequence and appenda()/prependa() when the first argument can be any object. That arrangement, I predict, would be more confusing and more likely to introduce errors in code, plus people would tend to just use the newer functions anyway.

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

50. Re: Discuss changing the behaviour of append()

DerekParnell said...

One of the better techniques in designing applications, is to always provide validity checking on data that comes from outside the program, as opposed to data that is generated by the program. It would be better, IMO, to validate the text file data before using that data. The application then can either fail gracefully or come to some other arrangement.

Agreed.

DerekParnell said...

Thus from my point of view, I see that my suggested change would not break working programs but would break non-working programs smile

Agreed in full!

DerekParnell said...

By the way, data that is generated by an application should be subject to 'logic' testing during the testing/debugging stages of an application's development, and not during live run times.

This is a sound principle, but in practice it's hard to think of/catch everything. So those 3 times when there was bad data (and the application crashed?) could easily have been from causes that somehow got missed during the original testing/debugging states, or an "impossible situation" that happened anyways. It happens. Hence the reason to keep validity checking code durning live run times.

I think we can all agree that relying on append() to crash your program for validity checking purposes is a silly way to do things.

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

51. Re: Discuss changing the behaviour of append()

jimcbrown said...
mindwalker said...

Since append currently works the way I thought I remembered it did, I disagree Pete with your statement. The proposed change could break a working program.

So some "slick" programmer may have realized that the first append would abort if the first object in the line wasn't a sequence. So said programmer may have skipped the separate sequence test and depended on the append function working as documented.

This is a particularly expensive way of doing that test, as you can't catch or trap exceptions like this. The error would abort the entire program. One would need to use multiple processes and have a master program that detected when the subprogram crashed and take appropriate action at that point. And of course, if the subprogram was halfway thru a giant list of records, that time is wasted as it'll have to start again from the begining.

Much simpler to just do the check manually in the first place.

I wasn't arguing the merits of a particular programming choice. There probably are a thousand better ways to do this. I was arguing that the change could break a working program and gave a hypothetical program scenario to illustrate.

jimcbrown said...
mindwalker said...

Five years of running this program every hour 24x7x365 and only 3 aborts when the input file was corrupted one way or another and the company decides to stop using Euphoria 2.4 and move to the latest and greatest OpenEuphoria.

At this point, the previously working as designed program would be broken. It would no longer be detecting and aborting if the first line item wasn't a sequence. That is unless the programmer migrating this just happens to remember/realize that the append function was providing this sequence check and modifies the program to now do its own check instead.

So if we used petelomax's suggestion of appenda()/prependa(), then there would be no problem here?

Actually for me Pete Lomax's suggestion is the one I would find least favorable. It is a step in the direction C and so many other languages have gone. The language becomes more complicated and harder to learn since there would be a sequence append and then a new objects append. Newbies would be trying to determine when each should be used, not realizing that they could use appenda in all cases and would never really need the old append.

I think in the end I like Derek's suggestion. But it does have the potential to break existing programs in subtle ways. I would suggest that the release notes should make a big deal about this change and maybe even the manual as well, so that anyone migrating from earlier versions has some chance of recognizing the potetial problem.

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

52. Re: Discuss changing the behaviour of append()

jimcbrown said...
DerekParnell said...

One of the better techniques in designing applications, is to always provide validity checking on data that comes from outside the program, as opposed to data that is generated by the program. It would be better, IMO, to validate the text file data before using that data. The application then can either fail gracefully or come to some other arrangement.

Agreed.

DerekParnell said...

Thus from my point of view, I see that my suggested change would not break working programs but would break non-working programs smile

Agreed in full!

DerekParnell said...

By the way, data that is generated by an application should be subject to 'logic' testing during the testing/debugging stages of an application's development, and not during live run times.

This is a sound principle, but in practice it's hard to think of/catch everything. So those 3 times when there was bad data (and the application crashed?) could easily have been from causes that somehow got missed during the original testing/debugging states, or an "impossible situation" that happened anyways. It happens. Hence the reason to keep validity checking code durning live run times.

I think we can all agree that relying on append() to crash your program for validity checking purposes is a silly way to do things.

All of my original post was a hypothetical scenario with hyperbola added to indicate the satifactory performace of the product in meeting the users needs. Three failures in 5 years indicating the source file generation works correctly except during sudden power outages or network failures or such.

That said in the real world people do make these choices; hobby programmers, small businesses where the owner is the programmer, etc. It may be clever of you to label their program as "not working" just because it doesn't meet your better programming standards. But for them it meets the need and didn't require a ton of programming. In fact that is one of the strengths of Euphoria. Someone doesn't need to be a professional programmer with knowledge of all the best programming standards to fashion a program that as far as they are concerned works.

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

53. Re: Discuss changing the behaviour of append()

mindwalker said...

All of my original post was a hypothetical scenario with hyperbola added to indicate the satifactory performace of the product in meeting the users needs.

It may be clever of you to label their program as "not working" just because it doesn't meet your better programming standards.

But it's not a good idea to just label it that way. Agreed. This reminds me of Linus Torvald's rule: http://felipec.wordpress.com/2013/10/07/the-linux-way/

mindwalker said...

Three failures in 5 years indicating the source file generation works correctly except during sudden power outages or network failures or such.

I.e. rare, unexpected cases. Gotcha. (Of course, you can simulate a power outage (pull the power cord out) or a network failure (unplug your hub/switch), but there's probably always something else that'll trip things up .. eventually. You can't think of everything.)

mindwalker said...

That said in the real world people do make these choices; hobby programmers, small businesses where the owner is the programmer, etc.

Yes, in the real world ppl sometimes take shortcuts. Agreed.

mindwalker said...

But for them it meets the need and didn't require a ton of programming. In fact that is one of the strengths of Euphoria. Someone doesn't need to be a professional programmer with knowledge of all the best programming standards to fashion a program that as far as they are concerned works.

Agreed. Still, relying on a program to complete crash in an ungraceful way is kind of unusual and must be rare. I agree with you - it should be ok to make the change to append() (as you agreed), but we should document it well (like we did with the change to length()).

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

54. Re: Discuss changing the behaviour of append()

Well, I'm joining late, but I think what we have here is performance vs the ability to introduce logical bugs.
As those bugs could still arise, specially if length()'s behavior is already similar to what's proposed, I'd go with performance.

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

55. Re: Discuss changing the behaviour of append()

DerekParnell said...

One of the better techniques in designing applications, is to always provide validity checking on data that comes from outside the program, as opposed to data that is generated by the program.

This can (and should) be applied to functions.

A[i] can be a sequence or an atom.\\ 
                 append(A[i],x) : sequence : OK\\ 
                 atom : How do you want to see it? (atom or sequence)? 
This is legitimate because A[i] is the i in A[i..i]??

A[i..i] is a sequence
A[i..i-1] ditto
A{i..i-2] is rubbish

At some point here I am losing you.

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

56. Re: Discuss changing the behaviour of append()

gimlet said...
DerekParnell said...

One of the better techniques in designing applications, is to always provide validity checking on data that comes from outside the program, as opposed to data that is generated by the program.

This can (and should) be applied to functions.

When reasonable to do so. The problem is when one adds in restrictions that serve no purpose.

(There's a second problem, where it's reasonable for a function in the general case to have a certain behavior, but in a specific context it causes a logical error in the data. But we don't seem to be discussing this here.)

gimlet said...

A[i] can be a sequence or an atom.\\ 
                 append(A[i],x) : sequence : OK\\ 

So far, so good.

gimlet said...

                 atom : How do you want to see it? (atom or sequence)? 

Autopromotion - we already do this from integers to atoms in the delete case. Now we can promote the atom to a sequence (n -> {n}) and then append x to it.

In general though, autopromotion should be done in specific, case-by-case circumstances.

gimlet said...

This is legitimate because A[i] is the i in A[i..i]??

Uh, no.

gimlet said...

A[i..i] is a sequence

Yes. That's always a 1-element sequence.

gimlet said...

A[i..i-1] ditto

Yes. That's the empty sequence.

gimlet said...

A{i..i-2] is rubbish

Agreed. That's a syntax error.

gimlet said...

At some point here I am losing you.

I followed you up to the bit after the bit about the rubbish.

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

57. Re: Discuss changing the behaviour of append()

gimlet said...
DerekParnell said...

One of the better techniques in designing applications, is to always provide validity checking on data that comes from outside the program, as opposed to data that is generated by the program.

This can (and should) be applied to functions.

I hold a contrary opinion. In the general case, a function should not be required to validate its arguments. I understand that this sounds almost heretical to many, but let me explain.

A function can not know if the values in its arguments originated from outside the program or internally. In a well-behaved program, data coming into the application will be validated prior to it being used, and usually at the point at which it can give the most meaningful error/warning message to the end user. Calling a function with arguments is defined as data usage. Also, in a well-behaved program, data generated by the program will be correct, and this will be proven by appropriate logic testing during the development stage prior to its live release.

I believe that a routine should assume that it is operating inside a well-behaving program. Thus it will not be required to ensure that its arguments, or any shared program data, are valid. The program will have already done that so there's no need to do it again. However, any data that a function creates and makes available to other code, should be validated by that function. This needs only to be done while in development mode.

At this point I need to talk a bit about "what is valid data?" In general terms, its the set of values that a routine can reasonably be expected to operate on.

For example, consider this function to calculate the real 'X' value for a quadratic equation, which is defined as ... aX2 + bX + c = 0 . For any given quadratic equation, the X can have zero, one or two values which solves the equation. Thus the function should return a sequence of 0, 1, or 2 elements.

The formula that the function needs to evaluate is ...

X1 = (-b - sqrt(b2 - 4ac)) / 2a and X2 = (-b + sqrt(b2 - 4ac)) / 2a

The first thing to note is what should the function do if a=0? If it doesn't check then the program will fail with a divide-by-zero error. If it does check, then its also admitting that the calling program is trying to use the function in an illogical manner. By definition, if a=0 then the equation is no longer a quadratic one but a linear one, and this function is only supposed to be used for quadratics.

I maintain that it's the calling program's responsibility to do the checking of a before calling the function, because it knows when is the most efficient time to do that. The function would be nearly always be duplicating or wasting effort if always checked for a=0.

So, one version of this function could be ...

 
ifdef DEVEL_MODE then 
  include std/error.e 
  procedure fail_if(integer x, sequence t) 
    if x then 
        crash("** FAILURE DETECTED **\n" &  t & '\n') 
    end if 
  end procedure 
end ifdef 
 
-- Compares two floating point numbers allowing for a tolerance. 
function compare_real(atom f, atom g, atom e = 0.000_000_000_000_009) 
 
	atom r 
	r = f-g 
	if r > e then 
		return 1 
	elsif r < -e then 
	    return -1 
	else  
	    return 0 
	end if 
end function     
 
----------------------------------- 
public function solve_quad_eqn(atom a, atom b, atom c) 
----------------------------------- 
    sequence res 
 
    atom descrim 
 
    descrim = b * b - 4 * a * c 
    -- Adjust result for limitations in floating point math. 
    if compare_real(descrim, 0) = 0 then 
    	descrim = 0 
    end if 
     
    if descrim < 0 then 
        res = {} 
         
    elsif descrim = 0 then 
        res = {-(b / 2 * a)} 
         
    else 
        descrim = sqrt(descrim) 
        res = - { b - descrim, b + descrim } / (2 * a)  
         
    end if 
 
    ifdef DEVEL_MODE then 
    	-- Check that results are sane. 
        for i = 1 to length(res) do 
            fail_if(not atom(res[i]), "The results must be real numbers") 
        end for 
 
    	atom temp = compare_real( (b*b), (4*a*c) ) 
    	atom vx = -(b / 2 * a) 
    	atom vy = -( b*b - 4*a*c) / (4 * a) 
    	 
        if length(res) = 0 then 
           fail_if(temp >= 0, "Should have one or two Real results") 
           fail_if(a > 0 and vy <= 0, "Vertex too low") 
           fail_if(a < 0 and vy >= 0, "Vertex too high") 
            
        elsif length(res) = 1 then 
           fail_if(temp != 0, "Should have zero or two Real results") 
           fail_if(compare_real(vy , 0) != 0, "Vertex Y should be zero") 
           fail_if(compare_real(vx , res[1]) != 0, "Vertex X should be result") 
            
        elsif length(res) = 2 then 
           fail_if(temp <= 0, "Should have zero or one Real results") 
           fail_if(compare_real(-(b / a), res[1] + res[2]) != 0, "Invalid symetry") 
           fail_if(a > 0 and vy >= 0, "Vertex too high") 
           fail_if(a < 0 and vy <= 0, "Vertex too low") 
            
        else 
           fail_if(1, "Only a maximum of two results are allowed") 
        end if 
 
    end ifdef 
 
    return res 
 
end function 
 
ifdef UNIT_TESTING then 
procedure test_solve_quad() 
	for i = -20 to 20 do 
	for j = -4 to 4 do 
	for k = -5 to 5 by 0.3 do 
 
	if i != 0 then 
		solve_quad_eqn(i/10, j, k) 
	end if 
	end for 
	end for 
	end for 
end procedure 
test_solve_quad() 
end ifdef 


So only during development would the results of the function be validated. Once in production, the function is known to be behaving as specified so we no longer have to make sure its results are good; they will be. And we don't check its arguments because the calling program should have already checked those.

gimlet said...

At some point here I am losing you.

Yes, that you have.

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

58. Re: Discuss changing the behaviour of append()

Here is a another draft of possible documentation. After the change to the definition of "length" from Euphoria 3 to 4, these results follow. Everything seems to fit a consistent pattern once you allow for atomatic promotion of an atom to a sequence.

_tom

(Edit, I made an interesting typo: atomatic vs automatic...)

Object Length

The length of an object "is a count of the top-level items it contains."

The length of a sequence is the number of items it contains; if an item is a nested sequence it still counts as one item. The length of an empty sequence is zero; it contains no items.

The length of an atom is one. This is a convenient viewpoint because when an atom is added to a sequence (by inserting or concatenating) the result is an increase in length by one.

It is easy to argue that a number or a character has no defined length. In Euphoria versions before O[ 4 atoms did not have a defined length.

An atom a is not the same as a sequence {a} containing one atom. This distinction is important and is used in many algorithms.

Increasing Length

You can increase the length of an object by adding one or more items. There are two ways to do this: insertion and concatenation.

  • An insertion (prepend, insert, append) increases length by exactly one.
  • A concatenation ( & ) increases length by the length of the added object.

It is really convenient to promote an atom into a one item sequence automatically when performing an object lengthening.

Concatenation &

The concatenation operator "joins two objects together to make a sequence whose length is equal to the sum of their individual lengths."

Numerical Text
atom : atom

? 3 & 4
--> {4,5} 

? 'O' & 'E'
--> "OE" 
sequence : sequence

? {1,2} & {3,4}
--> {1,2,3,4} 

? "Open" & "Euphoria"
--> "OpenEuphoria" 

Visualizing Concatenation

A way to visualize the process of concatenation is to imagine some intermediate steps:

Concatenating sequences:

? {1,2} & {3,4} 
--> {1,2  }&{  3,4} 
--> {1,2   ,   3,4} 

When concatenation involves an atom:

? {1,2} & 3 
--> {1,2  }&   3 
--> {1,2  }&{  3}   -- promotion of atom to sequence required 
--> {1,2   ,   3} 

Prepend, Insert, Append

An insertion function (prepend, insert, append) "places an object as an item within a sequence."

The index argument of the insert function places the new item at the index value of the new sequence.

Think of prepend as insertion of an object before the first item of a sequence. Think of append as insertion of an object after the last item in a sequence.

An insertion of a string into a string never produces another string. Use a concatenation if you need the result to be a string.

Numerical Text Special Note
atom : atom

? prepend(3,4)
--> {4,3} 

? prepend('a','z')
--> "za" 
* fails on O[ 4 and earlier
* considered for O[ 4.1
sequence : atom

? append({1,2},3)
--> {1,2,3} 

? insert("horse", 'a', 3)
--> {104,111,97,114,115,101}
-- "hoarse" 
sequence : sequence

? prepend({1,2},{3,4})
--> { {3,4}, 1,2} 

? prepend("jay","blue")
--> { {98,108,117,101},
--    106,
--    97,
--    121 } 

Visualizing Insertion

A way to visualize an insertion function is to imagine some intermediate steps:

An append of an atom to a sequence:

? append({1,2},3) 
--> {1,2,   } 
           ^  
           3 
--? {1,2,3} 

An insertion of a character in a string:

? insert("44",'X',2) 
--> {52,    52} 
         ^ 
         120 
--> {52,120,52} 
--  "4X4" 

Inserting an object into an atom makes no sense unless you allow for an automatic promotion of an atom into a sequence:

? prepend(1, 5) 
--> prepend( {1}, 5)  -- promotion of atom to sequence required 
--> {  1} 
      ^ 
      5 
--> { 5, 1 } 

For version O[ 4 and earlier Euphoria this automatic promotion of an atom to a sequence was not allowed.

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

59. Re: Discuss changing the behaviour of append()

jimcbrown said...

Ok, I see the reasoning.

But, I disagree - append() is wrong, not the code.

jimcbrown, append() worked as expected in that Derek's original wrong code.
It signalled about Derek's bug.
Same as it worked 20 years in thousands of EU programs, without any claims from users.

Do you know just one claim?

Derek had to use &, not append(). Period.

And he do use & now, and his new function works ok (as he says) and looks good.

If you wanna another append(), it is yours choice and it is totally another story.

Regards


kinz

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

60. Re: Discuss changing the behaviour of append()

kinzz said...
jimcbrown said...

Ok, I see the reasoning.

But, I disagree - append() is wrong, not the code.

If you wanna another append(), it is yours choice and it is totally another story.

No, it's not another story, it's this story. The whole point of this thread is about that!

kinzz said...

jimcbrown, append() worked as expected in that Derek's original wrong code.
It signalled about Derek's bug.

I'll rewrite your quotes as follows:

said...

jimcbrown, append() worked as expected in that Derek's original code.
It signalled to Derek about something it couldn't handle.

Now that I've removed the loaded assumptions in your quotes, I'm happy to agree with you.

Derek's original code found a case that append() couldn't handle, and we want to replace append() with another append() that can handle this case.

kinzz said...

Same as it worked 20 years in thousands of EU programs, without any claims from users.

Do you know just one claim?

There were probably bigger claims to deal with, like not being able to declare a variable in the middle of a function, or to assign a value to a variable upon declaration.

kinzz said...

Derek had to use &, not append(). Period.

This is just flat out wrong. Technically, he could have used append(), even in a 2.4 compatible version of his function. It would have been more work, true, but it could be done.

kinzz said...

And he do use & now, and his new function works ok (as he says) and looks good.

He never said that it looks good.

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

61. Re: Discuss changing the behaviour of append()

kinzz said...
jimcbrown said...

Ok, I see the reasoning.

But, I disagree - append() is wrong, not the code.

jimcbrown, append() worked as expected

This reminds me of an argument I had a while ago about PS/2 connections.

You have serial mice, and maybe there were serial keyboards at one point in time too.

Now, like USB, the original serial line cable was designed so you could unplug and replug devices back in. You could unplug your serial mouse from the computer and plug it back in and it'd work again.

Then I was told that PS/2 is actually derived from serial. And yet, with PS/2, if you unplug your mouse or keyboard, you generally have to reboot to get things working again. I wonder why PS/2 actually lost functionality, compared to the older serial protocol.

Anyways, I was told that this was "by design." That's right, PS/2 was designed to lose functionality compared to serial. I don't know if that's actually true or if the person I was holding this conversation with was just pulling my leg, but this brings up a good point I'd like to make.

Just because something works as designed, doesn't mean that everything's perfect - the original design might have been bad (as imvho PS/2 lack of hotplugability is), thus making improvements necessary.

I still have a lot of old PS/2 keyboards around (and even one DIN keyboard), and a few older desktops that still have PS/2 ports on them. But I got a bunch of USB/PS2 converters and connect everything via USB now. Life's just easier that way.

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

62. Re: Discuss changing the behaviour of append()

DerekParnell said...

Can you point out where there are any logic errors in this function or discussion of mine, please.

The code I posted earlier was the working code. My first attempt at this function used the append() function but that failed at times, due to it trying to append to an atom.

Instead of ...

S[I[1]] &= {X} 

my original function had

integer n = I[1] 
S[n] = append(S[n], X) 

This fails when the item at S[n] is an atom.

There is nothing amazing that "this fails". Manual reads: Syntax: s2 = append(s1, x)

You had to append to the sequence, not to an atom.
Your logic was erroneous, you thought that S[n] can not be an atom.
But why not ???
If S={1,2,3,4} then S[3]=3
The atom!

That above was about the function logic.

And there is no any reason to tell on append() if your function works after correction with append() and (much better) with &.
This is about the discussion logic.

But if you wanna another append(), it is fully another story.

Regards


kinz

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

63. Re: Discuss changing the behaviour of append()

kinzz said...
DerekParnell said...

Can you point out where there are any logic errors in this function or discussion of mine, please.

The code I posted earlier was the working code. My first attempt at this function used the append() function but that failed at times, due to it trying to append to an atom.

Instead of ...

S[I[1]] &= {X} 

my original function had

integer n = I[1] 
S[n] = append(S[n], X) 

This fails when the item at S[n] is an atom.

There is nothing amazing that "this fails". Manual reads: Syntax: s2 = append(s1, x)

You had to append to the sequence, not to an atom.
Your logic was erroneous, you thought that S[n] can not be an atom.
But why not ???
If S={1,2,3,4} then S[3]=3
The atom!

That above was about the function logic.

And there is no any reason to tell on append() if your function works after correction with append() and (much better) with &.
This is about the discussion logic.

But if you wanna another append(), it is fully another story.

Regards


kinz

I started to respond to this, then I realized that it was more-or-less identical to http://openeuphoria.org/forum/m/124582.wc, which I already responded to.

Perhaps we should take a different tack. What is the compelling original reason for append() to have refused to accept atoms as the first parameter in the first place?

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

64. Re: Discuss changing the behaviour of append()

kinzz said...

There is nothing amazing that "this fails".

I believe that English is not your first language, so I'd just like to point out that at no time have I said that I was "amazed" or even surprised by the current behaviour of append().

kinzz said...

Manual reads: Syntax: s2 = append(s1, x)

You had to append to the sequence, not to an atom.
Your logic was erroneous, you thought that S[n] can not be an atom.

What does "amaze" me is your attempts to read my mind. I did not think that S[n] can not be an atom. I fully realized that an arbitrary sequence element could be an atom. What actually happened was that I forgot that append() had the restriction about it's first parameter, because it seemed natural to me that append() would work just like the concatenation operator.

kinzz said...

if your function works after correction with append() and (much better) with &.

And this is the point I'm making ... the function does not become better (in my opinion) using the concatenation operator.

kinzz said...

But if you wanna another append(), it is fully another story.

But I don't want another function that works almost the same as append(). I want append() to work using the same rationale as concatenate does.

So to summarize, you have not understood me or my posts at all. For this, I'm sorry.

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

65. Re: Discuss changing the behaviour of append()

Derek,

We agree that a function's parameters should be validated before entering the function. This is why I have said:

The real problem is not append but A[i] being an atom.

My point with the various slices is that every slice is a sequence but A[i] is possibly not.

It would seem reasonable (to me at least) that A[i] and A[i..i] ought be interchangeable. Therefore I am inclined to say the difficulty with append is with the (ambiguous) nature of A[i].

I don't think that append shoud be changed as restricting the target type to sequence is a good restriction.

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

66. Re: Discuss changing the behaviour of append()

Derek,

We agree that a function's parameters should be validated before entering the function. This is why I have said:

The real problem is not append but A[i] being an atom.

My point with the various slices is that every slice is a sequence but A[i] is possibly not.

It would seem reasonable (to me at least) that A[i] and A[i..i] ought be interchangeable.
Therefore I am inclined to say the difficulty with append is with the (ambiguous) nature of A[i].

I don't think that append shoud be changed as restricting the target type to sequence is a good restriction.

    append(A[i], x) -- yes 
append(1,2)     -- no 

Obviously with call-by-value the function wouldn't know the difference.
I don't know how the caller could differentiate cleanly except with a sequencing operator (an idea which seems to have been rejected)

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

67. Re: Discuss changing the behaviour of append()

DerekParnell said...
    integer n = I[1] 
    
    return S[1 .. n - 1] & {deep_append(S[n], I[2 .. $], X)} & S[n + 1 .. $] 
end function 

The above function works as specified, however I'd hoped for better performance.

Would this not help?

    integer n = I[1] 
    S[N] = deep_append(S[n], I[2 .. $], X)    
    return S 
end function 



mindwalker said...

Five years of running this program every hour 24x7x365 and only 3 aborts...

At this point, the previously working as designed program would be broken.

As others might have said, I think you meant

the previously broken as designed program would no longer be properly broken. 
I can (just about) see your point, but I'm not going to lose any sleep over it. As long as things get properly documented, of course.

DerekParnell said...
jimcbrown said...

So if we used petelomax's suggestion of appenda()/prependa(), then there would be no problem here?

Ugh! No, please.

We would have append()/prepend() when the first argument must only be a sequence and appenda()/prependa() when the first argument can be any object. That arrangement, I predict, would be more confusing and more likely to introduce errors in code, plus people would tend to just use the newer functions anyway.

Agreed, that was certainly not one of my better suggestions.

jimcbrown said...

Perhaps we should take a different tack. What is the compelling original reason for append() to have refused to accept atoms as the first parameter in the first place?

Absolutely. When Derek said something similar, it completely changed my mind, and 4 days later I still cannot think of a valid answer to that one.

Pete

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

68. Re: Discuss changing the behaviour of append()

petelomax said...

Would this not help? ...

Damn you Pete ... smile That's much better, 38% better in fact. And it's Euphoria Performance Tip #1, which is ...

Don't create sequences, especially temporary ones, if you don't have to.

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

69. Re: Discuss changing the behaviour of append()

Still trying to make sense of this topic. Here is a summary of a few ideas I see in this thread.

  • Is change going to happen?
  • How do I describe these chagnes?

_tom


From Euphoria 1.0 Documentation from 1993:

Concatenation of Sequences:

The & operator will concatenate two sequences into a longer sequence. e.g. {1, 2, 3} & {4, 5, 6} is {1, 2, 3, 4, 5, 6}. Atoms can also be concatenated: 6.5 & 9 is {6.5, 9}. {1, 2, 3} & 4 is {1, 2, 3, 4}.

Operations on Sequences:

	i = length(s)          -- length of sequence 
	s = repeat(x, a)       -- repeat x a times 
	s2 = append(s1, x)     -- append x to end of s1 
	s2 = prepend(s1, x)    -- prepend x at beginning of s1 

Twenty years later...OpenEuphoria wants to look like:

Concatenation of Objects:

The & operator will concatenate two objects into a longer sequence. e.g. {1, 2, 3} & {4, 5, 6} is {1, 2, 3, 4, 5, 6}. Atoms can also be concatenated: 6.5 & 9 is {6.5, 9}. {1, 2, 3} & 4 is {1, 2, 3, 4}.

Operations on Objects:

	i = length(x)          -- length of object 
	s = repeat(x, a)       -- repeat x a times 
	s2 = append(x1, x)     -- append x to end of x1 
	s2 = prepend(x1, x)    -- prepend x at beginning of x1 
  1. Keep things the way they were
  2. Extend Euphoria

Keep Things

  • change is bad
  • some suble changes in program behaviour if append is changed?
  • append was optimized for speed; do not sacrifice this speed
  • & is a "sequence creation" operator; therefore it makes sense that you can concatenate two atoms
  • append is a "sequence modifying" function; therefore it makes sense that you can not start with an atom

Extend Euphoria

  • length has already been changed
  • a complementary change given that length is now different
  • fits the existing pattern that & already has
  • promoting an atom a to a sequence {a} is a convenience that is expected because of the flexibility of Euphoria
  • usability is more valuable than interpreter speed
  • thinking in terms of objects is more powerful than thinking in terms of just sequences
new topic     » goto parent     » topic index » view message » categorize

70. Re: Discuss changing the behaviour of append()

I vote for the proposal to change the behavior of append() and prepend(). The impact should be minimal and it is clearer and more intuitive.

I like the original documentation, it's clear and concise. It should still be valid even with the proposed change.

Some things of note:

append(atom1, atom2) would be the same as atom1 & atom2
append(atom1, sequence1) would be the same as append({atom1}, sequence1)

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

71. Re: Discuss changing the behaviour of append()

jaygade said...

I vote for the proposal to change the behavior of append() and prepend(). The impact should be minimal and it is clearer and more intuitive.

I like the original documentation, it's clear and concise. It should still be valid even with the proposed change.

Some things of note:

append(atom1, atom2) would be the same as atom1 & atom2
append(atom1, sequence1) would be the same as append({atom1}, sequence1)

Ok, I've now implemented a prototype implementation at http://scm.openeuphoria.org/hg/euphoria/rev/b49d88483f04

There may be room to improve upon this, but at least it's a starting point. If we still want to do this, that is.

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

72. Re: Discuss changing the behaviour of append()

jimcbrown said...

Ok, I've now implemented a prototype implementation at http://scm.openeuphoria.org/hg/euphoria/rev/b49d88483f04

     3.1 --- a/source/execute.e	Tue Jun 10 20:31:17 2014 -0400 
     3.2 +++ b/source/execute.e	Sun Jun 15 18:33:11 2014 -0400 
     3.3 @@ -2965,7 +2965,11 @@ 
     3.4  	a = Code[pc+1] 
     3.5  	b = Code[pc+2] 
     3.6  	target = Code[pc+3] 
     3.7 +	if atom(val[a]) then 
     3.8 +	val[target] = append({val[a]}, val[b]) 
     3.9 +	else 
    3.10  	val[target] = append(val[a], val[b]) 
    3.11 +	end if 
    3.12  	pc += 4 
    3.13  end procedure 
That should be unnecessary, unless you need execute.e to run on previous versions of OE

Pete

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

73. Re: Discuss changing the behaviour of append()

petelomax said...

That should be unnecessary, unless you need execute.e to run on previous versions of OE

Agreed. The same is true of all the changes to execute.e in this branch. However, this is a prototype branch. The point is that users can check it out and immediately experiement with it.

So, all the changes are made to execute.e with the idea that they should run on the default branch while being able to demonstrate the new functionality.

(Well, that's part of it. The other part is that it's easier to prototype the changes in Eu in execute.e, get some test code running to validate the changes, then redo the changes in C afterwards and make sure that both versions produce the same output from the test code.)

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

74. Re: Discuss changing the behaviour of append()

Jim, just out of curiosity, did you consider this style of implementation? ...

if atom(val[a]) then 
    val[target] = val[a] & {val[b]} 
else 
    val[target] = append(val[a], val[b]) 
end if 
new topic     » goto parent     » topic index » view message » categorize

75. Re: Discuss changing the behaviour of append()

DerekParnell said...

Jim, just out of curiosity, did you consider this style of implementation? ...

if atom(val[a]) then 
    val[target] = val[a] & {val[b]} 
else 
    val[target] = append(val[a], val[b]) 
end if 

I didn't. Should I change it to this? Is it better than append({val[a]}, val[b]) ?

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

76. Re: Discuss changing the behaviour of append()

Lets change the behavior of operator[]:

atom a1 = 5 
for i = 1 to length(a1) do 
   ? a1[i] -- prints 5 
end for 

JUST KIDDING!

Seriously, this continued treatment of atoms as one-member sequences in more and more instances will only add to confusion. Changing behavior of things is low lying fruit. In some cases this will break new users. In EUPHORIA 5 != {5} but the standard library and builtins more and more treat them as if they are the same.

For example: If you do a match_replace with 'a' you will replace 'a' in a word without complaints but if you are purposely replacing one character in a string you ought to use find_replace and this kind of distinction should be taught everywhere for consistency.

When you try to replace a word in a list of words using match_replace, you would get burned. The lazy conversion here causes people to use a function wrong and learn it wrong. The word wont be replaced in the word because it is comparing the word with lists of words. One might argue good programmers would never make this mistake, however.

If you are using a data structure which contains a list of numbers, you are not going initialize the list to one number. If you know what you are doing, you might initialize it to 0 to signal it is not yet setup and later initialize it with a sequence of numbers. Setting this list to a single atom is an accident and changing append such that it hides this kind of problem is a mistake.

Shawn Pringle

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

77. Re: Discuss changing the behaviour of append()

SDPringle said...

Seriously, this continued treatment of atoms as one-member sequences in more and more instances will only add to confusion. Changing behavior of things is low lying fruit. In some cases this will break new users. In EUPHORIA 5 != {5} but the standard library and builtins more and more treat them as if they are the same.

I understand your concern, Shawn. I can see why one might come to this conclusion. So please indulge me to explain where I was coming from.

My proposed change is based around function-centric rather than data-centric thinking. Of course an atom is never a sequence. These datatypes are by definition mutually exclusive. However, I was thinking more along the lines of the activity that append()/prepend() is trying to do, the end result of its endeavours. Namely, add a single object to something to form a list. Currently that 'something' must be an existing list, and all I want to do is expand its usefulness to allow 'something' to be any object. The functionality of append()/prepend() would remain identical.

SDPringle said...

For example: If you do a match_replace with 'a' you will replace 'a' in a word without complaints but if you are purposely replacing one character in a string you ought to use find_replace and this kind of distinction should be taught everywhere for consistency.

This is a good example ... but maybe not of what you were trying to show. To me this is a good example of poor function naming. The difference between 'find' and 'match' in English is extremely subtle and most people would actually use them interchangeably. Maybe its just me, but I've been burned by this a number of times until I finally learned the Euphoria meanings off-by-heart.

Maybe they should have been named like "locate_an_item_and_replace" and "locate_a_tuple_and_replace" ... well you get the picture ...

The find_replace() is problematic on a few levels anyway. Consider ...

  result = find_replace('a', "This is a list of characters", "A") 
 
  -->  {'T','h','i','s', ' ', 'i', 's', ' ', {'A'}, ' ', 'l', 'i', 's', 't',  ... etc } 

find_replace() can be used to find a character and replace it with a word! Probably not was intended. And similarly with match_replace().

SDPringle said...

When you try to replace a word in a list of words using match_replace, you would get burned. The lazy conversion here causes people to use a function wrong and learn it wrong. The word wont be replaced in the word because it is comparing the word with lists of words. One might argue good programmers would never make this mistake, however.

Hmmm ... not so sure about that.

   object word = "abc" 
   object word_list = {"def", "abc", "ghi"} 
   object new_word = "xyz" 
   result = match_replace(word, word_list, new_word)  

Of course, if word_list was initialized to "def,abc,ghi" then it would fail. But I'd argue (taking the data-centric view point), that is a list of characters and not a list of words blink

SDPringle said...

If you are using a data structure which contains a list of numbers, you are not going initialize the list to one number. If you know what you are doing, you might initialize it to 0 to signal it is not yet setup and later initialize it with a sequence of numbers. Setting this list to a single atom is an accident and changing append such that it hides this kind of problem is a mistake.

Again, a good example. Especially of breaking the good programming design principle of never using a single data item to represent multiple concepts. A better design is to use two variables - one to indicate the state of the list and another for the actual list. But I digress ... ok, given this scenario, we have a program that doesn't check the state of the list before using it. That is to say, the program has a bug in it. Now consider, what if instead of append(), the program was using concatenate when using the list (because it needed to add multiple items rather than just one). In that case, the bug would still remain hidden. So should we consider changing concatenate to only add to sequences? It could be that, until learned, a new Euphoria programmer could assume that 1 & 2 & 3 gives 123 rather than {1,2,3}?

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

78. Re: Discuss changing the behaviour of append()

While we shouldn't confuse atoms with sequences, there are many times when it makes sense to accept any object. This is one of those cases.

Same with length of an atom being 1 while the length of the empty sequence is 0.

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

79. Re: Discuss changing the behaviour of append()

Derek, Thank you for taking the time to explain your rationale. As you pointed out everything about a language is learned after all. So, this is not about belief or facts but rather about feelings.

I feel that append should continue to be a function where the first argument is a sequence and the second an object. I have never written a EUPHORIA program and wished that append could accept() a character as the first argument. I sincerely believe you are the only person who has ever wished they could pass an atom to the first argument of this function.

Shawn

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

80. Re: Discuss changing the behaviour of append()

SDPringle said...

So, this is not about belief or facts but rather about feelings.

Hmm ... kind of ... the only empirical thing I can come up with, which is actually why I even bothered in the first place, is that if it were built into the append() function (i.e. C code) it would give better performance than using IL code. The built-in is already testing for the first argument's data-type so there's no change there, so the only difference would be that instead of crashing the application, it could efficiently build a new sequence using the first argument.

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

81. Re: Discuss changing the behaviour of append()

DerekParnell said...
SDPringle said...

So, this is not about belief or facts but rather about feelings.

Hmm ... kind of ... the only empirical thing I can come up with, which is actually why I even bothered in the first place, is that if it were built into the append() function (i.e. C code) it would give better performance than using IL code. The built-in is already testing for the first argument's data-type so there's no change there, so the only difference would be that instead of crashing the application, it could efficiently build a new sequence using the first argument.

I believe Derek is saying if we don't have this change, map:put with the APPEND argument will have to handle more cases specially in order to behave as intended. I am saying routines have this kind of strictness to data-types to help users. Especially new users who are learning the language. A new user who misuses this builtin currently gets a helpful error. I feel the if in doubt give an error is more in the spirit of EUPHORIA. Instead of garbage in - garbage out. It is garbage in - give a helpful error. Like I said before, it is subjective which is better.

I think there are better ways to improve EUPHORIA's performance.

Maybe we can handle the case specially where a variable is passed to a user defined function and the result of that function is assigned to that variable. It really shouldn't add a reference at all but just leave the reference count alone in this circumstance to avoid unnecessary copying. Another thing, what is the bottle neck that motivates library wrapping developers to create shims for libraries? Is it the cost of the allocate/poke/peek cycle? Is c_func too slow?

Shawn

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

82. Re: Discuss changing the behaviour of append()

We had a classic at work today:
We had been writing

S14 for Spring FY 13/14
X14 Christmas
A14 Autumn
T14 Tax

All clear and no problems.

Then it was decided that this was not good enough because they did not sort in order.

So this is changed to:

141
142
143
144.

What happened next was unfortunate.

Two appeals at one time, one file containing records for both, the same sub-codes for both, no communication.

We had campaign codes like 143A-MAR-12 and 144A-MAR-12, not exactly distinguishable.

I failed to see that there were 2 campaigns in the list (this was unexpected - like I had never seen this before). As a result all were imported to the database as campaign 144.

During the processing (in Excel) the records within the the sub-groups with an empty column between so the database IDs no longer matched the list IDs. (Two separate files not checked off against each other).

This was not detected: no one checked as this has not happened before.

This story has some relation to efficiency, cutting corners, etc.

I'm thinking
documentation: communication,
clear coding: meaningful names,
typing: one file per campaign.

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

83. Re: Discuss changing the behaviour of append()

If we look at the term append(), there's no obviousness, innately or on the surface, about what it should accept as parameters. The only thing we can draw from it is that we will get a sequence of items, whereby the needle is added to the end of the haystack. This needle can be sequence or atom, and the haystack can be sequence or atom, so long as the result is a sequence.

If it matters, I'm not against modifying the behavior of append() to accept (object,object).

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

84. Re: Discuss changing the behaviour of append()

Here is some code to test append:

-- given 
sequence s = {1,2,3,4,5,6,7,8,9,10} 
 
-- want 
--> {2,4,6,8,10}  that is reduce s to all even items 
--> use a recursive function to do this 
 
function isEven(atom x) 
    --common utility function 
    return remainder( x,2 ) = 0 
end function 
 
--------------------------------------------------------------- 
 
-- CASE I 
 
 
function extractEvens1( sequence list ) 
   if equal( list, {}) then 
      return {} 
    elsif isEven( list[1] ) then 
       return append( list[1], extractEvens1( list[2..$ ] ) ) 
    else 
        return extractEvens1( list[2..$] ) 
    end if 
end function  
 
? extractEvens1( s ) 
 
--error 
--> first argument of append must be a sequence  
 
 
 
-- CASE II 
 
-- respond to classic OE error message 
-- use &   
-- (since append can not be used) 
 
function extractEvens2( sequence list ) 
   if equal( list, {}) then 
      return {} 
    elsif isEven( list[1] ) then 
       return  list[1] &  extractEvens2( list[2..$ ] ) 
    else 
        return extractEvens2( list[2..$]) 
    end if 
end function  
 
? extractEvens2( s ) 
--> {2,4,6,8,10} 
--> get the "expected" result 
 
 
 
-- CASE III 
 
-- "change behavior of append" 
-- grab a slice 
 
function extractEvens3( sequence list ) 
   if equal( list, {}) then 
      return {} 
    elsif isEven( list[1] ) then 
       return  append( list[1..1] ,   extractEvens3( list[2..$ ] ) ) 
    else 
        return extractEvens3( list[2..$]) 
    end if 
end function  
 
? extractEvens3( s ) 
 
-- { 
--  2, 
--  { 
--    4, 
--    { 
--      6, 
--      { 
--        8, 
--        {10,{}} 
--      } 
--    } 
--  } 
--} 
 
sequence s3 = extractEvens3(s) 
include std/sequence.e 
s3 = stdseq:flatten(s3) 
? s3 
--> {2,4,6,8,10} 
 
-- CASE IV 
 
function extractEvens4( sequence list ) 
   if equal( list, {}) then 
      return {} 
    elsif isEven( list[1] ) then 
        if atom( list[1] ) then 
            list[1] = { list[1]} 
        end if 
       return  append( list[1] ,   extractEvens2( list[2..$ ] ) ) 
    else 
        return extractEvens4( list[2..$]) 
    end if 
end function  
 
? extractEvens4( s ) 
 
-- { 
--  2, 
--  {4,6,8,10} 
-- } 
-- an odd looking result 
  • CASE I: classic error message, I did something wrong.
  • CASE II: based on classic error message, I fix the program.
  • CASE III: try to "fix" append, I get a result in an indirect way.
  • CASE IV: try to "fix" append, output is now ugly.

The proposed change to O[ looks like

3.1 --- a/source/execute.e	Tue Jun 10 20:31:17 2014 -0400  
     3.2 +++ b/source/execute.e	Sun Jun 15 18:33:11 2014 -0400  
     3.3 @@ -2965,7 +2965,11 @@  
     3.4  	a = Code[pc+1]  
     3.5  	b = Code[pc+2]  
     3.6  	target = Code[pc+3]  
     3.7 +	if atom(val[a]) then  
     3.8 +	val[target] = append({val[a]}, val[b])  
     3.9 +	else  
    3.10  	val[target] = append(val[a], val[b])  
    3.11 +	end if  
    3.12  	pc += 4  
    3.13  end procedure  

Looks like this is a CASE IV solution

  • works for a simple case
  • result can be ugly

Explaining CASE I to newcomers to O[ is simple. Explaining CASE IV is difficult; hard to justify a possible ugly result.

_tom

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

85. Re: Discuss changing the behaviour of append()

_tom said...
-- CASE III 
 
    elsif isEven( list[1] ) then 
       return  append( list[1..1] ,   extractEvens3( list[2..$ ] ) ) 

This is non-sensical. This has the effect of creating a new sequence, even if list[1] is already a sequence. It is equivelant to:

    elsif isEven( list[1] ) then 
        if atom( list[1] ) then 
            list[1] = { list[1]} 
	else 
            list[1] = { list[1]} 
        end if 
_tom said...
-- CASE III 
sequence s3 = extractEvens3(s) 
include std/sequence.e 
s3 = stdseq:flatten(s3) 
? s3 
--> {2,4,6,8,10} 
  • CASE III: try to "fix" append, I get a result in an indirect way.

But this indirect way works for case IV as well.

-- CASE IV 
sequence s4 = extractEvens4(s) 
include std/sequence.e 
s4 = stdseq:flatten(s4) 
? s4 
--> {2,4,6,8,10} 
_tom said...

The proposed change to O[ looks like

3.1 --- a/source/execute.e	Tue Jun 10 20:31:17 2014 -0400  
     3.2 +++ b/source/execute.e	Sun Jun 15 18:33:11 2014 -0400  
     3.3 @@ -2965,7 +2965,11 @@  
     3.4  	a = Code[pc+1]  
     3.5  	b = Code[pc+2]  
     3.6  	target = Code[pc+3]  
     3.7 +	if atom(val[a]) then  
     3.8 +	val[target] = append({val[a]}, val[b])  
     3.9 +	else  
    3.10  	val[target] = append(val[a], val[b])  
    3.11 +	end if  
    3.12  	pc += 4  
    3.13  end procedure  

Yes, and (assuming I did it right) the change to the C runtime works identically.

_tom said...

Looks like this is a CASE IV solution

Agreed.

_tom said...

Explaining CASE I to newcomers to O[ is simple.

Explaining how it works is simple. Justifying the reasons behind it is not so easy.

If someone things that explaining away the reason for this behavior is easy, then please explain why, in original instance (i.e. before we have code and have to deal with backwards incompatibilities), should append() only allow the haystack to be a sequence?

_tom said...

Explaining CASE IV is difficult; hard to justify a possible ugly result.

extractEvens() seems to naturally fit better with concatenation, not append, regardless.

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

86. Re: Discuss changing the behaviour of append()

jimcbrown said...
_tom said...

Explaining CASE I to newcomers to O[ is simple.

Explaining how it works is simple. Justifying the reasons behind it is not so easy.

If someone things that explaining away the reason for this behavior is easy, then please explain why, in original instance (i.e. before we have code and have to deal with backwards incompatibilities), should append() only allow the haystack to be a sequence?

The original append functionality made sense, an atom by definition is autonomous and can't have something appended to it. An analogy, think of an atom as a point. You can't append another point to it because as soon as you do it is no longer a point. It becomes something else entirely, a line perhaps.

That said it was a little short sighted. One big strength of Euphoria is not having to remember to use the sequence append when appending to a sequence and the atom append when creating a sequence by putting a supplied atom into a new sequence and then appending another atom, and the object append when you are working with objects, etc.

So many other languages have this data type differentiation in their functions; either by function overloading or by multiple functions with different names that do the same basic thing but that take different parameter types. This is one reason other languages can be so hard to learn.

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

87. Re: Discuss changing the behaviour of append()

Whats left is to make sure that append, prepend, and insert are consistent.

I can't figure out a way to "break" the expanded viewpoint for append. As Jim Brown shows, non of the attemps are convincing.

As Mindwalker suggests, it makes more sense than the old way.

_tom

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

88. Re: Discuss changing the behaviour of append()

_tom said...

Here is some code to test append:

FYI (this does not really add anything to the subject being discussed), this is how I would code that (if I had to use recursion):

sequence s = {1,2,3,4,5,6,7,8,9,10}  
  
function isEven(atom x)  
    --common utility function  
    return remainder( x,2 ) = 0  
end function  
  
function extractEvens(sequence source, integer done=0, sequence thusfar={})  
    done += 1 
    if done>length(source) then return thusfar end if 
    atom thisone = source[done] 
    if isEven(thisone) then 
        thusfar = append(thusfar,thisone) 
    end if  
    return extractEvens(source, done, thusfar) 
end function   
 
? extractEvens(s) 
 
new topic     » goto parent     » topic index » view message » categorize

89. Re: Discuss changing the behaviour of append()

Just a question:

Assuming that appending/prepending to atoms is on
we have:

? append(1,{})  -- {1,{}} 
? append({},1)  -- {1} 
? prepend(1,{}) -- {{},1} 
? prepend({},1) -- {1} 

I'm not saying it's wrong but don't you find the difference strange?

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

90. Re: Discuss changing the behaviour of append()

gimlet said...

Just a question:

Assuming that appending/prepending to atoms is on
we have:

? append(1,{})  -- {1,{}} 
? append({},1)  -- {1} 
? prepend(1,{}) -- {{},1} 
? prepend({},1) -- {1} 

I'm not saying it's wrong but don't you find the difference strange?

No I don't, because the result is identical to when the first argument is a sequence ...

-- Ensuring that the first parameter is a sequence. 
? append({1},{})  -- {1,{}}  
? append({},1)  -- {1}  
? prepend({1},{}) -- {{},1}  
? prepend({},1) -- {1}  
 
-- Performing the 'equivalent' functionality using concatenate 
? {1} & {{}} 
? {} & {1} 
? {{}}& {1} 
? {1} & {} 

Or putting the operation into English text ...

  • Create a list such that the second argument is added, as a single element, to the set of elements in the first argument.
new topic     » goto parent     » topic index » view message » categorize

91. Re: Discuss changing the behaviour of append()

_tom said...

Here is some code to test append:

-- given 
sequence s = {1,2,3,4,5,6,7,8,9,10} 
 
-- want 
--> {2,4,6,8,10}  that is reduce s to all even items 
--> use a recursive function to do this 

Tom,
the append() function is not the appropriate functionality to use in this case because it appends the second argument as a single element to the result, and if the extract function is returning a list of things, then using its returned object in this manner is not what is required. The concatenate operator is a better choice for this exercise. To use append(), the extract function would have to return only a single element from the original list each time it was called.

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

92. Re: Discuss changing the behaviour of append()

Derek,

What I was getting at is this (and we agree this is not about a processing error).

An argument could be made that:

  append(1,{})  -- {1,{}}  
  prepend(1,{}) -- {{},1}  

ought to be identical as {} is empty. (We assume the return value is a sequence).

i.e. it should not matter if you prepend or append an empty sequence you have added nothing.

Mathmatically this means: {} is the identify under append. which would mean:

{} append A   ==   A append {}  -- == equivalence 

I certainly could understand people reading things this way.

I know it doesn't fit with the mechanics of Eu.

Appending an empty slice might appear as an error.

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

93. Re: Discuss changing the behaviour of append()

gimlet said...

An argument could be made that:

  append(1,{})  -- {1,{}}  
  prepend(1,{}) -- {{},1}  

ought to be identical as {} is empty. (We assume the return value is a sequence).

i.e. it should not matter if you prepend or append an empty sequence you have added nothing.

Ahh... I see what you were trying to get across now.

However, "you have added nothing" is not accurate. The append()/prepend() functions add the second argument as a whole to form the new sequence element. An empty list is still a list; it is a something; an object. The append()/prepend() functions do not add the contents of a list to the first argument, but the entire object itself. For example ...

  append({1},  {1})  --> gives {1, {1}} and not {1, 1} 
  prepend({1}, {1})  --> gives {{1}, 1} and not {1, 1} 
--thus-- 
  append({1},  {})   --> gives {1, {}} and not {1} 
  prepend({},  {1})  --> gives {{}, 1} and not {1} 

In all cases, append(X,Y) is identical in result to X & {Y} and prepend(X,Y) is identical in result to {Y} & X so that gives ...

  object X, Y, Z 
 
  X = {1} 
  Y = {} 
  Z = append(X,  Y)  --> gives {1, {}} 
  Z = X & {Y}        --> gives {1, {}} 
  Z = prepend(X,  Y)  --> gives {{}, 1} 
  Z = {Y} & X        --> gives {{}, 1} 
gimlet said...

Mathmatically this means: {} is the identify under append. which would mean:

{} append A   ==   A append {}  -- == equivalence 

I certainly could understand people reading things this way.

I know it doesn't fit with the mechanics of Eu.

If append(1,{}) were to give {1}, then it must follow that append(1, {2,3}) should give {1,2,3}, and that is not what it is intended to do. Likewise if prepend(1,{}) were to give {1}, then it must follow that prepend(1, {2,3}) should give {2,3,1}, and again, that is not what it is intended to do.

gimlet said...

Appending an empty slice might appear as an error.

There is effectively nothing different between an empty slice and an empty sequence.

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

94. Re: Discuss changing the behaviour of append()

Not wanting this to degenerate.

From the Haskell wikibook:

Haskell wiki said...

In earlier parts of the book, we have made a few passing allusions to monoids and the Monoid type class ... Here we'll give them a more detailed look and show what makes them useful. Introduction

A monoid (m, mappend, mempty) is a type m together with an associative operation mappend :: m -> m -> m (also called (<>) in Haskell) that combines two elements and a zero element mempty :: m which is the neutral element of mappend: Before describing them more formally, let's see monoids in action. Examples

As an information introduction, let's take a look at a common pattern:

> (5 + 6) + 10 == 5 + (6 + 10) 
True 
> (5 * 6) * 10 == 5 * (6 * 10) 
True 
> ("Hello" ++ " ") ++ "world!" == "Hello" ++ (" " ++ "world!") 
True 

This property is called associativity, and it doesn't hold for only those selected values but for all integers under addition, all integers under multiplication, and all lists under concatenation.

Here's another type of pattern:

> 255 + 0 == 255 && 0 + 255 == 255 
True 
> 255 * 1 == 255 && 1 * 255 == 255 
True 
> [1,2,3] ++ [] == [1,2,3] && [] ++ [1,2,3] == [1,2,3] 
True 

Here 0 is the identity element when adding integers,
1 is the identity when multiplying them, and,
[] is the identity when appending two lists.

So:

Integers form a monoid under addition where 0 is the unit: (Integer, (+), 0) Integers form a monoid under multiplication where 1 is the unit: (Integer, (*), 1) Lists form a monoid under concatenation: ([a], (++), []) ...

Take a function that concatenates three lists:

threeConcat :: [a] -> [a] -> [a] -> [a] threeConcat a b c = a b c

We can generalize this function to work with any monoid:

threeConcat' :: Monoid m => m -> m -> m -> m threeConcat' a b c = a <> b <> c

  threeConcat' "Hello" " " "world!"     -- "Hello world!" 
  threeConcat' (Sum 5) (Sum 6) (Sum 10) -- Sum {getSum = 21} 

Other functions like fold :: (Foldable t, Monoid m) => t m -> m from Data.Foldable use properties of monoids to reduce any foldable structure containing monoids into a single monoidal value:

 fold ["Hello", " ", "world!"] -- "Hello world!" 
> fold (Just (Sum 10))         -- Sum {getSum = 10} 
> fold Nothing :: Sum Integer  -- Sum {getSum = 0} 

My point is not that Euphoria should be more Haskell-like but that the monoid makes it easy reasoning about functions.

Our append is strange this way. It has no identity.

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

95. Re: Discuss changing the behaviour of append()

gimlet said...

From the Haskell wikibook:

Haskell wiki said...

This property is called associativity, and it doesn't hold for only those selected values but for all integers under addition, all integers under multiplication, and all lists under concatenation.

[] is the identity when appending two lists.

Lists form a monoid under concatenation: ([a], (++), [])

Other than one lapse, the term concatenation is used consistently.

gimlet said...

Our append ... has no identity.

Agreed.

gimlet said...

Our append is strange this way. It has no identity.

Well, I think this is not so strange. Lots of functions lack an identity. f(x) = x + 1 lacks an identity. Logical NOT lacks an identity.

Furthermore, this has nothing to do with the proposed change. Even without it, append lacks an identity. To demonstrate this with the most likely canidate, append({}, {}) does not return {}.

append also lacks associativity. append(1, {}) and append({}, 1) return different things. Again, this is nothing to do with the proposed change - append({1}, {2}) and append({2}, {1}) also return different things. Other mathematical functions, like division or subtraction, also lack associativity.

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

96. Re: Discuss changing the behaviour of append()

Jim,

So what? What are you trying to prove?
All you are doing is confirming the obvious.

   a + b == b + a 
   a + 0 == 0 + a 

is what I am talking about.

Are you listening?

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

97. Re: Discuss changing the behaviour of append()

gimlet said...

Jim,

So what? What are you trying to prove?
All you are doing is confirming the obvious.

   a + b == b + a 
   a + 0 == 0 + a 

is what I am talking about.

Are you listening?

Are you?

jimcbrown said...

append also lacks associativity. append(1, {}) and append({}, 1) return different things. Again, this is nothing to do with the proposed change - append({1}, {2}) and append({2}, {1}) also return different things. Other mathematical functions, like division or subtraction, also lack associativity.

To borrow your example,

   a + b = b + a 
   a + 0 = 0 + a 

Also see that,

   a / b = b / a 
   a / 1 = 1 / a 

Oh, wait a minute....

gimlet said...

is what I am talking about.

Actually, you contradict yourself. You state,

gimlet said...
   a + b == b + a 
   a + 0 == 0 + a 

is what I am talking about.

as well as

gimlet said...

From the Haskell wikibook:

This property is called associativity,

but you also say

gimlet said...

Our append is strange this way. It has no identity.

These (lack of associativity and lack of identity) are two different things.

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

98. Re: Discuss changing the behaviour of append()

So, Jim,

Associativity doesn't require identity?
Is that what you are saying?

Well, no it doesn't - did I say it did?
But "Hello World!" & "" = "Hello World!"
or didn't you notice?

If A append B is not a monoid then|| (OK: you have your own (rather (beautiful??) operator))
But what have you proved to the world?

I think Jim,
that what you have proved is that (mostly, you are talking BS).

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

99. Re: Discuss changing the behaviour of append()

gimlet said...

So, Jim,

Associativity doesn't require identity?
Is that what you are saying?

I didn't say that - but it's true: http://en.wikipedia.org/wiki/Semigroup

Haskell has a list concatenation operator that has both of these properties, as does Euphoria's own concatenation operator. Euphoria's append has neither. I don't find this state of affairs particularly strange - concatenation naturally has these properties, but the function we define as append (which can be simulated by concatenation) does not.

gimlet said...

Well, no it doesn't - did I say it did?

Since you contradict yourself, it's very hard to determine what you are saying.

gimlet said...

But "Hello World!" & "" = "Hello World!"
or didn't you notice?

You keep comparing append to concatenation. If you are trying to state that append should behavior identically to concatenation, please state so explicitly, not in this roundabout way.

If you disagree that append should behavior identically to concatenation, I'd like to see some examples where you believe they ought to behave differently.

gimlet said...

If A append B is not a monoid then|| (OK: you have your own (rather (beautiful??) operator))

Hardly. Appending is basic operation involving any kind of list structure. I can't think of a single programming language that lacks a function or method to implement this functionality. (Maybe Haskell.)

gimlet said...

But what have you proved to the world?

I think Jim,
that what you have proved is that (mostly, you are talking BS).

This is implicitly an ad hominen argument and can be dismissed as such.

I'll restart my original argument for clarity: Since there are many mathematical functions which lack both the associativity and identity properties, I do not find it at all strange that Euphoria's append routine lacks these properties as well.

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

100. Re: Discuss changing the behaviour of append()

[quote jb] gimlet said...

So, Jim,

Associativity doesn't require identity? Is that what you are saying?

I didn't say that - but it's true: http://en.wikipedia.org/wiki/Semigroup

Haskell has a list concatenation operator that has both of these properties, as does Euphoria's own concatenation operator. Euphoria's append has neither. I don't find this state of affairs particularly strange - concatenation naturally has these properties, but the function we define as append (which can be simulated by concatenation) does not. gimlet said...

Well, no it doesn't - did I say it did?

Since you contradict yourself, it's very hard to determine what you are saying. gimlet said...

But "Hello World!" & "" = "Hello World!" or didn't you notice?

You keep comparing append to concatenation. If you are trying to state that append should behavior identically to concatenation, please state so explicitly, not in this roundabout way.

If you disagree that append should behavior identically to concatenation, I'd like to see some examples where you believe they ought to behave differently. gimlet said...

If A append B is not a monoid then|| (OK: you have your own (rather (beautiful??) operator))

Hardly. Appending is basic operation involving any kind of list structure. I can't think of a single programming language that lacks a function or method to implement this functionality. (Maybe Haskell.) gimlet said...

But what have you proved to the world?

I think Jim, that what you have proved is that (mostly, you are talking BS).

said...

This is implicitly an ad hominen argument and can be dismissed as such.

I'll restart my original argument for clarity: Since there are many mathematical functions which lack both the associativity and identity properties, I do not find it at all strange that Euphoria's append routine lacks these properties as well.

I didn't say that - but it's true: http://en.wikipedia.org/wiki/Semigroup

So Jim,

I contradict myself.

But "Hello World!" & "" = "Hello World!" or didn't you notice?

You keep comparing append to concatenation. If you are trying to state that append should behavior identically to concatenation, please state so explicitly, not in this roundabout way.

Jim, you are so sure!

1 Why should append be different? Append is supposed to be fast and (&) slow. So Jim, what are you saying, that I should not expect them to behave similarly? That I must expect a slow program because I asked for correct behaviour?

That because Euphoria's append is not a monoid that I expect all functions to be monoids?

And on the ground that many FUNCTIONS are not monoids you argue... what?

Jim, I am rather of the opinion that you don't know what you are talking about.

So how much maths did you do? To what purpose?

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

101. Re: Discuss changing the behaviour of append()

gimlet said...

Not wanting this to degenerate.

LOL ... how's that working out for you?

gimlet said...

My point is ... that the monoid makes it easy reasoning about functions.

In what ways do "the monoid makes it easy reasoning about functions"?

gimlet said...

Our append is strange this way. It has no identity.

Oh well ... c'est la vie.

Euphoria's append() works, its useful, and it's used. I've got no problem with the concept behind it. Should I have?

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

102. Re: Discuss changing the behaviour of append()

gimlet said...

(mostly, you are talking BS).

Well ... that escalated quickly!

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

103. Re: Discuss changing the behaviour of append()

gimlet said...

And on the ground that many FUNCTIONS are not monoids you argue...

Jim, I am rather of the opinion that you don't know what you are talking about.

So how much maths did you do?

Again, I get the impression that if you believe that probability is BS then you you didn't go very far.

More ad hominen attacks and now a Loaded Statement ...

This post lacks any new facts or a valid logical argument. I think we've crossed the line into personal attacks here.

Another post like this, and I'm placing you under "extraordinary measures". If you can't play nice, then don't play at all.

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

104. Re: Discuss changing the behaviour of append()

OK Jim,

And I suppose I have no recourse.

Bye.

I hope the others agree with you.

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

105. Re: Discuss changing the behaviour of append()

DerekParnell said...
gimlet said...

My point is ... that the monoid makes it easy reasoning about functions.

In what ways do "the monoid makes it easy reasoning about functions"?

gimlet said...

Our append is strange this way. It has no identity.

Oh well ... c'est la vie.

Euphoria's append() works, its useful, and it's used. I've got no problem with the concept behind it. Should I have?

I think Shawn hit it on the nail when he said ( http://openeuphoria.org/forum/m/124652.wc ) that it wasn't about facts but feelings. Changing append to be monoid (whatever that would look like) is one of these (unlike the original topic, where the change is backed up by facts about performance optimization and code/algorithm simplication by removing edge cases).

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

106. Re: Discuss changing the behaviour of append()

Jim, Shawn, Derek,

I haven't been making ad hominem attacks.

My criticism of Jim is criticism of his attacks on me.
He thinks that because I said something about monoids I wanted changes.
I had repeatedly said I didn't (and Derek, I have kept my silence except
in the face of Jim's attacks).

Personally, I don't care much any more, as it is clear that Euphoria is not going to be usable for what I want. And no, that is not sour grapes, just the truth. If load_map doesn't work then I need to use GAWK (and these days I can get up to date versions for Windows so I'm happy).

Jim outed me of course.

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

107. Re: Discuss changing the behaviour of append()

gimlet said...

That because Euphoria's append is not a monoid that I expect all functions to be monoids?

1 Why should append be different?

So Jim, what are you saying, that I should not expect them to behave similarly?

Exactly. append and concatenation are two different things, two different functions. The same way that f(x) = x + 1 and g(x, y) = x + y are two different functions. They simply do different things and therefore do not behavior similarily.

gimlet said...

That I must expect a slow program because I asked for correct behaviour?

The fact that concatenation is slower (in the general case only) is irrelevant. Even if it were faster than append, the two functions still are two different functions. (Incidently, when concatenating an atom and a sequence, concatenation is implemented by calling either append or prepend (in the later case swapping the arguments), depending on the order of the arguments. So concatenation is not always slower.)

gimlet said...

And on the ground that many FUNCTIONS are not monoids you argue... what?

That append is not strange, as you originally posed:

gimlet said...

I'm not saying it's wrong but don't you find the difference strange?

gimlet said...

OK Jim,

And I suppose I have no recourse.

Bye.

I'd like to say good riddance, but I have a feeling that this wouldn't be the case: http://openeuphoria.org/forum/m/118296.wc http://openeuphoria.org/wiki/view/forum-msg-id-124160-edit.wc

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

108. Re: Discuss changing the behaviour of append()

gimlet said...

Jim, Shawn, Derek,

I haven't been making ad hominem attacks.

Denial won't help you.

gimlet said...

He thinks that because I said something about monoids I wanted changes.
I had repeatedly said I didn't

I never stated this. I did call a "proposed change" making append gain the properties of associtivity and identity, but this was just to emphasis that today, without Derek's idea of allowing atoms in the first argument of append, append still lacks those properties. It always did.

gimlet said...

If load_map doesn't work

I asked you before what about load_map() was broken: http://openeuphoria.org/forum/m/124614.wc

You didn't answer then, which now makes me think that it's because you can't.

gimlet said...

Jim outed me of course.

I have no recollection of this. I admit that I'm not sorry to see you go, but, wait, you are still here!

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

109. Re: Discuss changing the behaviour of append()

So you don't remember:

You went out of your way to ...

Jim Brown said...

gimlet said...

Yet you then tell me what I already know (and don't understand).

I think it's better to be a little repetitive to establish common ground and make sure all sides are discussing the same topic, than to make assumptions about what the other side knows and doesn't know. gimlet said...

This may not be couth:

Agreed. Quoting a post that is entirely unrelated to what you are saying and then double posting both are not couth. gimlet said...

every time I say 'I don't understand this' or, 'I don't think this is right' I am assumed to stupid.

This is a real pain: you are saying I am stupid.

I can't recall anyone saying this. Still, by your use of the generic 'you', you seem to think that everyone is saying so. If you believe that everybody thinks this about you, regarding such an intimate state about yourself, then - regardless of what everybody is truly thinking - perhaps there is some truth to the matter.

I mean, you should know if you are stupid or not, right?

I previously said that one shouldn't be afraid to ask questions here, and that's still true. Unfounded accusations, however, are not welcome. gimlet said...

Because you make up your mind instantly you miss important things.

If this is the case for "everybody", then maybe it's you who is missing something.

For example, to quote myself, from

http://openeuphoria.org/forum/m/124133.wc jimcbrown said...

please demonstrate a use case where it is necessary to preserve carriage returns.

Still waiting on an answer for that.

I also said, jimcbrown said...

Even so, we might want to add a with-statement option to control this behavior,

but, I'm not sure if it's worth the effort if there's no known use case for it. I'm willing to compromise but I'm going to need a little more first. gimlet said...

So what use are you to me?

What use are you to me? What use are you to the rest of the community? No one is getting paid for this. If this isn't fun for us, then why should anyone have long pointless debates with you about irrelevant details? gimlet said...

Bill

I thought this was your final message: http://openeuphoria.org/forum/m/118296.wc

And what is that?

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

110. Re: Discuss changing the behaviour of append()

gimlet said...

So you don't remember:

You went out of your way to ...

jimcbrown said...

I think it's better to be a little repetitive to establish common ground and make sure all sides are discussing the same topic, than to make assumptions about what the other side knows and doesn't know.

So this is in reference to a post on a different thread, where I stated:

jimcbrown said...

Anyways, this load_map() argument is becoming quite repetitive. Unless you (or anyone else) has any new evidence, I think we should just agree to disagree and drop the subject.

at http://openeuphoria.org/forum/m/124693.wc

Yes, it's constructive to be a little repetitive to make sure both sides are talking about the same thing, but once that's been established, it's still possible to become too repetitive, rehashing the same arguments over and over, post after post.

gimlet said...
jimcbrown said...
gimlet said...

Bill

I thought this was your final message: http://openeuphoria.org/forum/m/118296.wc

And what is that?

I thought it'd be self-evident.

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

111. Re: Discuss changing the behaviour of append()

gimlet said...

Just a question:

Assuming that appending/prepending to atoms is on
we have:

? append(1,{})  -- {1,{}} 
? append({},1)  -- {1} 
? prepend(1,{}) -- {{},1} 
? prepend({},1) -- {1} 

I'm not saying it's wrong but don't you find the difference strange?

Thanks to Gimlet, the documentation has hopefully been improved:

I think you can choose to view this as strange or as not strange. From there you can design two forks of Euphoria.


Consider append and prepend to be special cases of the insert function.

The proposed changes to O[ are going to put emphasis on object behavior and blur some previous atom-sequence distinctions. While a != {a} remains true, the new behavior tolerates the idea that a becomes {a} when it is convenient in a prepend, insert, or append.

TARGET is an atom, like  1, is promoted to { 1   ,$} using prepend, insert, append  
 
prepend    append 
      V    V 
   {    a     ,$ }   -- all sequences can be written using the ,$ end marker 
      ^    ^     
      1    2   -- insert index value 
                  -- 1 is like prepend 
                  -- 2 is like append 
 
 
when inserting a empty sequence into an "promoted" atom the results are     
{ {}, 1}   {1, {} }  
     

TARGET is an empty sequence, like {}, can be written as {$}     
 
prepend    append 
      V    V 
   {     $   }   -- an empty sequence can be written using the $ end marker 
      ^    ^     
      1    2   -- insert index value 
                  -- 1 is like prepend 
                  -- 2 is like append 
 
when inserting an atom into an empty sequence the results are     
{ 1 }   { 1 }  
     

The length function is now oddly named. It could have been named item_count (items, count, size, topsize, ... ?)

  • on its own {} has a zero item count, but inside a sequence it is itself one item
  • length( { } ) or length( "" ) is zero, this does not mean a null sequence, but a sequence with no item count
  • length( a ) is one, this does not mean a number has "length", but that one atom is one item
  • length( {a} ) = length( a ) means there is one item count in each example

Concatenation combines objects into one. The total item count is equal to the sum of the individual item counts.

Insert, prepend, or append extends the item count by exactly one.

The results of an insertion follow a consistent pattern; from that basis the results are not strange.

If the results seem "strange" is it because an empty sequence can be both paradoxically "length zero and one item"? Note that quantum physics copes with ideas like this.

Another consideration is that syntax is not mathematics. Sometimes syntax is based on mathematics (consider lisp, haskel), sometimes syntax just looks like mathematics (consider the assignment operator = which is better represented by :=), and sometimes syntax just happens when a language is designed.

You must always believe in the documentation, the basis of all truth, and disregard all previous knowledge; documentation in progress see above. (If you are not in a humorous mood, ignore the previous sentence.)

_tom

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

112. Re: Discuss changing the behaviour of append()

Give me documentation that is right and code that is poorly written and you have bugs in the code. Give me documentation that is wrong and code that is well written and you have bugs in the code because documentation is the promise and the code is the execution of that promise.

Shawn

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

113. Re: Discuss changing the behaviour of append()

SDPringle said...

Give me documentation that is right and code that is poorly written and you have bugs in the code. Give me documentation that is wrong and code that is well written and you have bugs in the code because documentation is the promise and the code is the execution of that promise.

Shawn

So, how close am I getting to understandable documentation that tells the truth?

I welcome bits and pieces of documentation (let them be rough, let them be just ideas). A community project is not a one person job.

_tom

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

114. Re: Discuss changing the behaviour of append()

Please don't refer to 'quantum physics' in the documentation. Let's avoid referring to something so counterintuitive in a document that describes something so simple and intuitive. Please don't use smilies in the documentation.

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

115. Re: Discuss changing the behaviour of append()

I think it's a mistake to refer to it as a promotion of an atom to a sequence.

<built-in> function append(object target, object x) 

Adds object x as the last item in target.

Returns:

A sequence whose first elements are those of target and whose last element is x.

Comments:

The length of the resulting sequence will be length(target) + 1, no matter what x is.

If x is an atom this is equivalent to result = target & x. If x is a sequence it is not equivalent. If target is an atom then it becomes the first element of the new sequence.


Then add in some of the new examples, but probably no more than 3 to 4. It might be useful to unify the examples given between this post, the & operator, and prepend.

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

116. Re: Discuss changing the behaviour of append()

_tom said...

Consider append and prepend to be special cases of the insert function.

Interesting point of view ... and probably a useful one too in some circumstances. I've mainly regarded append/prepend as special cases of concatenate.

_tom said...

The proposed changes to O[ are going to put emphasis on object behavior and blur some previous atom-sequence distinctions. While a != {a} remains true, the new behavior tolerates the idea that a becomes {a} when it is convenient in a prepend, insert, or append.

Well ... except that "a becomes {a}" never actually happens. An atom does not become a sequence during those operations. Its more that these operations will work on objects rather than just atoms or sequences.

_tom said...

TARGET is an atom, like  1, is promoted to { 1   ,$} using prepend, insert, append  

I'm not sure the term TARGET is correct, maybe RESULT? Also, I'm not sure if anything is promoted either.

Also, the $ symbol, in this context, is used as a LIST construction device and has nothing whatsoever to do with sequences.

_tom said...

The length function is now oddly named. It could have been named item_count (items, count, size, topsize, ... ?)

  • on its own {} has a zero item count, but inside a sequence it is itself one item
  • length( { } ) or length( "" ) is zero, this does not mean a null sequence, but a sequence with no item count
  • length( a ) is one, this does not mean a number has "length", but that one atom is one item
  • length( {a} ) = length( a ) means there is one item count in each example

Yes, but I guess we are stuck with it now. Evolution, eh?!

_tom said...

If the results seem "strange" is it because an empty sequence can be both paradoxically "length zero and one item"? Note that quantum physics copes with ideas like this.

There is no paradox here. An empty cup is still one thing and contains nothing. A sequence is just a container. It can be empty (length = 0) while still having an existence (is an object).

_tom said...

Another consideration is that syntax is not mathematics. Sometimes syntax is based on mathematics (consider lisp, haskel), sometimes syntax just looks like mathematics (consider the assignment operator = which is better represented by :=), and sometimes syntax just happens when a language is designed.

I like this explanation.

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

Search



Quick Links

User menu

Not signed in.

Misc Menu