1. Problems with structures
- Posted by "Boehme, Gabriel" <gboehme at MUSICLAND.COM> Feb 04, 1999
- 467 views
Many of the responses to my original post are still overlooking some fundamental problems. I'm going to use more examples here, to help clarify my points. I warn you, this is *long*. But the detail is necessary. I agree that something *like* structures is needed and would be very useful in Euphoria, for very much the same reasons they're useful in other languages. An example from QuickBasic: TYPE Card Suit AS STRING * 9 Value AS INTEGER END TYPE DIM Deck(1 TO 52) AS Card Deck(1).Suit = "Club" Deck(1).Value = 2 PRINT Deck(1).Suit, Deck(1).Value As you can see, it's very easy to understand what's going on here. You have a deck of 52 cards, identified here by suit and value. You don't have to define constants to use as subscripts (i.e. Suit = 1, Value = 2) -- you can give the individual portions field names to uniquely identify them. Plus, the compiler will *always* know if a field reference is valid or not, and can tell you right away if you've made a mistake. If I were to try this... DIM Hand(1 TO 7) AS STRING * 9 Hand(1).Suit = "Spade" ..the compiler would immediately flag it as an error -- the "Hand" array is not defined to contain "Card"-type data, so the ".Suit" reference has no meaning here. The same goes for all the other languages containing structures -- since every data element's type *must* be defined beforehand, the compiler can know instantly if a field reference is valid or not. Now, let's look at a Euphoria-style example, based on the above. [Note: in all my examples, I will use "?" to indicate general screen output, rather than the specific "?" statement.] -- we'll assume the string_9() type is already defined as a maximum-length-9 sequence of integer values structure Card string_9 Suit integer Value end structure sequence Deck of Card -- we'll ignore for the moment the problem of initialization... Deck[1].Suit = "Club" Deck[1].Value = 2 ? Deck[1] This seems just fine, doesn't it? Just throw in structure definitions and "of" statements and you're all set! It sure *seems* to simplify things -- and if you don't like it, just don't use structures! That's pretty much the pro-structure viewpoint, and it seems quite reasonable at first glance. However, these examples (and most others I've seen) are deeply flawed, because they are all based on examples from other languages that *only* have rigid definitions for variables. They don't deal with the fundamentally different way that Euphoria treats its variables. First of all, look at the apparently harmless syntax of "sequence Deck of Card". Heck, it almost reads like English, doesn't it? However, this now marks a *major* departure for the definition of objects. Before, the only restriction on an object was its type -- be it sequence, integer, user-defined types, whatever. With this new syntax, however, an object can be *doubly* restricted -- both in type and structure. Imagine if someone tried to do this: string_9 Hand of Card What is the interpreter to do? Yes, the "string_9" type conflicts with the "Card" structure, but the interpreter won't know that until "Hand" is initialized -- at *runtime*. This can cause much confusion, since a definition like this won't cause a "compile" error. I suppose we could restrict the "of" clause to be used only with "sequence" declarations, but then what if we have our own user-defined sequence types that we *want* to use with "of"? Like this: my_sequence Hand of Card There will most likely be occasions where we would like to do this. But there's no way for Euphoria to know if there's a conflict between type and structure until the program actually runs, and the first value is assigned to "Hand". In any event, we would have two *very* different ways of restricting object values. This does not strike me as being "elegant". Secondly, examples such as these don't deal adequately with generic Euphoria objects. Most of the structure ideas I've seen show how we could define and use the structures, but don't really examine how *normal* Euphoria would work with them. What if I try this: object one_card one_card = Deck[1] ? one_card.Suit ? one_card.Value It seems simple enough at first glance -- the object "one_card" is assigned the value from "Deck[1]", which is in "Card" format. So naturally the field references would make sense. But how does the Euphoria "compiler" know for sure what's in "one_card"? How does it know that "one_card.Suit" is a valid field reference? After all, "one_card" is a generic object. There's no guarantee that it will *always* contain "Card"-structured data. In theory, I suppose the Euphoria "compiler" could try to get really clever -- it knows that "Deck[1]" contains "Card"-structured data, so it "allows" the field references to be attached to the object "one_card" in this particular case. In practice, however, this would be a *nightmare* to implement, especially if the value is passed back via a function: function get_card() return Deck[1] end function object one_card one_card = get_card() ? one_card.Suit ? one_card.Value The Euphoria "compiler" would have to know that function get_card() always returns a "Card"-structured object! Now imagine hundreds of structure definitions, and thousands of functions all returning different structures -- poor old Euphoria would have to chase down all this extra information at "compile" time just so it knows if the field references are valid. This is clearly ridiculous. "Compile"-time field-reference checks in Euphoria -- as depicted here -- are impossible. This means that field references would *only* be resolvable at runtime. This would be a major step backwards -- normally Euphoria catches all sorts of things other languages force you to hunt down for yourself, but in this case the *other* languages would catch field references at compile time that Euphoria would force you to hunt down yourself at runtime! In other words, the Euphoria "compiler" would have to allow code like this to pass through: object one_card one_card = 1 ? one_card.Suit As anyone can see, this is ridiculous. There is no way that "one_card" can possibly contain "Card" data here. But "one_card" has the *potential* of holding "Card"-structured data, so this *must* be allowed to pass. This will cause an error at runtime, of course, but the point is that this shouldn't be allowed past the "compile" step in the first place. So, instead of allowing *that* can of worms to be opened, perhaps we ought to say that field references are *only* valid on objects *declared* for that specific structure. So, for the above examples, the programmer would be forced to declare "one_card" like this: Card one_card Then the rest of the code follows naturally -- the Euphoria "compiler" knows that "Suit" and "Value" are valid field-names for "one_card", because it is explicitly defined as a "Card"-type variable. This seems to solve our earlier problems, since the "compiler" doesn't have to do any extra work -- it always *knows* if the field references are valid or not. However, forcing the programmer to define "one_card" in this manner creates two problems. Firstly, it dilutes the universal nature of a generic "object" -- the object-defined variable can "hold" the data, but can't access it. (I suppose it *could* access the raw data -- the sequence elements, or whatever -- but it couldn't use the meaningful field names.) Secondly, forcing the programmer to specifically define the structure of the returning data can be quite a headache, and defeats many of the advantages of using Euphoria to begin with. It returns us to the QuickBasic way of doing things -- we *must* define our receiving variable correctly to get meaningful data. For example, let's say I have a function in my card-playing program like this: sequence Hand of Card function draw_card_of_suit(string_9 suit) -- determine if we have a card of this suit -- if yes, return the card; if no, return 0 for i = 1 to length(Hand) do if equal(Hand[i].Suit, suit) then -- we have a card of this suit! return Hand[i] end if end for -- we don't have a card of this suit return 0 end function Now, ideally, I would like to do something like this: object next_card next_card = draw_card_of_suit("Spade") if Card(next_card) then ? next_card.Value ? next_card.Suit else ? "I don't have one" end if I don't want to declare "next_card" as a "Card", since that will cause problems when draw_card_of_suit() returns 0. But I *can't* define "next_card" as an object, because the field references can *only* be attached to "Card"-defined objects!! So I'm forced to do this instead: object tmp Card next_card tmp = draw_card_of_suit("Spade") if Card(tmp) then next_card = tmp ? next_card.Value ? next_card.Suit else ? "I don't have one" end if I have to declare two variables instead of one, and I'm forced to move the object value into the "Card" structure just to access its fields. Is this "elegant"? I don't think so. This is just the tip of the iceberg. How about a generic sequence containing mixed data -- all kinds of very different structures. If we want to access their data, we've first got to type-check what's coming in; then, we're forced to define specific variables for *each* of these structures, just so that we can access their fields! The overhead of defining those extra variables will certainly make performance-conscious programmers mad. And what about backwards-compatibility? Are we *sure* that we won't pass a structure into a routine that will treat it like a regular object? Say we have a sequence containing many different structures, and we pass it to an existing routine. Tragically, this existing routine has a nasty flaw which accidentally concatenates a zero to each object in the sequence. How does Euphoria prevent the corruption of a "structure" in this instance? How does it know that each element of the sequence is a structure in the first place? Is it a completely different type of object in Euphoria? If so, then how can we implement it without breaking any existing code? How much more internal work will Euphoria have to do to deal with the exceptional conditions raised by structures? How much more work will the *programmers* have to do on their existing programs if structures are introduced into the language? I could go on, but I trust my point is clear. Structures will not "simplify" anything in Euphoria -- they will add a whole slew of new rules and exceptions to existing rules. Whether you *want* to use them or not will make no difference -- they *will* have an effect on how you write your programs. The kinds of structures described here *cannot* work within the context of Euphoria. The reason is simple -- they do not fit! These structure ideas all have their roots in rigid, every-data-type-defined programming languages, and there's no way we can introduce them into Euphoria -- not without a lot of fundamental changes to the language. Introducing structures into Euphoria would *not* turn it into C. But it would be very different from the Euphoria we all know and love. Gabriel Boehme
2. Re: Problems with structures
- Posted by Robert Pilkington <pilking at BELLATLANTIC.NET> Feb 04, 1999
- 487 views
>I agree that something *like* structures is needed and would be very useful >in Euphoria, for very much the same reasons they're useful in other >languages. An example from QuickBasic: True. I'm glad the discussion on structures has matured past "We need structures" and "Structures are from C, so I don't like them". I just hope it stays that way. >TYPE Card > Suit AS STRING * 9 > Value AS INTEGER >END TYPE > >DIM Deck(1 TO 52) AS Card >Deck(1).Suit = "Club" >Deck(1).Value = 2 >PRINT Deck(1).Suit, Deck(1).Value >As you can see, it's very easy to understand what's going on here. You have >a deck of 52 cards, identified here by suit and value. You don't have to >define constants to use as subscripts (i.e. Suit = 1, Value = 2) -- you can >give the individual portions field names to uniquely identify them. Plus, >the compiler will *always* know if a field reference is valid or not, and >can tell you right away if you've made a mistake. If I were to try this... > >DIM Hand(1 TO 7) AS STRING * 9 >Hand(1).Suit = "Spade" > >..the compiler would immediately flag it as an error -- the "Hand" array is >not defined to contain "Card"-type data, so the ".Suit" reference has no >meaning here. The same goes for all the other languages containing >structures -- since every data element's type *must* be defined beforehand, >the compiler can know instantly if a field reference is valid or not. Right. Argument #1 for structures: Type checking. Argument #2 hasn't been touched on in your message, so I'll point it out here: Speed. Structures, in theory, should be faster than the dynamic sequence Euphoria has. >Now, let's look at a Euphoria-style example, based on the above. [Note: in >all my examples, I will use "?" to indicate general screen output, rather >than the specific "?" statement.] > > -- we'll assume the string_9() type is already defined as a >maximum-length-9 sequence of integer values > >structure Card > string_9 Suit > integer Value >end structure > >sequence Deck of Card > > -- we'll ignore for the moment the problem of initialization... > >Deck[1].Suit = "Club" >Deck[1].Value = 2 >? Deck[1] > >This seems just fine, doesn't it? Just throw in structure definitions and >"of" statements and you're all set! It sure *seems* to simplify things -- >and if you don't like it, just don't use structures! That's pretty much the >pro-structure viewpoint, and it seems quite reasonable at first glance. Well, that's why it got so much support. Then a few of us thought about it... There is a *LOT* of special syntax associated with structures in other languages. >string_9 Hand of Card > >What is the interpreter to do? Yes, the "string_9" type conflicts with the >"Card" structure, but the interpreter won't know that until "Hand" is >initialized -- at *runtime*. This can cause much confusion, since a >definition like this won't cause a "compile" error. I suppose we could >restrict the "of" clause to be used only with "sequence" declarations, but >then what if we have our own user-defined sequence types that we *want* to >use with "of"? Like this: What about: Card Hand -- Define Hand as Card. sequence Card Hand -- Make it an "array" Now, before you say the interpreter could see this: sequence Card Hand Remember: What would happen here: type Card(atom x) return atom(x) end type sequence Card We get an error, right? That's because the interpreter *KNOWS* that Card has been defined as a type. Now, if it *KNOWS* that Card was defined as a structure, then it would know what to do with the next "line". The only problem is that Euphoria's syntax is usually pretty predictable. This is slightly less consistent. Now, this next argument you provide, has one huge flaw in it: >Secondly, examples such as these don't deal adequately with generic Euphoria >objects. Most of the structure ideas I've seen show how we could define and >use the structures, but don't really examine how *normal* Euphoria would >work with them. What if I try this: > >object one_card >one_card = Deck[1] >? one_card.Suit >? one_card.Value > >It seems simple enough at first glance -- the object "one_card" is assigned >the value from "Deck[1]", which is in "Card" format. So naturally the field >references would make sense. But how does the Euphoria "compiler" know for >sure what's in "one_card"? How does it know that "one_card.Suit" is a valid >field reference? After all, "one_card" is a generic object. There's no >guarantee that it will *always* contain "Card"-structured data. In theory, I >suppose the Euphoria "compiler" could try to get really clever -- it knows >that "Deck[1]" contains "Card"-structured data, so it "allows" the field >references to be attached to the object "one_card" in this particular case. >In practice, however, this would be a *nightmare* to implement, especially >if the value is passed back via a function: Well, how does Euphoria know that an object is a sequence or atom? It would also know if it is a structure or not. Think about this: object a a = 1 ? a[1] -- Error! Why on Earth would it do that? Could it be that Euphoria somehow magically knew that a was currently an atom? So why would it be so hard to keep this from happening? Object a a = 1 ? a.Suit Same basic principle: Access a part of a that doesn't exist. Both cases, a crash. >object one_card >one_card = 1 >? one_card.Suit > >As anyone can see, this is ridiculous. There is no way that "one_card" can >possibly contain "Card" data here. But "one_card" has the *potential* of >holding "Card"-structured data, so this *must* be allowed to pass. This will >cause an error at runtime, of course, but the point is that this shouldn't >be allowed past the "compile" step in the first place. Euphoria wouldn't allow one_card[1] to pass (at runtime). But it has the *potential* of holding sequence data, so it *must* be allowed to pass, right? It would crash at runtime. I don't see any problem here. Well, this problem already exists... So I don't see any NEW problem with the crashing with structures. >This is just the tip of the iceberg. How about a generic sequence containing >mixed data -- all kinds of very different structures. If we want to access >their data, we've first got to type-check what's coming in; then, we're >forced to define specific variables for *each* of these structures, just so >that we can access their fields! The overhead of defining those extra >variables will certainly make performance-conscious programmers mad. Kinda works against that 'speed' theory of ours, eh? And initializing the structures is also something that hasn't been worked out. >I could go on, but I trust my point is clear. Structures will not "simplify" >anything in Euphoria -- they will add a whole slew of new rules and >exceptions to existing rules. Whether you *want* to use them or not will >make no difference -- they *will* have an effect on how you write your >programs. The kinds of structures described here *cannot* work within the >context of Euphoria. The reason is simple -- they do not fit! These >structure ideas all have their roots in rigid, every-data-type-defined >programming languages, and there's no way we can introduce them into >Euphoria -- not without a lot of fundamental changes to the language. > >Introducing structures into Euphoria would *not* turn it into C. But it >would be very different from the Euphoria we all know and love. And this is actually the only really good argument against structures: They just don't fit. As was pointed out, we need "something". Something that fits these three parameters, of possible: Is faster when not being resized. Remember, there is a lot of code that uses a sequence to emulate a structure. The 2nd level, usually, never grows or shrinks. Could the speed to access such a non-dynamic part of a sequence be sped up? Is it already like that? Has type-checking: What if the data of a sequence, which somewhere has an atom where it should have a sequence, is sent to a generic save-to-disk routine? Then when it is loaded, the corrupted data will crash when sent as an argument to say pixel() as the coordinents. This could be a tough bug to track down, especially in a large program saved to disk by Edom. (Data isn't human-readable on disk) trace() would be the only hope, and it WOULD work, it would just take a bit of work. Elemenates name-space conflicts. If two pseudo structures have the same field, LIFE, then that field has to be in the *SAME SPOT* in each structure, because we can't have two constant declarations of LIFE. This can be quite irritating, when you have to reorder a logically based constant order because you need another pseudo structure to have access to LIFE. I have run into this porting C code to Euphoria. It can be worked around, but it is *VERY* annoying.
3. Re: Problems with structures
- Posted by "Boehme, Gabriel" <gboehme at MUSICLAND.COM> Feb 04, 1999
- 479 views
Robert Pilkington <pilking at BELLATLANTIC.NET> writes: >>As you can see, it's very easy to understand what's going on here. You have >>a deck of 52 cards, identified here by suit and value. You don't have to >>define constants to use as subscripts (i.e. Suit = 1, Value = 2) -- you can >>give the individual portions field names to uniquely identify them. Plus, >>the compiler will *always* know if a field reference is valid or not, and >>can tell you right away if you've made a mistake. If I were to try this... >> >>DIM Hand(1 TO 7) AS STRING * 9 >>Hand(1).Suit = "Spade" >> >>..the compiler would immediately flag it as an error -- the "Hand" array is >>not defined to contain "Card"-type data, so the ".Suit" reference has no >>meaning here. The same goes for all the other languages containing >>structures -- since every data element's type *must* be defined beforehand, >>the compiler can know instantly if a field reference is valid or not. > > >Right. Argument #1 for structures: Type checking. Argument #2 hasn't been >touched on in your message, so I'll point it out here: Speed. Structures, in >theory, should be faster than the dynamic sequence Euphoria has. I would agree with argument #1, on some level -- named references should be available *somehow*. But it doesn't work within the context of Euphoria. Neither does argument #2, not that I can see. Read on for more details... >What about: > >Card Hand -- Define Hand as Card. >sequence Card Hand -- Make it an "array" Does Euphoria have arrays? No. Euphoria has *sequences*. Once again, we are creating contradictory syntax for sequence definitions, and diluting the *essential* features of generic sequences... >Now, before you say the interpreter could see this: > >sequence Card >Hand > >Remember: What would happen here: > >type Card(atom x) > return atom(x) >end type > >sequence Card > >We get an error, right? That's because the interpreter *KNOWS* that Card has >been defined as a type. Now, if it *KNOWS* that Card was defined as a >structure, then it would know what to do with the next "line". The only >problem is that Euphoria's syntax is usually pretty predictable. This is >slightly less consistent. This is true -- "sequence structure variable" just looks confusing. This method of variable declaration could cause problems within routines, as well. Please try this yourself -- create a Euphoria executable containing just this code: type five(integer x) return x = 5 end type procedure test() integer five five = 4 ? five end procedure test() Now run it. Hey, it works! Type-declarations are allowed to be used as variable names within routines. Would structures be any different? Sequences are *by definition*, a collection of *any* Euphoria objects. The only current restriction on this is to declare a sequence as a different type: my_sequence Card This way, it's obvious how to type-check Card -- just look at the type-declaration of my_sequence() to see what's legal. With your three-identifier declaration, it's unclear just how the type-checking takes place. Argument #1 for structures should not *create* any type-check confusion -- it should *solve* it. >Now, this next argument you provide, has one huge flaw in it: > >>Secondly, examples such as these don't deal adequately with generic Euphoria >>objects. Most of the structure ideas I've seen show how we could define and >>use the structures, but don't really examine how *normal* Euphoria would >>work with them. What if I try this: >> >>object one_card >>one_card = Deck[1] >>? one_card.Suit >>? one_card.Value >> >>It seems simple enough at first glance -- the object "one_card" is assigned >>the value from "Deck[1]", which is in "Card" format. So naturally the field >>references would make sense. But how does the Euphoria "compiler" know for >>sure what's in "one_card"? How does it know that "one_card.Suit" is a valid >>field reference? After all, "one_card" is a generic object. There's no >>guarantee that it will *always* contain "Card"-structured data. In theory, I >>suppose the Euphoria "compiler" could try to get really clever -- it knows >>that "Deck[1]" contains "Card"-structured data, so it "allows" the field >>references to be attached to the object "one_card" in this particular case. >>In practice, however, this would be a *nightmare* to implement, especially >>if the value is passed back via a function: > >Well, how does Euphoria know that an object is a sequence or atom? It would >also know if it is a structure or not. Think about this: > >object a >a = 1 >? a[1] -- Error! > >Why on Earth would it do that? Could it be that Euphoria somehow magically >knew that a was currently an atom? So why would it be so hard to keep this >from happening? > >Object a >a = 1 >? a.Suit > >Same basic principle: Access a part of a that doesn't exist. Both cases, a >crash. Yes, but a crash at *runtime*. Structures in every other language can be verified at "compile" time. Euphoria won't flag "? a[1]" as an error until the statement is actually executed. Please, try this on your own -- create a Euphoria executable containing *just* this code: procedure try_this() object a a = 1 ? a[1] end procedure Notice that we're *not* calling try_this() from anywhere, so we're not executing anything. The only way "? a[1]" can be flagged as an error is if the "compiler" catches it. Now run the executable. Well, what do you know, no errors! Again, every other language can verify its structure field references at compile-time -- please refer back to the QuickBasic example given at the beginning. Euphoria's "compiler" would be unable to do this. True, it lets subscript references like "a[1]" go by, but subscripts at least potentially apply to *any* sequence within the program. Structure field names would *only* apply to their specifically-defined structures -- yet they, too must be allowed *anywhere* in the program. Programmers from other languages would wonder why Euphoria is unable to do "compile"-time field reference checks. >>object one_card >>one_card = 1 >>? one_card.Suit >> >>As anyone can see, this is ridiculous. There is no way that "one_card" can >>possibly contain "Card" data here. But "one_card" has the *potential* of >>holding "Card"-structured data, so this *must* be allowed to pass. This will >>cause an error at runtime, of course, but the point is that this shouldn't >>be allowed past the "compile" step in the first place. > > >Euphoria wouldn't allow one_card[1] to pass (at runtime). But it has the >*potential* of holding sequence data, so it *must* be allowed to pass, >right? It would crash at runtime. I don't see any problem here. Well, this >problem already exists... So I don't see any NEW problem with the crashing >with structures. With "one_card[1]", Euphoria doesn't have to do any major type-check work to determine if "one_card" is a sequence or atom. For "one_card.Suit", how will Euphoria know if "one_card" contains "Card"-type data? Why, it will have to do a type-check, of course -- EVERY TIME IT'S REFERENCED WITH A FIELD NAME. object one_card one_card = Deck[1] ? one_card.Suit -- type-check required here ? one_card.Value -- type-check required here, too This would be a MAJOR performance-killer. Kinda goes against argument #2 for structures, doesn't it? True, the "without type_check" directive could turn this off. But this would (again!) create major changes in the way Euphoria does business. Currently, the only time it needs to do user-defined type-checks is when a value is assigned to the variable -- now it needs to do them just to *reference* the varaible! >And this is actually the only really good argument against structures: They >just don't fit. As was pointed out, we need "something". > >Something that fits these three parameters, of possible: > >Is faster when not being resized. Remember, there is a lot of code that uses >a sequence to emulate a structure. The 2nd level, usually, never grows or >shrinks. Could the speed to access such a non-dynamic part of a sequence be >sped up? Is it already like that? This might be a possibility. However, Euphoria's current design allows for changes to *any* of the sequences. Exceptions would require extra logic, which would kill speed. >Has type-checking: What if the data of a sequence, which somewhere has an >atom where it should have a sequence, is sent to a generic save-to-disk >routine? Then when it is loaded, the corrupted data will crash when sent as >an argument to say pixel() as the coordinents. This could be a tough bug to >track down, especially in a large program saved to disk by Edom. (Data isn't >human-readable on disk) trace() would be the only hope, and it WOULD work, >it would just take a bit of work. Type check the data before you access it. I don't understand why pro-structure people are so allergic to big type-checks. They're only performance killers when type-checking is *on*. That should be perfectly fine when you're testing your program. When you're confident your program is stable, you can specify "without type_check" at the beginning of your source. It's true that this *could* be a problem if you haven't plugged all the code leaks in your program. That's why type-checks are so cool to begin with. Structures are an attempt to "force" Euphoria to rigidly "lock" the types of data it can use and pass around. But structures would *still* require type-checks -- they'd just be hidden under the surface of the "structure" syntax. Plus, they'd introduce all sorts of other exceptional conditions that sequences don't have to deal with, so I don't see what we're gaining with structures. For problems as described above -- an incoming file that has somehow become corrupted -- just type-check your incoming data *once* before passing it on to the rest of your program. You know, call the type like a function to verify that the data is legal. This way, if there's a problem, you get a very easy-to-understand type-check error -- it's stopped before it gets too far into your program, and doesn't become "tough to track down". You still seem to be thinking in terms of other programming languages -- *they* don't have anything like type-checks or structure verification, so it doesn't occur to you that Euphoria's type-checking can catch this *before* it becomes a major problem. (I hope I'm not being too presumptuous here...) Euphoria type-checks are the best I've found in *any* language. You can restrict any value to any structure or range you desire. Instead of being tied to machine-types like other languages, Euphoria allows you to logically define your types in any way you need to. Yes, this kind of type-checking is slower. But the flexibility and runtime safety that's gained, IMHO, is worth the exchange. >Elemenates name-space conflicts. If two pseudo structures have the same >field, LIFE, then that field has to be in the *SAME SPOT* in each structure, >because we can't have two constant declarations of LIFE. This can be quite >irritating, when you have to reorder a logically based constant order >because you need another pseudo structure to have access to LIFE. I have run >into this porting C code to Euphoria. It can be worked around, but it is >*VERY* annoying. I agree with this point. But I don't see this as a structure issue -- more of a NAMESPACE issue instead. Perhaps solutions to namespace problems will eliminate the need for structures? Gabriel Boehme
4. Re: Problems with structures
- Posted by Don Groves <groves at ACM.ORG> Feb 04, 1999
- 473 views
At 10:57 2/4/99 -0600, Boehme, Gabriel wrote: ...major snipping... >I agree with this point. But I don't see this as a structure issue -- more >of a NAMESPACE issue instead. Perhaps solutions to namespace problems will >eliminate the need for structures? > >Gabriel Boehme Yes, the namespace problem is a separate issue from structures and Rob has already said that the namespace issue is a major focus for the next release. IMHO, future discussion of structures would be better served without raising the namespace issue (or the ghost of C/C++ You raise some very good points, Gabriel, about the impact of the addition of structures on the overall language. We need to remember TANSTAAFL (Their Ain't No Such Thing As A Free Lunch). Every feature comes with a cost, and you have identified some potentially expensive costs for the addition of structures. -- Don Groves -- Don Groves groves at acm.org If I could think of a clever tag line, this would be it.
5. Re: Problems with structures
- Posted by jguy <jguy at ALPHALINK.COM.AU> Feb 05, 1999
- 454 views
Boehme, Gabriel wrote: > I agree that something *like* structures is needed and would be very useful > in Euphoria, for very much the same reasons they're useful in other > languages. The problem is not with structures. It is with wanting to implement structures in Euphoria. Structures, period. A structure is a sequence with severe limitations imposed on it. A sequence in a straight-jacket. So is this: type string(object x) if atom(x) then return false else for i=1 length(x) do if not char(x) then return false -- I assume that type char() has been defined end if return true end type Another, real problem, is some of the proposed syntax: > sequence Deck of Card This "of" is borrowed from Pascal. But Pascal declarations have a very different syntax: [Pascal] var x,y,z: integer; [Euphoria] integer x,y,z So, as it is logical to write in Pascal Deck: array[1..52] of Card; ^^^^^^^^^^^^ of ^^^^ it is illogical to write in Euphoria sequence Deck of Card ^^^^^^^^ of ^^^^ > ... Just throw in structure definitions and > "of" statements and you're all set! Even the more Euphoria-like syntax sequence of Card Deck is a dreadful horror. By definition a sequence is a list of objects, and an object is *anything*. "Deck" is, at best, definable as a "sequence" of sorts, upon which such severe restrictions have been imposed as to make it something else altogether. > It sure *seems* to simplify things -- > and if you don't like it It confuses things. Let me remember my first encounter with structures. It must have been with Turbo Pascal 1.0. Back then I saw very little point in structures (called records in Pascal). The "Deck" example seemed an awfully convoluted way of implementing what I used to write: [style 1] var Value: array[1..52] of integer; Suit: array[1..52] of integer; or something like that, functionally equivalent to another way I had to generate decks: [style 2] var Deck: array[1..52] of array[1..2] of integer; Then, gradually, I got used to records, and found that, sometimes, rightly used, they were more convenient than my old ways: type Card= record Value,Suit: integer end; Deck= array[1..52] of Card; Shuffling and dealing were as easy as with the "style 2" code, AND I did not have to remember which of those two cells held the value of a card, which its suit. (I also got into awful messes using records when inappropriate. And objects too. And inheritance) [Thinking-aloud mode] Go back to "style 1". There is a bijection between Value and Suit. "Unfold" a record (a structure if you prefer), to turn it into "style 1" code, and you end up with a set of arrays such that there is a bijection between any two of them. Always. Oh... I could now have a deck the values of which are *anything*: {"ace low","deuce","trey",4,5,{6,0}... "ace high", ....} Ditto its suits: {0,{"clubs","fiori",1},.... It does not matter if such a deck is functionally useless (it only means that I can't think of a use right now), what matters is: Value and Suit are now Euphoria sequences, not arrays. The only restriction imposed on them is that they must be the same length, since there must be a bijection between them. [Thinking-aloud cap off] > However, these examples (and most others I've seen) are deeply flawed, > because they are all based on examples from other languages that *only* have > rigid definitions for variables. They are flawed because they try to inject the syntax and functionality of straight-jacket languages into Euphoria. You must, starting from the principles behind Euphoria, ... mmm... find an application of those principles that covers the functionality of stuff like structures. While NOT *aiming* at covering structures. > First of all, look at the apparently harmless syntax of "sequence Deck of > Card". Heck, it almost reads like English, doesn't it? Indeed, the syntax is abominable, the semantics absurd, and I am not even going into what the interpreter is supposed to make of it. Yes, I know: since it makes no sense, treat it as a comment > I could go on, but I trust my point is clear. Structures will not "simplify" > anything in Euphoria -- they will add a whole slew of new rules and > exceptions to existing rules. It depends how they are implemented. It they are a mere side-effect of an extension of Euphoria, an extension which does not modify the language, but *amplifies* it, then, there is no harm done. Right now you can write structures in two ways ("style 1" and "style 2" above). It only becomes terribly awkward when you feel the need for symbolic constants to index those "structured" sequences. At least, all this my ranting had the advantage of reminding me of the old way I used to represent structures, before I learnt C, before I learnt Pascal. "Style 1" might turn out to be easier for my purpose. I'll see. Jacques Guy