Skip to content

Instantly share code, notes, and snippets.

@timruffles
Created July 11, 2011 13:41
Show Gist options
  • Save timruffles/1075862 to your computer and use it in GitHub Desktop.
Save timruffles/1075862 to your computer and use it in GitHub Desktop.
move lang notes
nice = {'^':"it looks like a lambda! Also, if you pick a random point in time, I'm probably typing 'functio...' (-> now", "foo, 'bar', baz":"it's nicer than #{} even, especially with implicit spaces (tho how do you disable that? eg with 'foo, 'string'')", "semantic whitespace":"newbies don't like typing line noise like. semantic whitespace is visually intuative"}
suggestions_for_improvements = {"not being able to escape quotes":"it's awkward", "cmr-r being broken":"If I type r's in the repl, with a syntax error, the cmd-r shortcut often fires and I can't type", "== and === ":"is and isnt are more comprehensible to non-nerds", "the lack of for comprehensions":"functional iterators are less friendly to non-nerds"}
explain = ^(explainer, list) { Object.keys(list).forEach ^(reason) { print explainer, reason, "because", list[reason] } }
explain "I like", nice
explain "I don't like", suggestions_for_improvements
bank_account = ^(balance) {
^(cmd,amount) {
amount || (amount = 0)
if(cmd == "withdraw") bank_account(balance - amount)
else if(cmd == "deposit") bank_account(balance + amount)
else if(cmd == "balance") balance.toFixed(2)
else "What kind of person would try to ", cmd, " with a bank account?"
}
}
my_account = bank_account(1000)("withdraw", 50)("deposit", 110)
print "You have £", my_account("balance"), "don't spend it all at once"
backup_balance = my_account "deposit"
current = my_account("withdraw",96)
print "I had £", backup_balance("balance"), "before last night! Now I have £", current("balance")
@rsms
Copy link

rsms commented Jul 12, 2011

=D

@timruffles
Copy link
Author

Also spotted the embedded HTML feature, nice.

A super feature would be importing sugar for async modules (require.js etc), as they're more verbose than synchronous.

@rsms
Copy link

rsms commented Jul 12, 2011

In reply to

suggestions_for_improvements = {

1

"not being able to escape quotes":"it's awkward",

What do you mean by this? The following is all valid and prints "hello" or 'hello':

print "\"name\""
print '\'name\''
print '"name"'
print "'name'"

2

"cmr-r being broken":"If I type r's in the repl, with a syntax error, the
 cmd-r shortcut often fires and I can't type",

On the website at /try/? It works for me in the browsers I've tried. Or do you mean in Node.js?

3

  "== and === ":"is and isnt are more comprehensible to non-nerds",

Good point. Although adding is (as an alias for ==) and isnot (alias for !=) is simple, it's a bit ambiguous -- "whats the difference between == and is?" one might ask herself. Also, in some other languages there's a difference.

Anyhow, I might just add this feature.

4

"the lack of for comprehensions":"functional iterators are less friendly to non-nerds"

Any suggestions for improvement? Remember Move is not line-based. Also, I based the development of Move on actual interviews with people not familiar with neither programming nor math and one interesting thing I found out -- to my surprise -- was that no one had any difficulties with curly braces or parenthesis. That's why I decided to stick with curly braces so to keep a low level of abstraction.

For instance, keyword arguments are written just as an object:

foo {name:"Bar"}

Versus assigning the object to a variable:

foo = {name:"Bar"}

5

A super feature would be importing sugar for async modules (require.js etc), as they're more verbose than synchronous.

Now you lost me. "more verbose than synchronous"? And what's sugar?

I'm open for suggestions and improvements!

@rsms
Copy link

rsms commented Jul 12, 2011

Here's an idea for comprehensions:

https://gist.github.com/1078518

@timruffles
Copy link
Author

Re-wrote the below as Github stripped the links when I replied via email:

1 & 2

I must have got something wrong, or put /try into an odd state. Sorry about that!

3

Cool!

4

I've not done much language implementation so I'll read the source and come back to you. Interesting to hear the results of that research.

5

Sugar = syntactic sugar.

I'm using require.js at the moment, which in the browser uses the <script> inclusion technique for asynchronous script loading. Requiring with it looks like.

require(["foo/bar"],function(Bar) {

  // use asynchronously loaded modules

});

The above can get really verbose when you're importing lots of modules, or dealing with multiple levels of require

require(["require","x","y"],function(require,X,Y) {
  require(["a","b"],function(A,B) {

  });
})

So the feature would let you use:

import "foo/bar" 

and you'd be sure all the code beyond that point can access Bar. I guess it'd be a compiler flag to target the browser by translating the imports calls to the require/define with function wrapper.

I'm open for suggestions and improvements!

I've wanted to write language features since seeing how intuitive uglify's parse tree is. If I've some time this weekend I'll have a stab at it :)

