Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

Slices (Was: Inspirations from Python) #2

Closed
holtwick opened this Issue Mar 5, 2011 · 10 comments

Comments

Projects
None yet
2 participants

holtwick commented Mar 5, 2011

I love the concept of move. Since I have the feeling this language might not be totally fix yet, I would like to try to present you some of my personally preferred language structures with the hope you might like and integrate them ;)

Since I come from Python I got used of some very handy thing I miss very much in other languages like Objective-C (I'm forced to use now). These are:

SLICES

What I really like a lot in Python are slices, and I think they would fit well into 'move' too. In Python they work for Strings and Lists and go like this:

x = [1,2,3]
x[1:] == [2,3]
x[:-1] == [1,2]
x[1:2] == [2]
x[:] = [1,2,3] // copy of x

More about this here:

http://docs.python.org/py3k/tutorial/introduction.html#strings
http://docs.python.org/py3k/tutorial/introduction.html#lists

OBJECTS

In 'move' and 'Javascript' the attributes are added with a colon like x = {a:1, b:2}. In Python too. But there is a second way to do it in Python like x = dict(a=1, b=2). The same for function arguments, e.g.

def test(x,y):
  pass
test(x=1, y=2)

Since 'move' uses Objects kind of argument container, I wonder if it wouldn't be easier to understand for most people if 'move' would also support object creation like this:

x = {a=1, b=2}

TUPLES & MULTIPLE ASSIGNMENT

Another great structure are tuples which are basically immutable lists. But they have a great effect in assignment, e.g. you can do

a, b = (1, 2)

Or e.g. things like exchanging variable values without temporary variable

a, b = b, a // instead of h = a; a = b; b = h

This becomes very handy when having a function result of more than one value.

Owner

rsms commented Mar 5, 2011

Great write-up! I'm very familiar with Python — Python was my go-to-for-everything language before I moved to JS 2 years ago.

Slices

Slices would indeed be neat. Any suggestion on how this could be implemented? If you want to experiment, you want to create a new AST mutator (copy-paste one in lib/ast-mutators.js), enable it in the "compile" function (lib/index.js) and then hack in the support into the AST mutator. This provides a very flexible workflow as you can inspect the AST without, while and after your mutator applied.

Objects

What's wrong with x = {a:1, b:2}? The python "dict" function can be implemented in Move to allow the x = dict(a=1, b=2) "syntax", but with Move keyword arguments:

dict = ^(a){
  kw = arguments.keywords
  delete kw.__kw
  return kw
}
x = dict{a:1, b:2}
print x  # --> { a: 1, b: 2 }

However, I would never want to do that myself — it's more code, less intuitive imho and slower than just x = {a:1, b:2}. Or maybe I'm missing your point...

Tuples & multiple assignments

Move and JavaScript are fantastic in the way that everything is an object. Introducing an immutable list type would not make much sense. I've never really understood why Python have both tuples and lists, but my guess is legacy and/or for performance reasons.

Multiple assignments might feel like a time-saver but in many cases it actually makes the code less readable. Move makes some compromises to the speed of writing code in order to gain better readability.

By allowing multiple assignment you encourage developers to design functions that return lists of values, where objects should be used. Consider the following example using multiple assignment:

result = ^{
  core_temp = nuclear_power_plant.core_temp
  meltdown_deadline = (new Date)+10000000
  coffee_ready_at = (new Date)+30000000
  return [core_temp, meltdown_deadline, coffee_ready_at]
}

A user of such a function might mix up the meaning of the arguments or the arguments themselves (you might never taste coffee again). There's simply more room for human error (writing documentation, reading documentation, mixing up args, etc) The above function should instead be written as:

result = ^{
  r = {}
  r.core_temp = nuclear_power_plant.core_temp
  r.meltdown_deadline = (new Date)+10000000
  r.coffee_ready_at = (new Date)+30000000
  r
}

This would yield the undefined atom if you mix something up or misread the documentation and you can also inspect the returned value to get a sense of what results you got.

Slices, then

So, let's call this "issue" the "Let's have a look at slices" issue :)

holtwick commented Mar 6, 2011

I totally agree with your argumentation.

For 'slices' I forked the code and will see what I get accomplished. But I'm a little limited in time I can spend on it. I'll keep you updated.

But I would like to clarify some of the questions that came up:

Objects

This was just an idea to optimize the syntax of Move for better understanding for people learning to program. In you documentation you have this example for named arguments:

hello = ^(title = "Mr."){ "Hello "+title+" "+name }
print hello {title: "Ms."}

Ok, you could also write it like this:

print hello (title = "Ms.")

But I think this would also completely make sense:

print hello {title = "Ms."}

So, what I would like to consider is to allow '=' instead of ':' in object attribute assignment. Syntactically it would work out perfectly and I also see nothing in contrary from the logical point of view, because those attributes become local variables in some contexts.

This is just an idea to discuss about. I would also just see it as an optional way of creating object attributes. The colon should still exist. The Ruby people have the nice philosophy of the language being 'natural' which was my inspiration at this point ;) http://www.ruby-lang.org/en/about/

Tuples

You are right, they are useless and the same than lists.

Multiple Asignments

Your argumentation is right. And this is not a feature that is absolutely needed. But also the Go language makes heavy use of it and I could think of some examples where this might be nice to have. http://golang.org/doc/effective_go.html#multiple-returns

Anyways, the whole thing might come down to the need of just a universal 'copy' method, because this is why it is used most often. This could be the extract of this idea and might become part of your standard library. Example:

original_value = {x: 0}
value_to_work_with = copy original_value
value_to_work_with.x += 10
Owner

rsms commented Mar 6, 2011

Objects

The nice thing about keyword arguments is that they are in fact objects and parsed as such, thus simplifying both the parser and the syntax. But I see you point — although it's somewhat subjective (a lot of what we are discussing is inherently subjective) — that using the equals character for both key-value assignment and "other" assignment is probably easier to understand.

One thing I noticed when doing some face-to-face user tests while designing Move was that the concept of assignment did not feel natural to people without advanced math or cs background.

One person suggested that e.g. x = 5 be changed into 5 -> x ("assign 5 to x") or even x <- 5. This syntax was investigated but when transforming some real-world code it quickly broke down, namely longer function and object expressions ending with their "name", forcing you to read through to the end of a definition to find its variable name.

Based on this I'm doubtful that the equal character is the best fitted character to use for assignment. Another example pro colon characters is the use of assignments and hierarchy in natural language. For instance, you can write "Contestants: John, Lisa and Garry" but would never write "Contestants = John, Lisa and Garry" unless in a CS/programming language context which in this discussion would be irrelevant.

Thus my conclusion is that I chose the colon character for assignment when in doubt. The Move parser actually supports colon character for assignment of default keywords arguments. The following is a valid Move program:

name = "Julia"
hello = ^(title: "Mr."){ "Hello "+title+" "+name }
print hello {title: "Ms."}

What would be interesting is to research how eliminating the equal character for assignment, replacing it with the colon character, would work and feel. That is, something like this:

name: "Julia"
hello: ^(title: "Mr."){ "Hello "+title+" "+name }
if (name != "Unknown")
  print hello {title: "Ms."}

While at it, I have chosen to keep parenthesis call-style simply for being able to express "these are positional arguments" and "these are keyword arguments" in a simple manner. Admittedly primarily because the parser code got a lot easier to work with as I don't have to build a hybrid arguments parser but can simply activate either "read a comma separated list of values" or "read an object" depending on the kind of wrapping characters ("(" or "{"). With some effort it should be possible to merge the two, eliminating parenthesis call-style, thus this would be a valid Move program:

foo = ^(a=1, b=2, c=3){...}
print foo {11}        # --> a=11, b=2, c=3
print foo {a: 11}     # --> a=11, b=2, c=3
print foo {11, 22}    # --> a=11, b=22, c=3
print foo {11, c: 33} # --> a=11, b=2, c=33

But this case is ambiguous:

print foo {b: 33, 44} # --> a=?, b=?, c=?

Multiple Assignments

Your case of "copying" a value is a very good use-case and something which prototypal languages like Move and JavaScript solve very well — just create a new object where you set its prototype to the object you want to copy!

Here's a valid Move program which I believe do what you want:

original_value = {x: 0}
value_to_work_with = create original_value
value_to_work_with.x += 10
print original_value, value_to_work_with
# --> { x: 0 } { x: 10 }

The built-in create function (lives in Move and is based on Object.create) simply creates a new object and assign the first argument ("original_value" in this case) as the prototype.

Owner

rsms commented Mar 6, 2011

I just wrote a version of the compiler which uses the colon character instead of the equal character for assignments. However, when testing against some code I was reminded that this case is tricky (if possible at all) to solve:

foo: body == null ? defaultValue : typeof body

When we are parsing "defaultValue : ..." we don't know if it's an assignment or the "else" part of the logical test.

This brings us back to the equal character for assignment. However, I think we might be able to think about it in this way:

  • = is used for assigning values to variables
  • : is used for denoting a value in a key-value pair

Examples where the equal character is used:

foo = ^{...}
foo.bar = 1.618

Examples where the colon character is used:

{foo: 1, bar: 3}
^(x: 4, y: null){...}
hello {title: "Dr."}

I will deprecate the use of an equal character when defining default values for function arguments in the next release of Move.

Owner

rsms commented Mar 7, 2011

Related: issue #4 (first slice implementation)

holtwick commented Mar 7, 2011

Well, assignment and = char is a very much discussed topic. Pascal for example therefore uses := to not mix up with = for comparison.

Oh, didn't know about create. Maybe just an alias called copy would solve my troubles, but this is just a matter of taste ;)

Apropos taste, I like your considerations to unify the function argument definitions and the key value assignments. A last word about my idea using = instead:

After thinking a while about it, I came to the idea that everything is best described by the word Scope. An Object is more or less about its own Scope. For example take a simple function:

f = ^{ x = 1 }

x is now valid in the scope of f. In my idea of unifying the syntax I could imagine this (note also that no , would be needed any more):

o = { x = 1 }
print o.x == 1

Here x is in the scope of the object. It is kind of a local variable that is persistent as long as the object persists.

It is similar to the constructor idea of Javascript (Inspired by http://bonsaiden.github.com/JavaScript-Garden/#constructors ):

function cl() {
    this.x = 1
}
o = new cl()
console.log(o.x)

But you will agree, this is a matter of taste and this is just a personal opinion and kind of train of thought. Maybe such a solution would also throw up a bunch of new problems too.

holtwick commented Mar 7, 2011

Oh, I already found the first problem ;) In my approach this would not work:

o = {'this': 1}
Owner

rsms commented Mar 7, 2011

Well, assignment and = char is a very much discussed topic. Pascal for example therefore uses := to not mix up with = for comparison.

Indeed. I don't like the tedious ":=" syntax albeit the idea is nice ("=" is comparison or assertion while ":=" is both assignment/hierarchy expression and an assertion, in a way).

Oh, didn't know about create. Maybe just an alias called copy would solve my troubles, but this is just a matter of taste ;)

copy = create

And you're done :P Or even:

move.runtime.copy = move.runtime.create

And "copy" will exist in any Move code compiled/loaded after that statement.

Apropos taste, I like your considerations to unify the function argument definitions and the key value assignments. A last word about my idea using = instead:

After thinking a while about it, I came to the idea that everything is best described by the word Scope. An Object is more or less about its own Scope. For example take a simple function:

f = ^{ x = 1 }
x is now valid in the scope of f. In my idea of unifying the syntax I could imagine this (note also that no , would be needed any more):

o = { x = 1 }
print o.x == 1
Here x is in the scope of the object. It is kind of a local variable that is persistent as long as the object persists.

Interesting way of looking at this. But while:

o = { x = 1 }
o.x == 1

This might cause confusion (or not, depending on if you fully understand the difference):

f = ^{ x = 1 }
f.x != 1

It is similar to the constructor idea of Javascript (Inspired by http://bonsaiden.github.com/JavaScript-Garden/#constructors ):

function cl() {
this.x = 1
}
o = new cl()
console.log(o.x)
But you will agree, this is a matter of taste and this is just a personal opinion and kind of train of thought. Maybe such a solution would also throw up a bunch of new problems too.

Definitely. Move will continue to use "=" only for assigning values to variables and ":" for all other kinds of key-value associations. It makes the design simpler and the differences from JS smaller.

holtwick about 11 hours ago

Oh, I already found the first problem ;) In my approach this would not work:

o = {'this': 1}

:P

Owner

rsms commented Mar 7, 2011

Re. the "copy"/"create" function:

Note that "copy" is an ambiguous name since the designated object is not copied although the word of the function suggest so, but rather a new, empty object is created and then assigned the designated object as its prototype, creating an inheritance chain.

A programmer coming from another language where "copy" means "copy" might expect this to be a valid Move program:

x = {a:1}
y = copy x
x.a = 2
y.a == 1

The last statement would not be true, since the "x" object's properties was never copied to "y", but rather "x" was assigned as "y"'s prototype:

x = {a:1}
y = create x
x.isPrototypeOf(y) == true

The last statement in the above code is, however, true.

holtwick commented Mar 7, 2011

Great! This was a very informative and constructive discussion. Thanks a lot. In my opinion all questions and ideas have been clarified. So if it is ok for you I will close this issue.

This issue was closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment