Forums

Cobra and the newbie

General discussion about Cobra. Releases and general news will also be posted here.
Feel free to ask questions or just say "Hello".

Cobra and the newbie

Postby necromanco » Tue Mar 11, 2008 8:35 am

Hello Chuck,

I think that, as it is now, Cobra is easier to learn than Python or Ruby(except for
very initial only procedural code lessons).
I don't know how much importance you give about Cobra being friendly to newbies.
Anyway, I will share some thoughts, based on my personal experience, about things
that make the language harder for a newbie:

1) Zero based indexing. I know this is the pattern in the info world and that Cobra uses .NET base class library. So this will not change. I wrote it here just to help item 2.

2) The Python style for slicing: excluding the last index element:
Code: Select all
'012'[0:2] # makes '01'

It is not a pattern in the info world. Even .NET includes the last element - although its slicing uses count from first element index instead of last element index:
Code: Select all
someList.GetRange(index, count)

This may be tricky even for a skilled programmer from Ruby, for example.
And for a newbie, dealing with item 1 already filled his share of sacrifice. :D

3) The way to call a constructor without arguments:
Code: Select all
SomeClass()

Cobra sends warning message each time it sees a function call with parentheses but no arguments. I think it is fine - helps to keep the code visually clean. But, when comes the time to call a constructor with no arguments, Cobra behaves at the opposite, demanding an empty pair of parenthesis(no much coherency). I spent some minutes yesterday because of a missing '()' after a constructor call. I think that
Code: Select all
SomeClass.new

is less error prone(and, in this case, 'new' instead of 'init' would be welcome).

4) Shared X Non Shared. Although I have sympathy for the syntax 'is shared', for a newbie that is writing code, it is important that all the shared stuff of a class be toghether inside the only one shared sector, so keyword 'shared' is used no more than once per class. There is a post in this forum where skilled people stumbled a bit because of spreaded 'shared' declarations.

5) Liberal naming(this is scary):

Code: Select all
class NightmareOfANewbie
   shared
      def main
         .pass
      
      def pass
         print "'Hail, newbie!' from method 'pass'  :P"



---

Cobra ROX on many topics, so I feel it could be a pity to keep these small gotchas for newbies.

I hope you don't find me very intrusive.
Thanks for your attention :).

-necromanco
necromanco
 
Posts: 8

Re: Cobra and the newbie

Postby Charles » Tue Mar 11, 2008 8:18 pm

Constructive feedback is always welcome here, so no, I don't perceive you as being intrusive.

I'm currently on the road for business, though, so a more thoughtful response will have to wait til later in the week or this weekend.
Charles
 
Posts: 2515
Location: Los Angeles, CA

Re: Cobra and the newbie

Postby Charles » Thu Mar 13, 2008 11:13 pm

necromanco wrote:Hello Chuck,

I think that, as it is now, Cobra is easier to learn than Python or Ruby(except for
very initial only procedural code lessons).
I don't know how much importance you give about Cobra being friendly to newbies.

A lot of importance. There are many places where the Cobra compiler checks to see if you did something from Python or C# that isn't compatible with Cobra and then gives you a specific error message to help you out.

I also recently added the suggestions to help get the case of the method right.

But I also find it impossible to make a truly perfect syntax. I can only get really close. :-)

necromanco wrote:Anyway, I will share some thoughts, based on my personal experience, about things
that make the language harder for a newbie:

1) Zero based indexing. I know this is the pattern in the info world and that Cobra uses .NET base class library. So this will not change. I wrote it here just to help item 2.

2) The Python style for slicing: excluding the last index element:
Code: Select all
'012'[0:2] # makes '01'

It is not a pattern in the info world. Even .NET includes the last element - although its slicing uses count from first element index instead of last element index:
Code: Select all
someList.GetRange(index, count)

This may be tricky even for a skilled programmer from Ruby, for example.
And for a newbie, dealing with item 1 already filled his share of sacrifice. :D[/cobra]

In "someList.GetRange(0, 5)" what indexed elements of someList are returned? The answer is someList[0], someList[1], someList[2], someList[3] and someList[4]. You can get this same effect with slicing as in someList[0:5]. Slicing semantics are actually compatible with List<of>.getRange(i,c) and String.substring(i,c). If slices were inclusive then someList[0:5] would return one more element than someList.getRange(0, 5).

