CoffeeScript: lazy evaluation without using parenthesis - coffeescript

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
)

Related

What is the difference between "lazy var funcName: () -> Void" and regular function declaration in Swift

I have recently encountered such code block when I was working on a different codebase.
I was wondering is there any significant difference (in terms of memory impact etc.) between these two declarations other than syntactic ease? I regularly use lazy stored properties but I couldn't visualize how a function can be "lazy". Can you guys also enlighten me how functions are processed (or share an article explaining this topic) by the swift compiler as well?
Thanks in advance, wish you bug free codes.
A few differences I can think of off the top of my head:
With functions, you can use parameter labels to make the call site more readable, but since you can't add parameter labels to function types, you can't do that with the lazy var declaration.
// doesn't work
lazy var say: (message: String, to person: String) -> Void = {
...
}
// works
func say(message: String, to person: String) {
...
}
You can only invoke a function type with no parameter labels at all :( say("Hello", "Sweeper") instead of say(message: "Hello", to: "Sweeper").
lazy vars are variables, so someone could just change them:
helloFunc = { /* something else */ }
you could prevent this by adding a private setter, but that still allows setting it from within the same class. With a function, its implementation can never be changed at runtime.
lazy vars cannot be generic. Functions can.
// doesn't work
lazy var someGenericThing<T>: (T) -> Void = {
...
}
// works
func someGenericThing<T>(x: T) {
...
}
You might be able to work around this by making the enclosing type generic, but that changes the semantics of the entire type. With functions, you don't need to do that at all.
If you're implementing a language, the magic you need to implement 'lazy' as a feature is to make the value provided silently be wrapped in a function (of no arguments) that is automatically evaluated the first time the property is read.
So for example if you have
var x = SomeObject() // eagerly construct an instance of SomeObject
and
lazy var x = SomeObject() // construct an instance if/when someone reads x
behind the scenes you have, for the lazy var x, either x has not yet been read, or it has been and therefore has a value. So this is like
enum Lazy<T> {
case notReadYet(() -> T) // either you have a function that makes the initial T
case read(T) // or you have a value for T
}
var thing: Lazy<SomeObject> = .notReadYet { return SomeObject() }
and with a suitable property wrapper or other language magic, wrap the calls to the getter for thing with a switch on the case, which checks if you are in the notReadYet case, and if so automatically invoke the function to produce the T and then set the property to .read(t) for the particular value of t.
If you substitute in your type: () -> Void from your example you have something like:
var thing: Lazy<() -> Void> = .notReadYet({ return { print("Hello") } })
This is all rather odd because it's a void value and not much point making it lazy, e.g. it's not expensive to compute; it doesn't help break a two-phase initialisation loop, so why do it?

How to create a prefix function in swift for a property wrapper just like "$" in SwiftUI

Is there any public API that allows creating access to some certain properties inside a property wrapper? Just like '$' does with 'projectedValue'.
I found that it's possible to create prefix functions, however I'd need to know that a property of a class is wrapped.
It's not valid code but i hope you get the idea.
prefix func %<T: Wrapped>(value: T) -> GenericType<T> {
value.accessWrapper.propertyInsidePropertyWrapper
}
so in a class
class Test {
#MyWrapper var pTest = ""
}
I could access 'propertyInsidePropertyWrapper' of 'pTest' via '%pTest'.
Consider this property wrapper:
#propertyWrapper
struct MyWrapper<T> {
var wrappedValue: T
var foo: Int
}
Let's say you want to access foo with a prefix. You could just define the projected value as foo:
var projectedValue: Int { foo }
then you can use $ to access foo.
But let's say you actually want the projected value for something else, and would like a new prefix for foo. You could create a new operator:
prefix operator %
prefix func %<T>(wrapper: MyWrapper<T>) -> Int {
wrapper.foo
}
The problem is, how do we get ourselves a MyWrapper<T>? Let's consider:
struct Baz {
#MyWrapper(foo: 777)
var hello = ""
func f() {
// we want to access the "foo" of "hello" here
}
}
In this case, we could use _hello to access the MyWrapper<String> instance:
func f() {
print("Foo: \(%_hello)")
}
The catch is, that _hello is private, so if you want to access it outside of Baz, you need to change the projected value of MyWrapper to return self,
var projectedValue: MyWrapper<T> { self }
and do:
let baz = Baz()
print("Foo: \(%baz.$hello)")
And you would need to think of another prefix for your original projected value. If you are running out of ideas of what prefix to use, perhaps it is a bad idea to use prefixes to access properties. Just do it with names instead:
_hello.foo
$hello.foo // outside of the class, with the projected value returning self
Also note that the built in $ prefix is not a prefix operator. It is part of the identifier, hardcoded in the syntax of Swift as:
identifier → property-wrapper-projection
property-wrapper-projection → $ identifier-characters
OTOH, a prefix operator must be before a postfix expression (from Prefix Expressions):
prefix-expression → prefix-operator(opt) postfix-expression
Hence why %baz.$hello, not baz.%$hello.

Why does instance method doesn't work?

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.

Declare dynamically added class properties in TypeScript

I want to assign properties to the instance of a class without knowing the property names, values and types of values in TypeScript. Lets assume we have the following example.ts script:
// This could be a server response and could look totally diffent another time...
const someJson:string = '{ "foo": "bar", "bar": "baz" }'
class MyClass {
someProperty:boolean
constructor( json:string ) {
const parsedJson:any = JSON.parse( json )
Object.keys( parsedJson ).forEach(
( key:string ) => {
this[ key ] = parsedJson[ key ]
}
)
this['someProperty'] = true
}
}
const myInstance = new MyClass( someJson )
// Works fine, logs `true`.
console.log( myInstance.someProperty )
// Error: Property 'foo' does not exist on type 'MyClass'.
console.log( myInstance.foo )
// Error: Property 'bar' does not exist on type 'MyClass'.
console.log( myInstance.bar )
How can I make sure that the TypeScript compiler does not complain of the dynamically added properties but instead handle them as "key": value pairs of any type. I still want tsc to make sure that myInstance.someProperty has to be of type boolean but I want to be able to get myInstance.whatever even if it is not defined without running into compiler errors.
I did not find any documentation that makes this clear to me. Maybe because I'm not a native english speaker. So please keep the answers simple.
Edit:
I remember that there was something like the following but I never got that to work:
interface IMyClass {
[name:string]: any
}
The problem is that you're adding the new properties at runtime and the compiler has no way of knowing that.
If you know the property names in advance then you can do this:
type Json = {
foo: string;
bar: string;
}
...
const myInstance = new MyClass(someJson) as MyClass & Json;
console.log(myInstance.foo) // no error
Edit
If you do not know the properties in advance then you can't do this:
console.log(myInstance.foo);
Because then you know that foo is part of the received json, you'll probably have something like:
let key = getKeySomehow();
console.log(myInstance[key]);
And this should work without an error from the compiler, the only problem with that is that the compiler doesn't know the type for the returned value, and it will be any.
So you can do this:
const myInstance = new MyClass(someJson) as MyClass & { [key: string]: string };
let foo = myInstance["foo"]; // type of foo is string
let someProperty = myInstance["someProperty"]; // type of someProperty is boolean
2nd edit
As you do know the props, but not in the class, you can do:
type ExtendedProperties<T> = { [P in keyof T]: T[P] };
function MyClassFactory<T>(json: string): MyClass & ExtendedProperties<T> {
return new MyClass(json) as MyClass & ExtendedProperties<T>;
}
Then you simply use it like so:
type Json = {
foo: string;
bar: string;
};
const myInstance = MyClassFactory<Json>(someJson);
Note that this will work only on typescript 2.1 and above.
If you want to dynamically add class properties via an object upon instantiation, and type information is available for that object, you can very nicely get full type safety in this way (as long as you don't mind using a static factory method):
class Augmentable {
constructor(augment: any = {}) {
Object.assign(this, augment)
}
static create<T extends typeof Augmentable, U>(this: T, augment?: U) {
return new this(augment) as InstanceType<T> & U
}
}
This is using the (fake) this parameter to infer the constructor type of the class. It then constructs the instance, and casts it to a union of the instance type (using the InstanceType utility type) and the inferred type of the props you passed to the method.
(We could have casted directly to Augmentable & U, however this way allows us to extend the class.)
Examples
Augment basic properties:
const hasIdProp = Augmentable.create({ id: 123 })
hasIdProp.id // number
Augment with methods:
const withAddedMethod = Augmentable.create({
sayHello() {
return 'Hello World!'
}
})
withAddedMethod.sayHello() // Properly typed, with signature and return value
Extend and augment, with this access in method augments:
class Bob extends Augmentable {
name = 'Bob'
override = 'Set from class definition'
checkOverrideFromDefinition() {
return this.override
}
}
interface BobAugment {
whatToSay: string
override: string
sayHelloTo(to: string): void
checkOverrideFromAugment(): string
}
const bobAugment: BobAugment = {
whatToSay: 'hello',
override: 'Set from augment'
sayHelloTo(this: Bob & BobAugment, to: string) {
// Let's combine a class parameter, augment parameter, and a function parameter!
return `${this.name} says '${this.whatToSay}' to ${to}!`
},
checkOverrideFromAugment(this: Bob & BobAugment) {
return this.override
}
}
const bob = Bob.create(bobAugment) // Typed as Bob & BobAugment
bob.sayHelloTo('Alice') // "Bob says 'hello' to Alice!"
// Since extended class constructors always run after parent constructors,
// you cannot override a class-set parameter with an augment, no matter
// from where you are checking it.
bob.checkOverrideFromAugment() // "Set from class definition"
bob.checkOverrideFromDefinition() // "Set from class definition"
Limitations
Augmented properties aren't really part of the class, so you can't extend a class with those augments included. This may be a feature for some use cases where the augments are temporary additions that aren't meant to modify the prototype hierarchy
It is also not easy to add non-augment arguments to .create(), however an easy work-around is to simply utilize the augment functionality to accomplish the same thing you would have with an extra argument.
You can add index signature to your class:
class MyClass {
[index: string]: any; //index signature
someProperty:boolean
constructor( json:string ) {
const parsedJson:any = JSON.parse( json )
Object.keys( parsedJson ).forEach(
( key:string ) => {
this[ key ] = parsedJson[ key ]
}
)
this['someProperty'] = true
}
}

Coffeescript function declaration func: -> or func = ->?

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.