My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.
Showing posts with label Strict. Show all posts
Showing posts with label Strict. Show all posts

Friday, August 20, 2010

Object.defineProperty ... but Strict!

In my precedent post entitled A Pascal record via JavaScript I have showed a basic function able to emulate type hints behavior via JavaScript.
Even if that was a proof of concept, I consider other languages simulation of unsupported features an error, first of all because the behavior will rarely be exactly the expected one, secondly because our curent programming language may already have something similar to better learn and use.

A new ES5( direction )

As soon as I have written the Pascal example, I have realized that the good "old" Object.defineProperty, implemented in all major browsers (IE < 9 sucks, you know that ...), has basically the same meaning: define object accessors.

The only missing part, intrinsic in the JavaScript nature, is the property type, where this type could be eventually used for arguments or returns checks when the property is a method.

My Early Attempts

Back in May 2007, my JavaStrict experiment was already trying to do something similar, something implemented after 2 years even in dojo framework as well.
This may tell us that "somebody" would really like to put this kind of checks on daily code, even if performances cannot obviously be the best possible one.

Production VS Deployment

Keeping always in mind that performances matter, the new little monster I am going to show here can be flagged with a boolean, nothing different from:

Object.defineStrictProperty.production = true;

Once in place, the whole logic will switch from "strict type" to "free type" and the used function will be the native Object.defineProperty respecting basically the same behavior.

Object.defineStrictProperty / ies

I have put the code in devpro.it, while I am going to show how it works here.

How to declare a type

Next piece of code is the most basic usage of this method.

var myObject = Object.defineStrictProperty({}, "name", {
type: "string"
});

myObject.name = "it's a me!";

alert(myObject.name);

myObject.name = 123;

// throw new TypeError("wrong type");
//
// console.log =>
// Object
// > expected: "string"
// > received: 123



The meaning of "type"

The type property can contain any of these values:
  • string, where the check will be performed against the typeof value (e.g. "string", "number", "boolean", etc)

  • function, where the check will be performed against the value instanceof type (e.g. Function, Object, Array, Date, etc)

  • object, where the check will be performed against the type.isPrototypeOf(value) (e.g. Array.prototype, others ...)

The only exceptions are null and undefined.

How to declare a returned value

If the property type is "function" or Function there are other extra properties we could define such returns and arguments. Here there is a returns example:

var myObject = Object.defineStrictProperties({}, {
name: {
type: "string",
value: "it's a me!"
},
toString: {
type: "function",
returns: "string",
value: function () {
return this.name;
}
}
});

alert(myObject); // it's a me!

myObject.toString = function () {
return 456;
};

alert(myObject);

// throw new TypeError("wrong return");
//
// console.log =>
// Object
// > expected: ["string"]
// > received: 456


Multiple returns

Since JavaScript is dynamic, there is nothing wrong, logic a part, into different returns. To make it possible we simply need to specify an array with all expected types.

// precedent code with this difference
...
toString: {
type: "function",
returns: ["string", "number"],
value: function () {
return this.name;
}
}

// precedent example won't fail now


How to declare expected arguments

Similar returns concept applied to arguments, so that we can specify a single argument type, a list of arguments type, a list of arguments. Here the difference:

...
setName: {
type: "function",
// checks if a single argument
// with typeof string has been sent
arguments: "string",
// same as above
arguments: ["string"],
// checks if two arguments
// with typeof string and number
// has been sent, order matters!
arguments: ["string", "number"],
// checks if one argument
// with typeof string OR number
// has been sent
arguments: [["string"], ["number"]],
value: function (name) {
this.name = name;
}
}
...

The number of arguments and relative overload per call is arbitrary. As example, we could have a method defined with these arguments without problems.

// generic object for Ajax calls
...
get: {
type: "function",
returns: "string",
arguments: [["string"], ["string", "string", "string"]],
value: function (uri, user, pass) {
this.open("get", uri, false, pass && user, pass);
this.send(null);
return this.responseText;
}
}
...

With a simple logic like that we can "enforce" the program to send one argument or three when, if three, all of them will be checked as strings.

Unlimited arguments

We may decide that the first argument is the only one we would like to check. Well, in this case we can simply create a group of allowed types and ignore the rest.
The arguments logic should be flexible enough due JavaScript nature where overloads can only be simulated via number or tye of arguments checks.

Getters and Setters

The whole logic behind is based on get/set property descriptor, where a nice fallback to __defineGetter__ or __defineSetter__ will be used when Object.defineProperty is missing.
This means that we can describe properties the same way we could do via Object.defineProperty, using enumerable and others as well to make the whole code gracefully degradable when the production flag is set to true.

As Summary

Unless developers won't be interested into this approach, I think this method is basically complete as is and we can use it at own risk.
The whole script stays in about 870 bytes minified and gzipped, not a big deal for what it offers.
In any case, I am looking forward for your feedback!

Saturday, May 23, 2009

[ECMAScript 5] Do Not Remove arguments.callee !

Being subscribed in dunno how many developers mailing list, I completely forgot to follow up the arguments and arguments.callee discussion.
Accordingly to this John post, is with extreme surprise that I am discovering how critical is the gap between programming language developers and programming languages active users.

Even if I read more than once piece of SpiderMonkey or Google Chrome, I am part of programming languages users and I can tell you for sure that the decision to remove arguments.callee from the language will be a complete mess.

The Main Aim Of ECMAScript 5: Do Not Break What We Have So Far

