I'm reading through the spine.js documentation and I'm wondering a little bit about the function declarations.
In the doc i always read
constructor: ->
super
....
But otherwise i always read
constructor = ->
super
....
So are : and = equal for function declarations?
No.
When defining a class, the : works slightly differently than it does elsewhere. In a class definition the : means to put this function as a method on the prototype (instance method). constructor: is a special case to be used for the constructor function.
The difference is obvious if you look at the compiled javascript.
class Foo
constructor: -> #foo = 'foo'
method: ->
compiles to this (Good!)
var Foo;
Foo = (function() {
function Foo() {
this.foo = 'foo';
}
Foo.prototype.method = function() {};
return Foo;
})();
You can see the constructor is the constructor, and the method is on the prototype.
However, when you use = you simply assign local variables and the functions are not really part of the class as the constructor or the prototype:
class Bar
constructor = -> #bar = 'bar'
method = ->
Compiles to this (Bad!)
var Bar;
Bar = (function() {
var constructor, method;
function Bar() {}
constructor = function() {
return this.bar = 'bar';
};
method = function() {};
return Bar;
})();
Many issues about coffee script syntax can be discovered or resolved by simply looking at the compiled result. And this also why I do not recommend learning coffee script without also knowing JavaScript, as some of the things that it does for don't really make sense if you dont know what it compiles into.
Related
In Kotlin variables not declared as nullable or lateinit must be initialized in the constructor (or init). I am trying to do this like this:
class Foo{
var foo:myType
init{
complicatedFooInit()
}
fun complicatedFooInit(){
foo = //a whole bunch of code here
}
}
I still get the Property must be initialized or declared abstract error. You can easily reproduce this by making myType an Int and just setting it equal to 3 in the complicatedFooInit function. Obviously there are ways around this (just not making it a function, having complicatedFooInit return myType and setting foo equal to it, etc.). My question is, why is the above code invalid? Or is it valid with some tweaking?
Compiler have no idea what's going on inside complicatedFooInit() function (cause it may be too burden to investigate all execution flow of potentially dozens nested functions called from init block). He wants to see initialization directly inside init block. So you need to make complicatedFooInit() return desired value:
class Foo {
var foo: myType
init {
foo = complicatedFooInit()
}
fun complicatedFooInit(): myType {
//a whole bunch of code here
return ...
}
}
Actually, in this case property initialization on declaration site will be more concise (no need for init block at all):
var foo: String = complicatedFooInit()
Consider also that you can have multiple init blocks, so if you want to extract some part of initialization into a function, e.g.
init {
complicatedFooInit()
complicatedBarInit()
}
I would replace each function by a separate init block:
// comment about foo initialization
init {
// body of complicatedFooInit()
}
// comment about bar initialization
init {
// body of complicatedBarInit()
}
The problem is that you can't pass arguments to init blocks; but any arguments you'd pass to complicatedFooInit can only depend on the primary constructor parameters, and so can be set up in the beginning of the corresponding init block.
You also can't call the same function twice with different parameters,
init {
complicatedFooInit(true)
complicatedFooInit(false)
}
but you wouldn't want to anyway, because it would initialize foo twice; unless complicatedFooInit initializes different variables depending on its arguments, but it would make the compiler's burden mentioned in Михаил Нафталь's answer much worse!
I'm studying coffescript. I want an instance method (#generate_human) to run during every instantination of the class, but I'm getting error that the function doesn't exist. When I change the method to be the class method instead, everything works. What is going on?
class Human # As biological creature
constructor: (#given_sex = null,
#age = null, # Age of the person
#max_age = 85) -> # Maximum allowed age of the person during person generation
_alive = true
alive: ->
#_alive
dead: ->
not #alive()
has_died: ->
#_alive = false
_available_sexes: {0: 'female', 1: 'male'}
sex: ->
_sex = #_available_sexes[#given_sex]
generate_human: ->
#_alive = true
if #age is null
#age = Math.floor(Math.random() * #max_age)
if #given_sex is null
#given_sex = Math.floor(Math.random() * 2)
else if #given_sex not in [0,1]
n = #given_sex
err = 'Invalid sex value: ' + n
console.log(err)
throw new Error(err)
#generate_human() # In JavaScript this line throws an error that function 'this.generate_human()' does not exist
h = new Human()
At the class level, # (AKA this) is the class itself. When you say this:
class C
#m()
the # in #m() will be referring to the class itself, not an instance of the class. If you look at the JavaScript version you should see what's going on:
var C = (function() {
function C() {}
C.m(); // <---------- This is the #m() call.
return C;
})();
This lines up with the syntax for defining a class method:
class C
#m: ->
which becomes this JavaScript:
var C = (function() {
function C() {}
C.m = function() {}; // <--------- Here is the class method.
return C;
})();
If you want the method to run when an instance is created, call it in constructor:
class Human
constructor: (#given_sex = null, #age = null, #max_age = 85) ->
#generate_human()
#...
You probably want to look at what CoffeeScript does to that _alive = true too, that doesn't define a default #_alive instance variable, that is actually a private class variable of sorts; you probably want _alive: true instead.
Also, you should be very very careful and consistent with your whitespace in CoffeeScript, any variation or inconsistency can cause strange and puzzling errors. For example, I'd recommend against that format for your constructor definition and you'll have a better time if all your methods are at the same indentation level.
I want to call a function in initializer, but i couldn't do it
class someclass {
var a : Int
var b : Int
init() {
self.a = 5
self.b = 4
func printx () {
println("you called the function")
}
printx()
}
}
var ab = someclass()
Is it posible to do that something like ab.init().printx
And
Will this printx() function execute when i initialize with someclass()
For your questions: No - you can't reference your printx from outside of init and Yes - your printx will execute when init is called. In Swift 1.2:
20> class Bar {
21. var ping : Int
22. init () {
23. ping = 1
24. func bar () { println ("bar") }
25. bar()
26. }
27. }
28> Bar()
bar
$R4: Bar = {
ping = 1
}
Defining your printx in init is perfectly viable and you should demand that it works (and it does). Having lexically contained function definitions in Swift is critically important. Now, if the language could allow the function name to be recursively visible...
Maybe you would do something like this?
class someclass {
var a : Int
var b : Int
func printx () {
println("you called the function")
}
init() {
self.a = 5
self.b = 4
printx()
}
}
var ab = someclass()
ab.printx()
Consider you never call init() writing it but just with someClass()
This question simply demonstrates a lack of understanding when it comes to scope.
The function printx() is not a method on the class SomeClass, but it a function scoped within the particular init() method you've defined.
As written, printx() will execute (and will print) when you call the init() method:
See the box in the right pane at the top? That's showing us all of the console output from our playground, exactly as it would display if we ran this otherwise.
However, we cannot now call ab.printx() because as I stated earlier, printx() is not a member of the class SomeClass. If we want to call printx() on instances of SomeClass, we must then define it as an instance method of the class (and if we still want to call it in init, we can).
I am trying to create a sort of global, singleton object in my CoffeeScript code:
window.MyLib =
Foos: {}
Bars: {}
Client: ->
#_client ||= (
someVar = 'foo'
new ExpensiveClient(someVar)
)
# ...
MyLib.Client.performSomeAction()
However, the code actually requires me to do this:
MyLib.Client().performSomeAction()
How can I get around this problem of implementing a lazy instantiation without needing to use parenthesis to call it as a function?
Update: Alternatively, if there was a way to implement a sort of "method missing", the Client function could return an object that delegates the responsibilities to the actual client, but only instantiate it when it needs to.
Perhaps something like this:
lazyGet = (obj, name, property) ->
Object.defineProperty obj, name, get: property
window.MyLib = do ->
#_client = null
Foos: {}
Bars: {}
lazyGet this, 'Client', ->
#_client ||= (
someVar = 'foo'
new ExpensiveClient someVar
)
I would like to something like this in JavaScript
var init = function () {
// do some stuff once
var once = true
// overwrite the function
init = function () {
console.log(once)
}
}
CoffeeScript adds another local var init to the initial init so the second init doesn't overwrite the first one
var init = function () {
var init //automatically declared by coffeescript
// do some stuff once
var once = true
// overwrite the function
init = function () {
console.log(once)
}
}
Some tips for solutions / workarounds would be greatly appreciated.
(Update: The answer below was accurate at the time, under CoffeeScript 1.0.1. It is no longer the case under CoffeeScript 1.1.0, which fixed this issue.)
Wow, this surprises me. The CoffeeScript
init = ->
init = -> console.log once
declares both an outer init and an inner init. This strikes me as more likely a bug than a conscious language design decision—the compiler simpler evaluates the function before it evaluates the outer init = assignment. I've gone ahead and filed an issue on this.
Here's a workaround:
init = null
init = ->
init = -> console.log once
Now there's only one init, the one with the outermost scope.
I believe this is by design. You shouldn't rely on implicit globals. init is a property of the window/global object, so just reference it correctly:
window.init = ->
var once = true
window.init = ->
console.log once