Skip to content
New issue

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

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

export keyword #121

Closed
ghost opened this issue Apr 7, 2012 · 24 comments
Closed

export keyword #121

ghost opened this issue Apr 7, 2012 · 24 comments

Comments

@ghost
Copy link

ghost commented Apr 7, 2012

I was wondering if it'd be possible to add an export keyword. The reason is that in some JavaScript environments (hint hint, QtScript) people tend to require certain variable on the global scope. However, with the QtScript transition to V8, adding them to the "this" property will become a strict mode error if you don't use .call on a method, furthermore, many implementations don't posses a window-like object for the global scope.

The solution is to use the bare mode, but this isn't desirable. Instead I would propose the use of a special keyword, export.

a = 1
function randomNumber
 ...
export function eventAttacked victim, attacker
 ...

Might compile to:

var eventAttacked;
(function(){
 var a;
 eventAttacked = function (victim, attacker) { ... }
 function randomNumber () { ... }
 a = 1;
})();
@satyr
Copy link
Owner

satyr commented Apr 7, 2012

adding them to the "this" property will become a strict mode error

You mean, the global object is frozen by default in that environment or something? Then you shouldn't be able to add global variables at all.

Running

<script>
"use strict"
var a
Object.freeze(this)
a = 1
</script>

on Firefox 11 gives Error: a is read-only.

@ghost
Copy link
Author

ghost commented Apr 8, 2012

"use strict"

function f () {
 this.q = 3;
}
f()
console.log(q);
ryan@R26695g /tmp $ node file

node.js:201
        throw e; // process.nextTick error, or 'error' event on first tick
              ^
TypeError: Cannot set property 'q' of undefined
    at f (/tmp/file:4:9)
    at Object.<anonymous> (/tmp/file:6:1)
    at Module._compile (module.js:432:26)
    at Object..js (module.js:450:10)
    at Module.load (module.js:351:31)
    at Function._load (module.js:310:12)
    at Array.0 (module.js:470:10)
    at EventEmitter._tickCallback (node.js:192:40)

In strict mode, you can't use this inside a function that's not a property of an object or called with the call method. The compiler wraps all your code in a function. Using the bare option defeats the safeguards.

Edit: Fixed

@satyr
Copy link
Owner

satyr commented Apr 8, 2012

That's why we use .call(this) rather than just ().

@ghost
Copy link
Author

ghost commented Apr 8, 2012

@satyr
Sorry, maybe this(coco example) better shows my point:

"use strict"

this.x = 0
x := 3 # error
x = 4 # wrong
function e
 x := 2 # error
 this.x = 3 # error

Yes, it can be gotten around, but there are times when your environment does stupid things...

@satyr
Copy link
Owner

satyr commented Apr 8, 2012

x := 2 # error

How?

this.x = 3 # error

Depends on how e is called.

@ghost
Copy link
Author

ghost commented Apr 8, 2012

Well, you have to remove the line marked #wrong to get the error. It's an "undeclared variable" error.

And I don't want to call e with funny calls every time, or bind every function in my code. I think an export would be a better solution, it's much less coding.

@satyr
Copy link
Owner

satyr commented Apr 8, 2012

Assuming your intention at this.x = 3 is to change global x,

global = this
function e
  global.x = 3

is what you want.

@ghost
Copy link
Author

ghost commented Apr 8, 2012

Isn't that rather verbose?

And the issue is when I want to do that with a function.
do f
export function f

I would have to opt for the much less readable global.f = function ... for every function in my code which needs to be put on the global object.

@satyr
Copy link
Owner

satyr commented Apr 8, 2012

less readable global.f = function

On top level that's merely @f = ->.

@ghost
Copy link
Author

ghost commented Apr 8, 2012

I'm not always going to be on the top level, if I want to reference it, I have to make the global object a variable, or call all my functions using .call @

global = this
@a = !(b, c) ->
 d
function e
 global.a 3, 6

I think export would be easier, it hurts my eyes to see functions assigned, especially with all that extra stuff...

Edit: Fixed.

@satyr
Copy link
Owner

satyr commented Apr 8, 2012

@a = (b, c) !->

Try @a = !(b, c) ->.

@ghost ghost closed this as completed Apr 8, 2012
@ghost ghost reopened this Apr 8, 2012
@ghost
Copy link
Author

ghost commented Apr 8, 2012