Unfortunately, Internet Explorer will be broken, because named function persists in the scope, causing naming conflicts everywhere!


setTimeout(function f(){
alert(window.f);
}, 1000);

Above code will alert the function f in every Internet Explorer. Moreover, as you can read in one comme of John's entry, Internet Explorer has function interpretation "problems", declaring or overriding already declared function even if these are in an else case, while other browsers do not even consider that part of code if it will never be executed.

Another Attemp To Slow Down The Already Slow Browser


Reading this long thread about the discussion, some clever guy suggested a simple solution to avoid naming conflicts ... a closure

(function(){
setTimeout(function f(){
alert(window.f);
}, 1000);
})();

This simply means that our code size will drastically increase without a valid reason and number of created scopes and functions will be twice as was before (already too big). They think it will not slow down performances but they are forgetting old browsers that will be still used when all this wonderful shiny JavaScript will be released: Internet Explorer 7, and Internet Explorer 8 (assuming Internet Explorer 6 does not exist anymore ... would be nice to start to fight against the version 7 and then 8 ...).

Introspection and BackTrace? They Decided It Is A Bad Thing!


Without arguments.callee we are loosing arguments.callee.caller as well, often the only way we have to truly debug our code. At the same time the dynamic nature of JavaScript where "you can inject whatever wherever" will loose a big portion of its malleability.

Everything For Performances Reason ???


Apparently what this "new" language would like to get rid off is arguments variable, probably the most used variable ever, the inspiration of every ArrayLike object or library. This would be for performances, where we need as first step to call 2 functions rather than one for each runtime function assignment (setTimeout example and every assigned inline lambda) and where everybody will try to overload the language itself providing other methods to emulate the good old stuff!

Let's Start To Think About Re-Implementation Of Callee

As soon as I realized the news, I decided to think how to put again, and against performances, the callee behavior.

function F(original){
return function(){
var f = window.callee,
r = (callee = original).apply(this, arguments)
;
window.callee = f;
return r;
};
};

Above snippet execution time will be almost the same of a closure with mainly two advantages

  • less code to write

  • more performances when needed


Here an example:

var i = 0;
setTimeout(F(function(){
if(i++ === 1)
alert("OK");
else
setTimeout(callee /* please read more */, 1000);
}), 1000);

Since JavaScript is single thread, the instant that lambda will be called the global variable callee will refer to the lambda itself. Cool, but it is not enough.
To be sure that we do not loose the callee because of some other function assignment, we should trap again the callee inside that execution:

var i = 0;
setTimeout(F(function(){
if(i++ === 1)
alert("OK");
else
setTimeout(F(callee), 1000);
}), 1000);

There we are, a quick and dirty solution to the problem ... but, in this case for each timeout we are creating another function within its scope. This does not happen if we use the closure strategy so, again, we are trapped and we need to add more layers in the middle to increase responsiveness for something designed to improve performances ... does it make any sense?

function F(original){
function callee(){
// save the current window.callee
var f = window.callee, r;
// assign this callee function
window.callee = callee;
// retrieve the result calling original
// in the original scope, callee will be
// this whole function
r = original.apply(this, arguments);
// assign back the window callee
window.callee = f;
// return the result
return r;
};
// callee.callee will be the lambda
// rather than its wrap (callee itself)
callee.callee = original;
// here we are
return callee;
};

With above "monster" we have a slightly slower function that will always reassign the correct callee whatever will happen in the original function scope. This meanse simply that we can now use directly callee:

var i = 0;
setTimeout(F(function(){
if(i++ === 1)
alert("OK");
else
setTimeout(callee, 1000);
// callee.callee for direct access to the lambda itself
}), 1000);


I Bloody Hate This Idea

... but if they do not change their mind, it will be one of few other possibilities we have to avoid naming pollution everywhere causing conflicts nightmare with every Internet Explorer version less than 9.

Have fun with ECMAScript 5 and its brilliant strict mode.

Thursday, March 26, 2009

[PHP] Strict error on static call? Solved!

Just a quick post about Strict standard error when we have a class method which would like to behave differently if called statically.
Apparently, and this is a truly old bogus I opened ages ago, the solution was to supress Strict errors (lol, thanks developers) even if in every other programming language, via overload, this kind of situation could be solved in a bit.

How to solve the problem?

While last post I suggested a couple of runtime solutions, this time I suggest to simply avoid the method definition and use a different one with a meaningful name. In this example I created a String class:

class String {

// choose a recognizable name for protected method
static protected function _concat(array $arguments){
return new self(implode('', $arguments));
}

// use magic __callStatic function
static public function __callStatic($name, array $arguments){
// check the recognizable name
switch($name = '_'.$name){
case '_concat':
// return its call
return self::$name($arguments);
}
}

protected $__value__; // save the string somewhere

// assign initial string, if any
public function __construct($__value__ = ''){
$this->__value__ = $__value__;
}

// use magic __call method
public function __call($name, array $arguments){
// check the recognizable name
switch($name = '_'.$name){
case '_concat':
// normalize expected arguments
array_unshift($arguments, $this->__value__);
// return its call
return self::$name($arguments);
}
}

// this is a String class ...
public function __toString(){
return $this->__value__;
}

}


// test the alchemy
$str = new String('Hello');
var_dump(
String::concat('Hello', ' ', 'World', '!') == $str->concat(' ', 'World', '!')
// true, both are Hello World!
);

As we can see, errors are gone and the code is much easier to maintain. Hope this will help (performances? ... oh, come on!)