I know slices and right-exclusion can take some getting used to, but it actually works out really well. Newbies will have to endure some learning curve on this one.

necromanco wrote:3) The way to call a constructor without arguments:
Code: Select all
SomeClass()

Cobra sends warning message each time it sees a function call with parentheses but no arguments. I think it is fine - helps to keep the code visually clean. But, when comes the time to call a constructor with no arguments, Cobra behaves at the opposite, demanding an empty pair of parenthesis(no much coherency). I spent some minutes yesterday because of a missing '()' after a constructor call. I think that
Code: Select all
SomeClass.new

is less error prone(and, in this case, 'new' instead of 'init' would be welcome).[/cobra]

I played around with this idea before, but the problem I ran into is that "SomeClass.new" implies that there is a "def new is shared" in SomeClass. That doesn't line up with the constructor approach used in C#, VB, Java or Python. Most importantly, it wouldn't line up with C# and VB, whose virtual machine, Cobra lives on.

I agree it's a bit of a clash with avoiding empty parens on dotted access. Note that you also need empty parens for a variable that points to a method (aka, a delegate). So the rule is: Use parens to call on identifiers whether types or variable. Do not use empty parens on dotted members.

I haven't found a better alternative so far.

necromanco wrote:4) Shared X Non Shared. Although I have sympathy for the syntax 'is shared', for a newbie that is writing code, it is important that all the shared stuff of a class be toghether inside the only one shared sector, so keyword 'shared' is used no more than once per class. There is a post in this forum where skilled people stumbled a bit because of spreaded 'shared' declarations.

Yeah, I tend to code this way whenever I have more than one "shared" member. At the very least we can emphasize this approach more in the docs.

I'd also like to kill the requirement that "is shared" be attached to "def main". The compiler should be "smart enough" to make an instance and invoke "main" provided the class doesn't require initialization parameters.

necromanco wrote:5) Liberal naming(this is scary):

Code: Select all
class NightmareOfANewbie
   shared
      def main
         .pass
      
      def pass
         print "'Hail, newbie!' from method 'pass'  :P"


The ability to name class members after keywords is motivated by action words like "print" and "throw". It's scariness should be highly mitigated by the fact that dotted access is required. Also, you cannot use keywords for argument or variable names. This is staying.

necromanco wrote:Cobra ROX on many topics, so I feel it could be a pity to keep these small gotchas for newbies.

Thanks. I do appreciate the feedback and hope I don't come across as too stuck in my ways when I don't make most recommended changes. I've thought about most of these issues before and some of them are complex as they intersect with other issues like .NET.

Of course, there have been improvements based on feedback including allowing spaces for indentation (early Cobra versions only recognized tabs), the line continuation character (_) and the ability to declare public vars.

Here are some more things that will be changing based on feedback:
  • get count from var as int
  • if x < y, print x
  • for x in j
  • for x in i : j
  • for x in i : j : step
The first two are additions to the syntax.

The last three are really the same thing: The numeric for loop will use "in" and a colon-separated specification that matches slicing. The current numeric for loop already matches slicing, but has a funky syntax that doesn't tie in with anything else in the language. However, it will be supported for a long period of time (say a year?) to avoid breaking people's code.

-Chuck
Charles
 
Posts: 2515
Location: Los Angeles, CA

Re: Cobra and the newbie

Postby necromanco » Fri Mar 14, 2008 6:26 am

Hello!

Chuck wrote:A lot of importance. There are many places where the Cobra compiler checks to see if you did something from Python or C# that isn't compatible with Cobra and then gives you a specific error message to help you out.

I got some error messages(due to tests or mistakes) that made me smile while thinking 'this compiler undertands me and wants to be my friend!'.
Thanks for care.
Chuck wrote:In "someList.GetRange(0, 5)" what indexed elements of someList are returned? The answer is someList[0], someList[1], someList[2], someList[3] and someList[4]. You can get this same effect with slicing as in someList[0:5]. Slicing semantics are actually compatible with List<of>.getRange(i,c) and String.substring(i,c). If slices were inclusive then someList[0:5] would return one more element than someList.getRange(0, 5).

I can't agree with this argument(I am not meaning agree/disagree with the design decision). This example is mere coincidence. The numbers match because the start index is zero. They are not born to match:
Code: Select all
someList = [0, 1, 2, 3, 4, 5]
# python:
someList[1:2] # produces [1]
// C#:
someList.GetRange(1, 2) // produces [1, 2]

So if they are not born to match, the semantic of the slices has to be compared by the effect
inclusive or exclusive on the last element be it called by index or by count from the first index. And Python slicing is exclusive while C# slicing is inclusive.

Chuck wrote:I played around with this idea before, but the problem I ran into is that "SomeClass.new" implies that there is a "def new is shared" in SomeClass. That doesn't line up with the constructor approach used in C#, VB, Java or Python. Most importantly, it wouldn't line up with C# and VB, whose virtual machine, Cobra lives on.

I agree it's a bit of a clash with avoiding empty parens on dotted access. Note that you also need empty parens for a variable that points to a method (aka, a delegate). So the rule is: Use parens to call on identifiers whether types or variable. Do not use empty parens on dotted members.

Right now I have nothing to say about sytax for a variable that points to a method.
About the constructor, maybe C# separates constructor(static, implicit) from initializer(non static, explicit although not mandatory)? I don't know, it is just a guess. But if it happens this way, wouldn't this be possible?
Code: Select all
class Proposal
   shared
      def main
         x = SomeClass.new
         # SomeClass.init # ilegal
         # SomeClass.new.init # ilegal
         # SomeClass() # ilegal

class SomeClass
   def init
      print "Here is an instance of 'SomeClass'."

   # def new # ilegal, be shared or not

Constructors are special methods. They return objects but we can't assign return type in their declarations. What I mean is that anyway they have special syntax. So maybe the special syntax can tell that 'SomeClass.new' is not a standard call to a shared method.

Chuck wrote:I'd also like to kill the requirement that "is shared" be attached to "def main". The compiler should be "smart enough" to make an instance and invoke "main" provided the class doesn't require initialization parameters.

Like I said above, special method may have special syntax(specially when keeps coherence with the whole).
Chuck wrote:The ability to name class members after keywords is motivated by action words like "print" and "throw". It's scariness should be highly mitigated by the fact that dotted access is required. Also, you cannot use keywords for argument or variable names. This is staying.

Sure. Would it be useful to deny that some names would be used in declarations?. Maybe a Cobra program has to import a method named 'pass' from a class written in other language. I just feel that 'def pass' is an ugly possibility.

Well, thanks for your answers and forgive me if I bothered with details :D .

-necromanco
necromanco
 
Posts: 8

Re: Cobra and the newbie

Postby Charles » Sat Mar 22, 2008 5:29 pm

Sorry for taking so long to respond. Things got busy with bug fixing and I took another crack at Trac, but didn't get very far (with Trac; bugs are squashed).

So I'm responding here to the above message, but I was too lazy to quote it.

Regarding slices being right exclusive, I agree that using an example that starts with 0 was a bad choice because it makes the argued conclusion look coincidental. But I think you made a similar mistake with using 1. Let's go to 3! But first a quick side note. I want to point out that everyday subtraction is also exclusive. For example, 5 - 3 = 2, not the count of the inclusive list of the operands--[5, 4, 3]--which is 3.

Getting back to Cobra, the expression "t[3:5]" returns 5 - 3 = 2 elements which are t[3] and t[4].

Likewise, .NET's "t.getRange(3, (5-3))" returns t[3] and t[4].

Or you could say that "t.getRange(3, 2)" returns t[3] and t[4] which is the same as t[3:3+2].

Most importantly: "t.getRange(index, count)" does not include element t[index+count]. It stops one short of that. C#'s getRange matches Python's slicing except for using a count instead of a second index.

You are free to use either in a Cobra program and always will be.


Regarding the "SomeClass.new" suggestion, I simply prefer the more succinct Python style "SomeClass()" and see both as being equally arbitrary ("new" <> "init" and nothing about calling a type implies invoking "init").


Regarding denying some of the keywords for use as method names, there are many keywords and each one could be a debate as to whether or not you might like a method with that name. Then there is the cognitive load of remembering which ones you can use and which ones you can't. Then there are the gripes if a new version of Cobra restricts one. Sorry, can't go there.


I hope that's helpful. It's been an interesting discussion.

-Chuck
Charles
 
Posts: 2515
Location: Los Angeles, CA

Re: Cobra and the newbie

Postby necromanco » Sun Mar 23, 2008 6:03 pm

Hi,

Thanks for your reply. I see that you are/were busy with more important questions.
I am glad enough that I could show some points that were analyzed.

Though I consider this matter is over, I would not like to leave without explain better my point of view about:

Or you could say that "t.getRange(3, 2)" returns t[3] and t[4] which is the same as t[3:3+2].


It is that when I see "t.getRange(3, 2)", I expect t[3] and t[4] because I think on 2 elements, starting from t[3]. And when I see t[3:5] I expect t[3], t[4] and t[5] because I think on number 3 and number 5, and not on their subtraction. It is like what_you_see_is_what_you_get.
As I don't think on their subtraction, I conclude that the styles of Python and C# are diferent. But for who that sees subtraction, of course, must conclude that both styles mean the same.

I believe that the exclusion of the last element is made to easy work with zero based arrays.
If we have myArray = [a, b, c, d, e] (wich count is 5), then to get the whole array, 'myArray[0:myArray.count]' or 'myArray[0:5]' works fine; instead of myArray[0:myArray.count - 1].

In my opinion, zero based arrays are the logical choice for a low level language and one based arrays are the logical choice for a high level language. Unfortunately for the newcomers, it seems hard to find a stable language/implementation that is fully high level.

Thanks again.

-necromanco
necromanco
 
Posts: 8

Re: Cobra and the newbie

Postby Charles » Tue Mar 25, 2008 12:33 am

Btw I meant to post this link which also addresses this topic:

Why numbering should start at zero (PDF) by Dykstra.

As a "computing scientist", I definitely leans towards starting with 0 and using right exclusive bounds. However, if you ask a person to make a list and number it, they'll start with 1. Even computer books that contain numbered lists start with 1! So it's certainly debatable.

Furthermore, Cobra gives 0.8 for "4 / 5" and Python is headed in that direction as well. So there's an example of leaning towards the every day person instead of the "computing scientist" who might likely insist that an arithmetic operator produce a result of the same type as its operands (int, not decimal).

But this is just some "out loud thinking". My final decision is to stick with zero based lists and right exclusive slices in case people are wondering.
Charles
 
Posts: 2515
Location: Los Angeles, CA

Re: Cobra and the newbie

Postby hopscc » Wed Mar 26, 2008 3:59 am

Fair enough
The 0-based or 1-based is basically an aesthetic decision ( even for dykstra - as indicated by discarding some possibilities as 'ugly' - the notes regarding practise in mesa is a little more compelling (:-) ). folks notions of aesthetics differ (:-) .

The slice notation as start or end inclusive or exclusive can be made more intelligible for newbies (and everyone else) by taking a notion from (emacs)text editing and presenting the notation as giving the position *between* (and before )the items (interval between) rather than the item position itself.
If done that way the range is all inclusive ( for a 0 offset) and much more obvious.

i.e
for slice notation 0 represents the gap before the first ( 0th item - 0 base) item,
position 5 represents the gap before the 5th item( between the 5 and 6th item ( 1 base) or 4th and 5th item (0 base) , the 5th interval between items ( for 'abcdef' the gap between the 'e' and the 'f')

for 'abcde'

|a|b|c|d|e|
0a1b2c3d4e5



0:1 is the (single) item ('a') enclosed by the interval between the gap before the 0th item ('a') and the gap before the second item 'b'
|a|bcdef
0a1bcdef


[2:4] is 'cd'
ab|cd|ef
ab2cd4ef6


Clearer?
hopscc
 
Posts: 632
Location: New Plymouth, Taranaki, New Zealand

Re: Cobra and the newbie

Postby Charles » Wed Mar 26, 2008 8:15 am

hopscc wrote:Clearer?

Well I see what you're saying, but my brain is now wired for:
Code: Select all
abcde
01234

and thinking of slices through subtraction (for t[i:j] there are j - i elements) or simply as right exclusive. I guess the issue for me is that t[ i] is not to the left of an element--it is the element--so it's hard for me to think of ranges that way.

But some graphics libraries conceptualize pixel coordinates the way you are describing. It's a valid way to look at it.

Btw right exclusive ranges are also used in table lookups. Let's say you have a table driving the price of a physical product based on ranges of some attribute:
Code: Select all
Length
Min, Max, Price
  0,   5, $1.00
  5,  10, $2.00
_10,  20, $3.00

The logic has to be "if x >= Min and x < Max" or the table would be ambiguous.
Charles
 
Posts: 2515
Location: Los Angeles, CA


Return to Discussion

Who is online

Users browsing this forum: No registered users and 33 guests