The @a syntax does NOT hoist the functions. I have multiple files, and if I run code in part of it, a function may be in another file. It might end up below code that requires it. A good example is a util file: The functions have to be hoisted, if they aren't, they wont be available to most files. (those that start with anything which comes before u.)

I'm aware you don't like using multiple files and joining them. But we don't all live in a node.js environment where we can require the other files.

@satyr
Copy link
Owner

satyr commented Apr 8, 2012

Ah, hoisting. In that case you do need export function I guess. See #116 and the mockup I wrote there.

@ghost
Copy link
Author

ghost commented Apr 8, 2012

Looks good. Though I would make export work with regular variables as well.

@shesek
Copy link

shesek commented Apr 8, 2012

I would personally rather have export exporting to exports if it exists (as in the #116 mockup, where __out is set to exports ? this).

Currently I'm using a modified version of CoffeeScript where export sets any expression that can have a name determined for on exports, which includes variables, classes and assignments. It looks like that:

macro func 'export', (expr) -> assign(
  objAccess (value 'exports'),
    if      expr instanceof Class  then expr.determineName()
    else if expr instanceof Assign then expr.variable
    else if expr instanceof Value  then expr
    else throw new Error 'Cannot determine export name'
  expr
)
$ bin/coffee -bep 'export foo = (a,b) -> c'
var foo;

exports.foo = foo = function(a, b) {
  return c;
};
$ bin/coffee -bep 'export bar'
exports.bar = bar;
$ bin/coffee -bep 'export class Baz'
var Baz;

exports.Baz = Baz = (function() {

  function Baz() {}

  return Baz;

})();

I've been using that for some time now and its been working out quite well for me.

@satyr
Copy link
Owner

satyr commented Apr 9, 2012

We can further define:

export x, y         #=> export x; export y
export {x: y}       #=> __out <<< {x: y}
export [x, @y] = z  #=> what should this do?

export class Bar

The top level this equals exports in Node though, so you'd rather write class @Bar.

@satyr satyr closed this as completed in 7194d43 Apr 11, 2012
@ghost
Copy link
Author

ghost commented Apr 12, 2012

Is it possible to make the whole function return the exported variables? For when some environments use eval on your script...

@satyr
Copy link
Owner

satyr commented Apr 12, 2012

Like how?

@ghost
Copy link
Author

ghost commented Apr 12, 2012

export function f
 a()
(function(){
 var __returned = false, __result = {}, __out = typeof exports != 'undefined' && exports || this;
 function f () { 
  a();
 }
 __out.f = f;
 if (!__returned ) __result.f = f;
 __returned = true;
 return __result; 
}).call(this);

Faster way might be to make the code return the "this" object, but that might cause problems.

@satyr
Copy link
Owner

satyr commented Apr 12, 2012

Too bombastic for a relatively narrow use case.

I guess you can just add return this at the end. Ideally your environment should provide meaningful exports or this.

@ghost
Copy link
Author

ghost commented Apr 12, 2012

"use return_exports"
?
Then you don't even bother with the other code. (__out)

And for the record, one QtScript environment I script for DOES use eval on your script. It's stupid, I know. They all seem to be stupid somehow, seems to be a thing with programs which use QtScript.

Edit: Just realised this wont work, since there's no way to put the "use return_exports" at the top of the script for sure...

@satyr
Copy link
Owner

satyr commented Apr 12, 2012

It does sound like hard to make QtScript code reusable in browser/CommonJS.

Maybe you can abuse hoisting:

$ coco -sp
  eval Coco.compile """
    export a = 1
    return exports
    function exports then
  """

{ [Function: exports] a: 1 }

@shesek
Copy link

shesek commented Apr 12, 2012

export class Bar

The top level this equals exports in Node though, so you'd rather write class @Bar.

But than the constructor won't be accessible in scopes with different
this, unless you manually assign it to Bar too (Bar = class @Bar)

@satyr
Copy link
Owner

satyr commented Apr 12, 2012

Hm, this at-name linking is kinda annoying.

the constructor won't be accessible in scopes with different this

It will:

$ coco -bcs
  class @Bar then  # same as: @Bar = Bar = class then

var Bar;
this.Bar = Bar = (function(){
  Bar.displayName = 'Bar';
  var prototype = Bar.prototype, constructor = Bar;
  function Bar(){}
  return Bar;
}());

josher19 pushed a commit to josher19/LiveScript that referenced this issue Aug 1, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants