CoffeeScript: access actual #(this) in fat arrow function => [duplicate] - coffeescript

The title says it all. When I use the fat-arrow in CoffeeScript, it stores this first before calling the function. For example:
class myClass
constructor: ->
element = $ "#id"
element.click ->
#myMethod(#value)
return
return
myMethod: (c)->
window.console.log(c)
return
would yield
var myClass;
myClass = (function() {
function myClass() {
var element;
element = $("#id");
element.click(function() {
this.myMethod(this.value);
});
return;
}
myClass.prototype.myMethod = function(c) {
window.console.log(c);
};
return myClass;
})();
Now on line#8 of JavaScript, this.myMethod is wrong. In this scope, this refers to element instead of the class MyClass.
However, if on line#4 of CoffeeScript, I replace element.click -> by element.click => the line#8 in JavaScript will become _this.myMethod(_this.val) where this referring to myClass is stored in _this before calling the function. But _this.value is undefined and even if it were defined, the object I'm trying to access here is element (which is referred to by the actual this keyword in scope of this function).
How would access the actual this now?

You can achieve your goal in at least three ways. The 1st one would be:
class myClass
constructor: ->
element = $ "#id"
element.click =>
#myMethod(element.value)
return
return
myMethod: (c) ->
window.console.log(c)
return
And the 2nd:
class myClass
constructor: ->
element = $ "#id"
myMethodCallback = (c) => #myMethod(c)
element.click ->
myMethodCallback(#value)
return
return
myMethod: (c) ->
window.console.log(c)
return
The 3rd one is as showed below. I'm not sure about jQuery API usage though, so better check on appropriate docs page.
class myClass
constructor: ->
element = $ "#id"
element.click (event) =>
#myMethod(event.target.value)
return
return
myMethod: (c) ->
window.console.log(c)
return
I would prefer the 1st way as it seems to be more straightforward.
This or the other but you need to decide 'which this' you would like to have in scope of the element.click callback. It's not possible to access two 'thises' at the same time.
By the way. All those return statements seems unnecessary. The shortest working solution would look like:
class myClass
constructor: ->
element = $ "#id"
element.click => #myMethod(element.value)
myMethod: (c) -> window.console.log(c)

Related

Why does this function application generate a runtime error in purescript?

I have the following PureScript snippets; note parseXMLFromString is partially applied:
parseXMLFromString ∷ String → DOMParser → Effect Document
parseXMLFromString s d =
parseFromString "application/xml" s d
parseNoteDoc :: DOMParser -> Effect Document
parseNoteDoc = parseXMLFromString TD.noteXml
note <- parseNoteDoc domParser
The following code is generated:
// Generated by purs version 0.12.4
"use strict";
var Effect_Console = require("../Effect.Console/index.js");
var Test_Data = require("../Test.Data/index.js");
var Web_DOM_DOMParser = require("../Web.DOM.DOMParser/index.js");
var parseNoteDoc = Web_DOM_DOMParser.parseXMLFromString(Test_Data.noteXml);
var main = function __do() {
var v = Web_DOM_DOMParser.makeDOMParser();
var v1 = parseNoteDoc(v)();
return Effect_Console.log("TODO: You should add some tests.")();
};
module.exports = {
parseNoteDoc: parseNoteDoc,
main: main
};
The line var v1 = parseNoteDoc(v)(); gives the error TypeError: parseNoteDoc(...) is not a function.
I'm not sure where the extra () is coming from on parseNoteDoc but that is the issue. When I manually remove the () in the generated source, it works works as expected.
Update: Added the code to reproduce this on this branch. After the usual formalities, npm run testbrowser and open dist/index.html in a browser.
TL;DR: your FFI code is incorrect, you need to add an extra function().
Longer explanation:
The extra empty parens come from Effect.
This is how effectful computations are modeled in PureScript: an effectful computation is not a value, but a "promise" of a value that you can evaluate and get the value as a result. A "promise" of a value may be modeled as a function that returns a value, and this is exactly how it's modeled in PureScript.
For example, this:
a :: Effect Unit
is compiled to JavaScript as:
function a() { return {}; }
and similarly, this:
f :: String -> Effect Unit
is compiled to JavaScript as:
function f(s) { return function() { return {}; } }
So it takes a string as a parameter, and then returns Effect Unit, which is itself a parameterless function in JS.
In your FFI module, however, you are defining parseFromString as:
exports.parseFromString = function (documentType) {
return function (sourceString) {
return function (domParser) {
return domParser.parseFromString(sourceString, documentType);
};
};
};
Which would be equivalent to parseFromString :: String -> String -> DOMParser -> Document - i.e. it takes three parameters, one by one, and returns a parsed document.
But on the PureScript side you're defining it as parseFromString :: String -> String -> DOMParser -> Effect Document - which means that it should take three parameters, one by one, and then return an Effect Document - which should be, as described above, a parameterless function. And it is exactly this extra parameterless call that fails when you try to evaluate that Effect Unit, which in reality is not an Effect at all, but a Document.
So, in order to fix your FFI, you just need to insert an extra parameterless function, which will model the returned Effect:
exports.parseFromString = function (documentType) {
return function (sourceString) {
return function (domParser) {
return function() {
return domParser.parseFromString(sourceString, documentType);
}
};
};
};
(it is interesting to note that makeDOMParser :: Effect DOMParser is correctly modeled in your FFI module as a parameterless function)
But there is a better way
These pyramids of nested functions in JS do look quite ugly, you have to agree. So it's no surprise that there is an app for that - EffectFn1, runEffectFn1, and friends. These are wrappers that "translate" JavaScript-style functions (i.e. taking all parameters at once) into PureScript-style curried effectful functions (i.e. taking parameters one by one and returning effects).
You can declare your JS side as a normal JS function, then import it into PureScript as EffectFnX, and call it using runEffectFnX where needed:
// JavaScript:
exports.parseFromString = function (documentType, sourceString, domParser) {
return domParser.parseFromString(sourceString, documentType);
};
-- PureScript:
foreign import parseFromString ∷ EffectFn3 String String DOMParser Document
parseHTMLFromString ∷ String → DOMParser → Effect Document
parseHTMLFromString s d =
runEffectFn3 parseFromString "text/html" s d
P.S. People who purchased EffectFn1 also liked Fn1 and friends - same thing, but for pure (non-effectful) functions.

How to parse/understand this coffeescript syntax?

I have come across this construct a few times.
Example 1:
_ = require 'underscore'
class Controller extends App
_doSomething: _.throttle (event) ->
$div = $(event.target).closest 'div'
...
My Question:
I have trouble understanding why this construct is valid.
Normally a class function definition goes like:
_doSomething: (event) ->
$div = $(event.target).closest 'div'
...
, 500
So how can _.throttle sit in between : and (event) the function parameter?
If _.throttle is supposed to act as an wrapper, shouldn't it be written as
_doSomething = _.throttle( (event) -> # an anonymous function that takes event as parameter
$div = $(event.target).closest 'div'
...
, 500
Things might be clearer if we add the optional parentheses:
class Controller extends App
_doSomething: _.throttle( (event) ->
$div = $(event.target).closest 'div'
...
)
Now we see that _.throttle is function call and it is being given an anonymous function as its single argument. We can break it down a bit more to further clarify what's going on:
f = (event) ->
$div = $(event.target).closest('div')
...
throttled_function = _.throttle(f)
class Controller extends App
_doSomething: throttled_function
_.throttle returns a function so the code you're looking at just just a complicated version of:
class Controller extends App
_doSomething: some_function
which is nothing special. Keep in mind that you can use anonymous functions when building a class's methods but named functions or other expressions which evaluate to functions serve just as well; the syntax is really:
name: expr
where expr is some expression and f(x) (or _.throttle(some_anonymous_function)) is an expression.

Coffeescript class shares properties

I've detected a very weird behavior in coffeescript.
class Foo
list: []
add: (val)->
#list.push(val)
x = new Foo()
x.add(1)
console.log(x.list.length) // 1
y = new Foo()
y.add(1)
console.log(y.list.length) // 2
So as you see the #list property got shared between the two class instances in a strange way.
I've never faced similar issue before, in coffeescript.
Convert it to JavaScript:
var Foo, x, y;
Foo = (function() {
function Foo() {}
Foo.prototype.list = [];
Foo.prototype.add = function(val) {
return this.list.push(val);
};
return Foo;
})();
As you can see, Foo.prototype.list is a property of the prototype, not of an instance of your class. There's only one array and it will be shared across all of the instances of your class.
To make list an instance variable, add it to the constructor:
class Foo
constructor: ->
#list = []
add: (val)->
#list.push(val)

How to use set methods to initialize instance variables via constructor in coffeescript

I have a "class" in coffee script whose instance variables I want to initialize with instance methods that return a value via a callback, but it doesn't work as I had hoped:
EventEmitter = require('events').EventEmitter
class MyClass extends EventEmitter
constructor: ->
#datamember: setDatamember()
setDatamember: ->
someFunction (response) ->
#datamember = response
#emit 'init'
getDatamember: ->
return #datamember
----
myObj = new MyClass
myObj.on 'init', ->
console.log myObj.getDatamember
The result I get suggests that "this" in setDatamember is referring to something different from what "this" refers to in the object instance. If I explicitly call myObj.setDatamember, I get the expected result, but is there any way to call on a set method -- specifically one that sets the data member via a callback -- in the constructor? I've looked through the docs, as well as various other sources of coffeescript info (e.g. this one), and I haven't found anything that touches upon this.
Try using a fat arrow for the anonymous function:
setDatamember: ->
someFunction (response) =>
#datamember = response
#emit 'init'
Also, you'll need to call the correct function in the constructor:
constructor: ->
#setDatamember()
In general, avoid fat arrows on methods - the way Coffee-Script implements this does some bad things to memory usage. Also, it will rarely be necessary.
However, anonymous functions that refer to this will almost always need fat arrows. this is not held in closure like normal variables, and will only be set by binding (Function.prototype.bind) or by calling it as an object method (obj.myMethod() will set this to obj in myMethod).
Try using fat arrows on everything except the constructor:
class MyClass
constructor: ->
#setDatamember()
setDatamember: =>
someFunction (response) =>
#datamember = response
getDatamember: =>
return #datamember
However, you also look to have someFunction in there as an asynchronous function, so you'll never be able to just do
mc = new MyClass
console.log mc.datamember
Because that doesn't wait for someFunction to return before accessing mc.datamember.

Accessing Variables in a CoffeeScript Class

In the following code, I was under the impression that using a 'fat arrow' would allow me access to the class variables. Instead, regardless of fat or skinny arrow, I am not able to access the '#accounts' variable.
Any suggestions?
class MyClass
accounts:[]
constructor: (#accounts) ->
($ '.the_buttons').live 'click', bind_clicks
bind_clicks = (event) ->
console.log #accounts
jQuery ->
m = new MyClass([1, 2, 3])
Thanks.
Update
Looks like I had mistyped previously causing a bit of my problem.
Here is code that sort-of does the trick
class MyClass
accounts:[]
constructor: (#accounts) ->
($ '.the_buttons').live 'click', (event) => bind_clicks(event)
bind_clicks: (event) =>
console.log #accounts
jQuery ->
m = new MyClass([1, 2, 3])
However, it feels odd to resort to making the bind_clicks a public method.
You can do that
class MyClass
bind_clicks = (event) ->
console.log #accounts
accounts:null
constructor: (#accounts) ->
($ '.the_buttons').live 'click', bind_clicks
jQuery ->
m = new MyClass([1, 2, 3])
bind_clicks is declared BEFORE the instance methods. It is private. BUT, it is a private class method. It will generate a closure, that's why. I would suggest to use _bind_clicks, the underscore prefix is a common convention to define private methods. Since this doesn't really exist in javascript.
Be careful also when you declare accounts:[], I would suggest accounts:null, more on this: http://html5stars.com/?p=148
Another possible way of doing this. It's pretty similar to yours.
class MyClass
constructor: (#accounts) ->
$('body').live 'click', #bind_clicks
bind_clicks: (event) =>
console.log #accounts
$ ->
m = new MyClass([1, 2, 3])
Don't quote me on this, but I don't think JS has any notion of public/private methods. I supposed if you don't want to expose the method you could do this instead. But you might run the risk or repeating yourself if you use this method more than once.
class MyClass
constructor: (#accounts) ->
$('body').live 'click', (event) =>
console.log #accounts
$ ->
m = new MyClass([1, 2, 3])
Good luck!
Sandro
note: I bounded the events from the body so I could test out the code.
You can make bind_clicks private as follows:
class MyClass
accounts:[]
constructor: (#accounts, #button) ->
bind_clicks = (event) =>
console.log #accounts
#button.click bind_clicks
class Button
click: (#f) ->
do_click: -> #f('event')
button = new Button()
m = new MyClass([1, 2, 3], button)
console.log m.bind_clicks # undefined (private)
button.do_click() # [1, 2, 3]
You can try this out: http://jsfiddle.net/zp6LJ/1/
The private method will have to be defined int he constructor function to achieve what you are looking for.
class MyClass
accounts:[]
constructor: (#accounts) ->
bind_clicks = (event) =>
console.log #accounts
$('.the_buttons').click bind_clicks
jQuery ->
m = new MyClass([1, 2, 3])