This is probably out of scope for move, but I often think JS would benefit from full message passing. Then you could define meaningful semantics for all operators (=, >, * + and friends). Best for value objects like Date.

This is effectively reimplementing parts of Ruby in Javascript, but then that's what I end up doing without it (and everyone else? dojo#set and dojo.data, Backbone#set etc). Perhaps "Any sufficiently complicated Javascript program contains an ad-hoc, informally-specified, bug-ridden, slow implementation of half of Ruby"?.

Really rough sketch, bit too late to be thinking about this.

@rsms
Copy link

rsms commented Jul 13, 2011

Move actually includes a CommonJS module system. Modules written in Move are automatically made into modules. Other modules (e.g. written in JavaScript) need to be explicitly defined. Like this:

 move.require.define('foo/bar', function (require, module, exports) {
   // ... module source
 });

Prototype:

move.require.define(String id, [String uri,] block(require, module, exports){...})

@rsms
Copy link

rsms commented Jul 13, 2011

When it comes to message passing I don't really get it. To me, "message passing" is basically IPC/RPC. Makes little sense as a language construct imho.

Talked to Rob here and we tried to understand what you meant and have an idea that you might be talking about fancy setters..? Something like:

cow.set({
  name: 'John',
  age: 36
})

cow += {
  name: 'John',
  age: 36
}

cow.name = 'John'
cow.age = 36

extend cow, {
  name: 'John',
  age: 36
}

?

@timruffles
Copy link
Author

Message passing leaves the implementation of the behaviour stemming from a message up to the object. It conforms to the uniform access principle, which states there should be "no difference between working with an attribute, precomputed property, or method/query". Apple have a good discussion on page 16.

Javascript doesn't have this because of the "property bag" nature of its objects. When I'm calling a method foo.bar(), it can only ever mean deference/"take out" the object stored at bar in foo, and then try to call it. This limits the agency of an object severely - I can't define a Vector type and perform vector multiplication with the * operator. For instance vectorA * vectorB in Ruby has to be written as vectorA.multply(vectorB) in Javascript. Otherwise, in Javascript, vectorA * vectorB means deference foo, convert it to a number, and times that by bar as a number, and there's nothing you can do about it - the interpreter has defined it.

The best way to see this is to consider the difference between foo.bar = baz in Ruby and Javascript. In Ruby that's foo.bar=(baz), which is foo.send(:bar=,baz). foo, as receiver, can do whatever it wants in receipt of the :bar= message via its send method. The behaviour is up to the object (and therefore, programmer), not the interpreter.

It's most important in implementing generic behaviour like comparability. I've shown the implementation of <=>, I probably should have demonstrated it a bit. In Ruby, defining <=> and including Comparable makes an object is sortable by any Enumerable function. You don't need to know the implementation of that object, you just know you can sort it with the normal operators.

Message passing's total decoupling of implementation and behaviour means you can swap out the whole implementation of an object without the client code needed to change. If you've ever needed to add a guard to a property in Javascript which was previously set like foo.bar = baz, you can see why the "property bag" is less flexible that something conforming to the uniform access principle.

It's something that may appear trivial for programming until you think about how much more semantic intent can be expressed by

periodOne + periodTwo

than

new Date((+periodOne) + (+periodTwo))
// because in JS...
periodOne + periodTwo 
// evaluates to the string "Wed Jul 13 2011 06:59:06 GMT+0100 (BST)Wed Jul 13 2011 06:59:08 GMT+0100 (BST)"

Because I can't define the behaviour for periodOne.+(periodTwo), I need to convert to a Number(), the sum, then pass to the overloaded Date constructor. This could be fixed for built-in types like Date by improving the interpreter, but can't be for user defined types, like matrix maths as I discussed above, so you'll need to use m.multiply(b).multiply(c) not m * b * c.

In Javascript you have to do ugly things like the former (and dojo._Widget#get etc) because the implementation of objects as fancy hashes has limited your control as a programmer. That's fine for simple programs, but it's silly that the highest level object primitive you have to work with is a hash for all intents and purposes. All the expressiveness and flexibility of Ruby stems from message passing giving the programmer control of what happens when an object receives a message.

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