Experience required

I
was intrigued by Neil Deakin’s recent post “urn:schemas-microsoft-com:office:office” /> where
he says that when he was a young user, he got mad about all kinds of things that,
after a few years as an implementor, he didn’t feel mad about anymore.

 

I’m
with ya, Neil.  Many of my computer geek
high school friends were rather surprised to learn that I had taken a job at Microsoft,
given my earlier criticisms of all x86 based software.  (I
was an Amiga partisan as a teenager.) I feel the same way you do — I was a naïve
teenager.

 

Neil,
however, doesn’t actually explain why it is that he finds his former beliefs to be
naïve.  I can’t speak for Neil, but I
can take a stab at explaining what the revelation was for me.  Basically
it comes down to realizing two things:  (1)
good design is a series of difficult compromises,
and (2) software is by its nature incredibly complex.  I
failed to appreciate either of those facts until I’d actually done a lot of it myself.  Once
I appreciated these things, I understood that we have imperfect software BECAUSE the
people who make it are dedicated to writing quality software for end users, not in
spite of that fact.

 

And
it doesn’t matter what your business model is — open source or closed source, give
away the software and sell consulting, shrink-wrapped boxes, micropayments, freeware,
whatever.  The money story is irrelevant;
all software development is about coming up with an implementableusable design
for a given group of end users and then
finding enough resources to implement that
design
.  There are only a finite number
of programmers in the world, and way more problems that need solving than there are
resources available to solve them all, so deciding which problems to solve for what
users is crucial.

 

Sometimes
— not always, but sometimes — not fixing a trivial, obscure bug with an easy workaround is the
right thing to if it means that you can spend that time working on a feature that
benefits millions of customers. Sometimes features that are useless to you are life-savers
for someone else.  Sometimes — not always,
but sometimes — using up a few more pennies of hard disk space (ten megs costs, what,
a penny these days?) is justifiable.  Sometimes
— not always, but sometimes — proprietary designs allow for a more efficient design
process and hence more resources available for a quality implementation.   These
are all arguable, and maybe sometimes we humans don’t make the _best_ choices.  But
what is naive is to think that these are not hard problems that people think about
hard.

 

When
I was a teenager, I thought software engineering was trivial — but my end user was
myself, and my needs were simple.  Software
development doesn’t scale linearly!  Complex
software that exists to solve the real-world problems of millions of end-users is
so many orders of magnitude harder to write, that intuitions about the former can
be deeply misleading.

 

And
this is true in ALL design endevours that involve tough choices and complex implementations!  I
was watching the production team commentary track for the extendamix of The Two Towers
this weekend, and something that the producers said over and over again was that they
got a lot of flack for even slightly changing the story from the book.  But
before you criticize that choice, you have to really
think through all the implications
 of being slaves to the source material.  How
would the movie be damaged by showing Merry and Pippin’s story entirely
in flashback
?  By not interlacing
the Frodo-and-Sam story with the Merry-and-Pippen story?  By
making Faramir’s choice easy? By adding Erkenbrand as the leader of the Westfold?  Yes,
these are all departures from the book, but they also prevent the movie from being
confusing and weak at the end.  None are
obvious — they’re all arguable points, and ultimately someone had to make a tough
decision to produce a movie that wasn’t going to satisfy everyone fully, but would
nevertheless actually ship to customers!

 

There
is no perfect software; the world is far too complex and the design constraints are
too great for there to be anything even approaching perfect software.  Therefore,
if you want to ship the best possible software for your users, you’ve got to carefully
choose your imperfections.  As the guy
who writes tools for you software engineers, my mission is to make tools that afford deeper
abstrations
 and hence require fewer developer
resources
 to implement.  That’s the
only way we’re going to get past the fundamental problems of complexity and expense.

Tags Rants

Comments (6)

You must be logged in to post a comment.

  1. MartijnOK, about that software geek thingie, you are probably right, I can’t tell, but please don’t say they had to make all these changes to the Lord of the Rings book.
    Peter Jackson has a pretty good insight what the books where about, but I think that he has made some fatal errors in the second movie, by changing the story.
    I think the book is all about choices (what do you do when you are confronted with evil/power), and Peter has changed that behavior of some characters in the movy in a really bad way.
  2. Log in to Reply
  3. November 24, 2003 at 8:11 pm
  4. DanielSo what are your thoughts on this:
    http://www.fastcompany.com/online/06/writestuff.htmlcan NASA can get perfect software ?
  5. Log in to Reply
  6. “This software never crashes. It never needs to be re-booted. This software is bug-free.”
  7. November 24, 2003 at 10:02 pm
  8. silI agree entirely that your eyes are opened when you get into collaboratively developing code with a big team and putting together proper stuff rather than one-shot code for yourself, absolutely. However, the shift from user (or one-man coder) to software developer can also bring with it less of a focus on usability, because you forget everything that you didn’t know as that user; some stuff is just so *obvious* as a hacker that it never occurs to you that it’s confusing or non-obvious to others. I’m not necessarily suggesting that you’re guilty of this, Eric, but there’s not a lot of difference between “you don’t understand how putting software together works — you’re naive and should learn” and “you don’t understand how software works — you’re naive and should learn”, I feel. Dangerous ground, where it’s easy to sell out those youthful passions and compromise too much…
  9. Log in to Reply
  10. November 25, 2003 at 2:57 am
  11. Stuart DootsonDaniel – no, NASA cannot get perfect software..or rather, they *may* get perfect software, but they cannot know it – they cannot know if there are still defects in their software, unless they’ve decided to leave them in.Log in to Reply
  12. The other thing to think about is that by deciding to try and minimise the number of bugs in their software, NASA have effectively decided that they are going to spend an awful lot of money on what is effectively a small functionality set. They probably spend 10-100 times what Microsoft (or any other PC software vendor) spend per function point – possibly more. The testing budget is probably 50-60% of the total budget. (and yes, I’m speaking from experience – I’ve developed safety-critical software to DO-178B Level A).
  13. November 25, 2003 at 4:15 am
  14. Peter TorrIt would be more correct to say “This software has never crashed”. Also Eric is talking about building software for millions of people who do arbitrarily complex things with it, combine it with other arbitrary software and hardware, and so on. The Shuttle group has an incredibly narrow focus for their software: It must launch the shuttle, and that’s it.Log in to Reply
  15. I bet they don’t let the crew watch DVDs or run the SETI screen saver on the same machine that fires the engines 😉
  16. November 25, 2003 at 12:41 pm
  17. Mike DimmickThe space shuttle software also has an extremely limited range of hardware that it has to run on, while Windows has to run on the entire gamut of PC hardware.Log in to Reply
  18. NASA reportedly had a bit of a crisis recently because they weren’t able to source 8086 processors for the space shuttle computers.
  19. November 25, 2003 at 2:48 pm

Skip to main content

Follow Us

Popular Tags

C# Scripting JScript VBScript Language Design COM Programming Rarefied Heights Puzzles Rants Performance Security C# 4.0 Non-computer SimpleScript JScript .NET Immutability Code Quality Pages Recursion Books

Archives

Advertisements

The JScript Type System Part Seven: Yeah, you’ve probably guessed that I wrote the array stuff

A
reader asked me to clarify a point made in an earlier entry:

 

Note
that JScript .NET arrays do not have any of the methods or properties of a CLR array.
(Strings, by contract, can be used implicitly as either JScript .NET strings or as System.String values,
which I’ll talk more about later.) But JScript .NET arrays do not have CLR array fields
like Rank, SetValue,
and so on.

 

It’s
actually pretty straightforward.  When
you have a string in a JScript .NET program, we allow you to treat it as both a System.String
and as a JScript object with the String prototype.  For
example:

 

var s
= ”   hello   “;

print(s.toUpperCase());
// calls JScript string prototype’s toUpperCase

print(s.Trim());
// calls System.String.Trim

 

Which
is it?  From a theoretical standpoint,
it doesn’t really matter — you can use it as either.  From
an implementation standpoint, of course we use System.String internally and magic
up the prototype instance when we need one — just as in JScript classic all strings
are VT_BSTR variants internally and we magic up a wrapper when we need one.  So
JScript strings and CLR strings really are totally interoperable.

 

Arrays
aren’t quite so seamless.  As I mentioned
earlier, when you try to use a JScript .NET array when a CLR array is expected, we
create a copy.  But when you go the other
way, things are a little different. Rather than producing a copy, using a CLR array
as a JScript .NET array “wraps it up”. No copy is made. The operation is therefore
efficient and preserves identity. Changes
made to a wrapped array are preserved:

 

function
ChangeArray(arr : Array) : void

{

print(arr[0]);
// 10

arr[0]
+= 100;

//
JScript .NET methods work just fine

print(arr.join(“:”));
// 10:20:30

}

var arr
: int[] = [10, 20, 30];

ChangeArray(arr);

print(arr[0]);
// 110

 

The
principle rule for treating a hard-typed array as a JScript .NET array is that it
must be single-dimensional
. Since all JScript .NET arrays are single-dimensional
it makes no sense to wrap up a high-rank CLR array.

 

Once
the array is wrapped up it still has all the restrictions that a normal hard-typed
array has. It may not change size, for instance. This means that an attempt to call
certain members of the JScript .NET Array prototype on a wrapped array will fail.
All calls to push, pop, shift, unshift and concat as
well as some calls to splice will
change the length of the array and are therefore illegal on wrapped CLR arrays.

 

Note
that you may use the other JScript .NET array prototype methods on any hard-typed
array (but not vice versa). You can think of this as implicitly creating a wrapper
around the CLR array, much as a wrapper is implicitly created when calling methods
on numbers, Booleans or strings:

 

var arr
: int[] = [10, 20, 30];

arr.reverse();    //
You may call JScript .NET methods on hard-typed arrays

print(arr.join(“:”));   //
30:20:10

 

There
may be a situation where you do want to
make a copy of a CLR array rather than wrapping it. JScript .NET has syntax for this,
namely:

 

var sysarr
: int[] = [10, 20, 30];

var jsarr1
: Array = sysarr; // create wrapper without copy

var jsarr2
: Array = Array(sysarr); // create wrapper without copy

var jsarr3
: Array = new Array(sysarr); // not a wrapper; copies contents

 

In
the last case jsarr3 is
not a wrapper. It is a real JScript .NET array and may be resized.

Tags JScript JScript .NET Scripting

Comments (3)

You must be logged in to post a comment.

  1. Dan ShappirI think some of that reader’s misunderstanding is a result of the use of the word “contract” where the word “contrast” was intended.function ChangeArray(arr) : void
    {
    print(arr[0]); // 10
    arr[0] += 100;
    }Log in to Reply
  2. Would it still convert the CLR array to a JScript.NET array? Or have I created a generic function that works for both types with no conversions?
  3. In any event, thanks for the info.
    A question though: suppose I had changed the ChangeArray implementation to:
  4. November 23, 2003 at 5:33 am
  5. Peter TorrDan, your changed function does not specify a type for the ‘arr’ parameter, so it will be compiled as “Object” and all operations on ‘arr’ will be late-bound. Thus it will work with JScript arrays, CLR arrays, and indeed anything that has an overloaded indexing operator.
  6. Log in to Reply
  7. November 23, 2003 at 12:56 pm
  8. Eric LippertYeah, I guess the spell checker didn’t catch that one, did it? 🙂
  9. Log in to Reply
  10. November 24, 2003 at 6:02 pm

Skip to main content

Follow Us

Popular Tags

C# Scripting JScript VBScript Language Design COM Programming Rarefied Heights Puzzles Rants Performance Security C# 4.0 Non-computer SimpleScript JScript .NET Immutability Code Quality Pages Recursion Books

Archives

Thin To My Chagrin

I’m going to take a quick intermission from talking about the type system, but we’ll pick it up again soon.  I’ve been thinking a lot lately about philosophical and practical issues of thin client vs. rich client development.  Thus, I ought to first define what I mean by “thin client” and “rich client”. 

Theory

We used to think of Windows application software as being shrink-wrapped boxes containing monolithic applications which maybe worked together, or maybe were “standalone”, but once you bought the software, it was static — it didn’t run around the internet grabbing more code.  If the application required buff processing power and lots of support libraries in the operating system, well, you needed to have that stuff on the client.  The Windows application model required that the client have a rich feature set.

This is in marked contrast to the traditional Unix model of application software.  In this model the software lives on the server and a bunch of “thin” clients use the server.  The clients may have only a minimal level of memory and processing power — perhaps only the ability to display text on a screen!

The ongoing explosion of massive interconnection via the Internet starting in the 1990’s naturally led software developers to rethink the monolithic application model.  Multi-tiered development was the result — the front end that the user sees is just a thin veneer written in HTML, while the underlying business logic and data storage happens on servers behind the scenes.  The client has to be quite a bit fatter than a Unix “dumb terminal”, but if it can run a web browser, it’s fat enough. 

This was a great idea in many ways.  Multi-tiered development encourages encapsulation and data locality.  Encapsulating the back end means that you can write multiple front-end clients, and shipping the clients around as HTML means that you can automatically update every client to the latest version of the front end.  My job from 1996 to 2001 was to work on the implementation of what became the primary languages used on the front-end tier (JScript) and the web server tier (VBScript).  It was exciting work.

Right now, we’re looking to the future.  We’ve made a good start at letting people develop thin-client multi-tiered applications in Windows, but there is a lot more we can do.  To do so, we need to understand what exactly is goodness.  So let me declare right now Eric Lippert’s Rich Client Manifest

The thin-client multi-tiered approach to software development squanders the richness available on the vast majority of client platforms that I’m interested in.  We must implement tools that allow rich client application developers to attain the benefits of the thin-client multi-tiered model.

That’s the whole point of the .NET runtime and the coming Longhorn API.  The thin client model lets you easily update the client and keeps the business logic on the back tier?  Great — let’s do the same thing in the rich client world, so that developers who want to develop front ends that are more than thin HTML-and-script shells can do so without losing the advantages that HTML-and-script afford. 

Practice

I’ve been thinking about this highfalutin theoretical stuff recently because of some eminently practical concerns.  Many times over the years I’ve had to help out third party developers who have gotten themselves into the worst of both worlds.  A surprisingly large number of people look at the benefits of the thin client model — easy updates (the web), a declarative UI language (HTML), an easy-to-learn and powerful language (JScript) — and decide that this is the ideal environment to develop a rich client application.

That’s a bad idea on so many levels.  Remember, it is called the thin client model for a reason.  I’ve seen people who tried to develop database management systemsin JScript and HTML!  That’s a thorough abuse of the thin client model — in the thin client model, the database logic is done on the backend by a dedicated server that can handle it, written by database professionals in hand-tuned C.  JScript was designed for simple scripts on simple web pages, not large-scale software.

Suppose you were going to design a language for thin client development and a language for rich client development.  What kinds of features would you want to have in each?

For the thin client, you’d want a language that had a very simple, straightforward, learn-as-you-go syntax.  The concept count, the number of concepts you need to understand before you start programming, should be low.  The “hello world” program should be something like

print “hello, world!”

and not

import library System.Output;
public startup class MainClass
{
  public static startup function Main () : void
  {
     System.Output(“hello, world!”);
  }
};

It should allow novice developers to easily use concepts like variables and functions and loops.  It should have a loose type system that coerces variables to the right types as necessary.  It should be garbage collected.  There must not be a separate compile-and-link step.  The language should support late binding.  The language will typically be used for user interface programming, so it should support event driven programming.  High performance is unimportant — as long as the page doesn’t appear to hang, its fast enough.  It should be very easy to put stuff in global state and access it from all over the program — since the program will likely be small, the lack of locality is relatively unimportant. 

In short, the language should enable rapid development of simple software by relatively unsophisticated programmers through a flexible and dynamic programming model. 

OK, what about the rich-client language?  The language requirements of large-scale software are completely different.  The language must have a rigid type system that catches as many problems as possible before the code is checked in.  There must be a compilation step, so that there is some stage at which you can check for warnings.  It must support modularization, encapsulation, information hiding, abstraction and re-use, so that large teams can work on various interacting components without partying on each other’s implementation details.  The state of the program may involve manipulating scarce and expensive resources — large amounts of memory, kernel objects such as file handles, etc.  Thus the language should allow for fine-grained control over the lifetime of every byte.

Object Oriented Programming in C++ is one language and style that fits this bill, but the concept count of C++ OOP is enormous — pure, virtual, abstract, instance, static, base, pointers, references…  That means that you need sophisticated, highly educated developers.  The processing tasks may be considerable, which means that performance becomes a factor.  Having a complex “hello world” is irrelevant, because no one uses languages like this to write simple programs.

In short, a rich-client language should support large-scale development of complex software by large teams of sophisticated professional programmers through a rigid and statically analyzable programming model.

Complete opposites!  Now, what happens when you try to write a rich client style application using the thin client model? 

Apparent progress will be extremely rapid — we designed JScript for rapid development.  Unfortunately, this rapid development masks serious problemsfestering beneath the surface of apparently working code, problems which will not become apparent until the code is an unperformant mass of bugs. 

Rich client languages like C# force you into a discipline — the standard way to develop in C# is to declare a bunch of classes, decide what their public interfaces shall be, describe their interactions, and implement the private, encapsulated, abstracted implementation details.  That discipline is required if you want your large-scale software to not devolve into an undebuggable mess of global state.  If you can modularize a program, you can design, implement and test it in independent parts.

It is possible to do that in JScript, but the language does not by its very nature lead you to do so.  Rather, it leads you to to favour expedient solutions (call eval!) over well-engineered solutions (use an optimized lookup table).  Everything about JScript was designed to be as dynamic as possible.

Performance is particularly thorny.  Traditional rich-client languages are designed for speed and rigidity.  JScript was designed for comfort and flexibility.  JScript is not fast, and it uses a lot of memory.  Its garbage collector is optimized for hundreds, maybe thousands of outstanding items, not hundreds of thousands or millions.

So what do you do if you’re in the unfortunate position of having a rich client application written in a thin-client language, and you’re running into these issues?

It’s not a good position to be in.

Fixing performance problems after the fact is extremely difficult.  The way to write performant software is to first decide what your performance goals are, and then to MEASURE, MEASURE, MEASURE all the time.  Performance problems on bloated thin clients are usually a result of what I call “frog boiling”.  You throw a frog into a pot of boiling water, it jumps out.  You throw a frog into a pot of cold water and heat it up slowly, and you get frog soup.  That’s what happens — it starts off fine when it is a fifty line prototype, and every day it gets slower and slower and slower… if you don’t measure it every day, you don’t know how bad its getting until it is too late.  The best way to fix performance problems is to never have them in the first place.

Assuming that you’re stuck with it now and you want to make it more usable, what can you do?

  • Data is bad. Manipulate as little data as possible.  That’s what the data tier is for.  If you must manipulate data, keep it simple — use the most basic data structures you can come up with that do the job.
  • Code is worse.  Every time you call eval, performance sucks a little bit more.  Use lookup tables instead of calling eval.  Move code onto the server tier. 
  • Avoid closures.  Don’t nest your functions unless you really understand closure semantics and need them.
  • Do not rely on “tips and tricks” for performance.  People will tell you “declared variables are faster than undeclared variables” and “modulus is slower than bit shift” and all kinds of nonsense.  Ignore them.  That’s like mowing your lawn by going after random blades of grass with nail scissors.  You need to find the WORST thing, and fix it first.  That means measuring.  Get some tools — Visual Studio Analyzer can do some limited script profiling, as can the Numega script profiler, but even just putting some logging into the code that dumps out millisecond timings is a good way to start.  Once you know what the slowest thing is, you can concentrate on modularizing and fixing it.
  • Modularize.  Refactor the code into clean modules with a well-defined interface contract, and test modules independently. 

But the best advice I can give you is simply use the right tool for the right job.  The script languages are fabulous tools for their intended purpose.  So are C# and C++.  But they really are quite awful at doing each other’s jobs!

Comments (15)

  1. Anon

    Good essay, but there are two burrs marring it for me.

    1) You say, ” …but once you bought the software, it was yours.” Well, no, that’s not how I’m seeing things, and I’m shocked that as a Microsoft employee you’re spreading *that* mis-truth. Microsoft is *well known* for taking pains to make it known that no, we don’t own the software we bought. However, it’s not relevant to the main thrust of your essay. But it’s still a burr that needs sanding down.

    2) The essay boils down to, and you said it: “use the right tool for the right job.” While it’s good to conduct an overview of rich vs. thin client models the way you did, this is one of many such rants I’ve seen online, and it’s getting boring. Far better for us to start writing “why are people not choosing the right tools for the right job?” and “With those answers, how are we going to change things to make it better?”

  2. You know, thinking back, I think the last time I made an anonymous post to a public forum, the Amiga 500 was the cutting edge of technology. Any particular reason why you’re unwilling to put your name on your opinions? I promise to not sign you up for those “bill me later” magazine subscriptions.

    > we don’t own the software we bought

    The nuances of intellectual property law and the rights of property holders are fascinating topics which I have no intention of getting into at this time. My point, perhaps poorly expressed, was simply that once you bought the software, obtaining updates was typically a matter of buying more shrink-wrapped boxes. This is in contrast with the web model, where ownership is even more nebulous and updating is trivial.

    > it’s getting boring

    If my frequently updated, highly technical and excessively detailed writing bores you, I invite you to read one of the billion or so other blogs now available on the internet.

    > Far better for us to start writing “why are people not choosing the right tools for the right job?” and “With those answers, how are we going to change things to make it better?”

    Good questions.

    My mission is to throw developers into the Pit Of Quality. Historically, developer tools make developers climb the Hill of Quality — you have to do lots of work to ensure quality software, because the tools can easily work against that goal. Instead, we must make it HARD to write bad software.

    Consider C++ — you want to write a buffer overrun in C++, you can do so in about 30 seconds. Want to write a buffer overrun in C#? You’ve got to WORK IT, baby! Writing buffer overruns in C# is HARD.

    That’s how we make it better — with a combination of user education and tools which lead developers to good practices by their very nature.

    This is an EXTREMELY difficult design problem, and we will never be done. There is _always_ room for improvement in the tools space. (My job is secure!) But we are certainly working on it. In fact, the usability PM across the hall from me is about to embark upon a detailed study of how developers make mistakes using Visual Studio, how they realize they’ve made a mistake, and how they correct it. I’m sure it will be a fascinating study.

  3. Dan Shappir

    Choosing the proper tool for the job is always sound advice, and there is a lot of truth in the comments you make. To the list of distinctions between languages for “thin” vs. “fat” clients I would add behavior in the face of adversity. When encountering an unexpected situation, or even an error, a language like JavaScript should “try to get along” by doing the “right thing” – the spirit of HTML. With application development languages like C++ and C# I would prefer a crash over making any sort of assumptions about my intentions.

    Where I do take a bit of an exception with your post is that it’s very black and white. I believe there are scenarios where the distinctions between client and server can become blurred. Essentially, you can have the client and the server running together on the same computer, even within the same process. I’ll give two examples of applications I’ve designed and developed.

    The first application, which I’ve mentioned here before, implemented its UI using HTML and JavaScript. Underneath it utilized an ActiveX control, pushing in data as an OSP. I think the combination of a COM control doing the heavy lifting beneath an HTML front-end is/was a very powerful one. I remember Microsoft trying to push this model very hard in the 97′ PDC. I never understood why it hasn’t caught on.

    Another project had a stand-alone application that contained the browser control. The UI was generated by applying XSLT to XML in order to generate DHTML powered by JavaScript. The container also exposed methods to the JavaScript through window.external.

    In both cases the combination was very powerful because C++ provided power and stability, and JavaScript ease of development and flexibility. Also, in both cases the customer could modify the UI extensively because the XML/HTML/JavaScript portion was provided in source format.

  4. 6th Attempt to post comment

    >>…what I call “frog boiling”. You throw a frog into a pot of boiling water, it jumps out. You throw a frog into a pot of cold water and heat it up slowly, and you get frog soup. <<

    Another internet myth.

    http://www.snopes.com/critters/wild/frogboil.htm

  5. Dan Shappir

    > My mission is to throw developers into the Pit Of Quality. Historically, developer tools make developers climb the Hill of Quality — you have to do lots of work to ensure quality software, because the tools can easily work against that goal. Instead, we must make it HARD to write bad software.

    Consider me a pessimist, but I believe producing quality code will always be an uphill battle. For example, while switching from C++ to C# or Java helps you avoid buffer overruns, you introduce whole new sets of potential problems. Check out this C++ snippet:

    for ( int i = 0 ; i < 1000000 ; ++i ) {
    char buffer[1000];

    }

    Now lets naively translate this to Java:

    for ( int i = 0 ; i < 1000000 ; ++i ) {
    char[] buffer = new char[1000];

    }

    Can you see the problem here? I’ve uncovered this tye of problem several times in production code. And to make matter worse, unless you review or profile the code you may never find this problem.

    Other issues also come to mind, such as determinate finalization (yes I know about the using keyword, but it’s still much easier to do in C++). And if GC makes life so much easier, why did MSDN magazine run a three part series on the ins-and-outs of the .NET GC?

    Don’t get me wrong, I do think C# and .NET are a step in the right direction (unless you are a LISPer or SmallTalk hacker in which case you might consider it a step back 😉 I just thing it’s a never-ending road.

  6. Anu

    I’m still wondering why we have concept like thin and fat clients.

    It would be nice if code was mobile and just migrated to where it could most efficiently execute.

    Ie DBMS access on some server, client redraw on client, other code moves heuristically around depending on the execution circumstances.

  7. Anu, I am sure there are many reasons for this, not least of which is our favourite topic, security. We (as an industry) can’t even get security “right” for well-defined systems, let alone systems in which the threat models change radically depending on where the code is. For example, moving code off the web server and onto the client poses a whole new set of issues (hint: the server can’t trust anything that comes from the client, and vice-versa).

  8. Andy Schriever

    Eric, in my opinion you’ve not looked closely at some of the received wisdom you treat as fact.

    You state, for example, that any serious development language “must have a rigid type system”, and that it “must support compilation”. Though these statements represent conventional wisdom, I’m not certain they stand up to careful and open scrutiny.

    Take the rigid typing issue, for example. I’d suggest that our obsession as an industry with rigid typing is a sort of self-fulfilling prophecy — that the insistence on rigid control over data type is in fact the source of so may challenging type-related bugs. In more than 5 years of use of JavaScript for serious application development, I’ve learned (much to my surprise!) that by eliminating data type as a subject of constant concern, the entire collection of bugs relating to type mismatch disappears. We’ve implemented very systems with very substantial JavaScrpt front ends and have only once suffered a meaningful bug relating to data typing. (And, actually, that one was really a result of JavaScript’s unfortunate overloading of “+”). In our experience, the management of data types is something that can be safely left to the interpreter.

    Similarly, consider your requirement for compilation. The compiled-code environment tends to discourage the kind of careful, incremental build-and-test methodology one can follow in a purely interpreted environment. In the compiled environment, developers tend to build larger chunks of functionality in between bouts of testing, and the size of those chunks rises in proportion to the complexity and delay involved in compilation. That, in turn, leads to more bugs and greater unwillingness to change course when a concept doesn’t work quite as well as expected.

    Your reasonable counter to these arguments is one you address in your article — performance. Interestingly enough, this has never been an issue in any of our applications (though it’s caused a couple of struggles, particularly with IE’s reluctance to render large tables rapidly). After all, how much horsepower is really required to handle most user-side tasks? In the course of our life with JavaScript, the performance of the underlying platform has more than wiped out any advantage we might have gained, had we started in the compiled environment. Looking forward, this still seems to be a reliable expectation.

    As one of the other responders mentioned, my wish is that Microsoft and other vendors would recognize that many of us have taken the HTML+Javascript path for a good reason. The simplicity of Web distribution and the ability to distribute real business applications to any desktop is part of it. The efficiency, simplicity, and downright elegance of JavaScript is another part. Finally, the ability to hand off a while collection of UI management tasks to the best rendering engine that’s ever existed — the modern browser — removes a significant load from the developer and is still another factor. Rather than the .Net model (a truly superb job, by the way) of heavyweight development environments, enormous demand on developers, and cumbersome development and deployment models, why not find a way to add professional tools and environment to HTML + Javascript?

  9. Interesting post, Andy. One thing about typing: you say that it is not a problem as long as the developer is careful not to make assumptions about the type of data. True – and we wouldn’t need garbage collection if developers were careful with their memory allocations, and we wouldn’t need checking on array bounds if developers were careful with their indices and buffer sizes, and so on.

    What we’ve learnt (as an industry) is that developers are only human, which means they make mistakes and are sometimes lazy / careless / in a rush to ship a product out the door. Anything the system can do to catch these problems as early as possible is A Good Thing ™

    See more at:

    http://blogs.gotdotnet.com/ptorr/commentview.aspx/eccc1d2e-fb94-43a0-bf43-5adadb5f579f

  10. We can get sillier than that. We wouldn’t need variables if programmers would just keep track of their pointer values… heck, we wouldn’t need programming languages at all if programmers would just talk straight to the chips in binary like they used to…

    Writing software in the large is all about _managing complexity_, and static type checking is a great way to manage that complexity. My point is simply that if you have a lot of complexity to manage, use the right tools.

  11. This post is right up my alley. For 3 years I worked on a prototype client runtime system built around HTML, JavaScript and XML (yes for its time it was very buzzword compliant!) called Sash. We always thought that blurring the distinction between thin client web apps and fat client desktop apps was the sweet spot for developers to be in and our entire platform was built around that idea. We were able to realize significant development time returns because most of the heavy lifting was done by COM objects hosted in a consistent manner with a very granular security model. We built all manner of very rich client app all on top of JavaScript and HTML, so I think that some of the distinctions you make in your post, while valid, don’t take into account the fact that most developers who are using such a system are disciplined enough to practice good modularization and make the language and runtime work for them. I guess my point is that it really comes down to developer discipline and not the language model when you are trying to design any app (thin, rich, fat, bloated…whatever). As ana aside I think the term we used for our model was muscle client 😉

  12. Craig

    I have also been involved in creating a “rich client” for IE using JavaScript, DHTML and XML. This product has been shipping for almost 4 years and is very stable. Yes, we have had to deal with those nasty performance issues, but they seem no worse than in other environments (except for the lack of tools). Sounds programming saves yourself in any environment.

    The one issue that I want to bring up is that, in your history overview, you jump right from a discussion of thin-clients to your vision of a rich client using a technology that doesn’t exist yet. What about those products that had to be built in the in-between time? From say, 1998-2005?

    We cannot wait to build the products we want to build until Longhorn ships. Our product was started in late 1999 when there were really just a few options, Java, ActiveX or JavaScript and DHTML. I stand by our decision to use these technologies, they were the best out there (and still are).

    Now, this does not mean that I am not looking forward to moving to .NET and newer technologies if/when they ship. 😉

  13. Jon Innes

    Interesting article Eric. I’ve been involved with the development of rich GUIs built using web technology for some time–including one of the leading CRM packages available today. I agree that JavaScript and DHTML are not the best tools for rich client problems. But unfortunately the hype around web-apps has led to the fact that most enterprise application software (Oracle, Siebel, PeopleSoft…) sold today have web-based UIs.

    There are several reasons for this. First, easy distribution of updates of web apps addressed a need that wasn’t being addressed sufficiently by Java or .Net. Until the .Net runtime is as pervasive as IE on end-user desktops, IT groups in big organizations will prefer web-based apps for this reason alone. Another factor is that web-based UIs are, as you point out, fast and easy to develop. This has effects that are lost on companies that made their money in the shrink-wrap past. Web UIs can be prototyped quickly (independent of the server-side code), making it easy to get user and customer feedback. Web-apps are also easier to integrate with the web-based intranets found in today’s companies. IT staff are typically more comfortable customizing web-based apps, something most businesses do with enterprise application software that they don’t do with Word or Excel.

    HTML and JavaScript skills are more common than VB, C#, or C++ in IT departments and for that matter in most software companies today. Unfortunately, software developers and IT staff tend to pick the language for a project based on expertise, not suitability. That will probably stay that way until languages and tools become so easy to learn that programmers stop linking their professional careers with them. I look forward to that day. When that happens, people in our profession will identify more with the domain they work in (e.g., healthcare) rather than the tools/technology (C#, Jscript) they use.

  14. Andrey Skvortsov

    Longhorn XAML is simple expandable browser model(.NET powered=hosting/creation .NET objects as first class BOM elements/objects,.NET+DOM infoset mix), so what you are talking about?

    More powerful hosting environment(smart client anyone?)-more powerful client/server/…

    And any developer(experienced in particular) will choose most easy/flexible way to do the job-always,dosen’t matter that’s not right in someone opinion.

    expando properties/attached behaviors-I still miss about it in Longhorn(even if this functionality is present-it’s implementation is not transparent enough(Dependency properties etc.) compared to IE+JavaScript and THIS is BAD.

  15. I love this topic and agree with many of the posts here. I like to consider myself a developer who – before implementing any architecture/technology – understands it.

    I’ve recently been developing an IE/Javascript/DHTML/XML framework that takes advantage of the MS XMLHTTPRequest object and Inner-Browsing. In the process I’ve learned quite a lot about JS and rather like it. I had previously thought it was a toy language – but its OO abilities, among others things, are rather kick-butt.

    On the surface – a number of things are GOOD:

    1. fast development cycle.

    2. The ability to create a rich UI via DHTML (I spent over 9 years doing DOS, Win3.1, Win95, Win2K multi-media apps and can recite almost every GDI API there is. The amount processing going on to change the class for a mouseover event amazes me…)

    3. The ability to maintain state on the client via XML data islands

    4. Synchronous posts/gets via the XMLHTTPRequest object.

    5. Few to no ‘back-button’ issues

    6. The server code gets simpler and thus more scalable because it essentially just authenticates, saves and fetches XML.

    7. No more complicated code saving state at the server and re-creating the entire page. (Less bugs)

    So what’s wrong ? Well, if JS has the memory leaks I read about – then what is the point ? Why aren’t tools available to help pin-point when a leak occurs ? Why aren’t there good IDEs for JS debugging ? I’d like to know if this is a viable frame-work before preceding…

    Anyone develop an industrial app like this ?

The JScript Type System, Part Six: Even more on arrays in JScript .NET

You
might have noticed something odd about that last example using SetValue.
If you actually look up the method signature of SetValue in
the CLR documentation it notes that the function signature is:

 

public
function SetValue(value : Object, indices : int[]) : void

 

The
odd thing is the annotation on indices.
It is typed as taking a .NET array of integers but in the example in my last entry
we give it a literal JScript array, not a hard-typed CLR array.

 

JScript
.NET arrays and hard-typed CLR arrays work together, but because these two kinds of
arrays are so different they do not work together perfectly.
The problem is essentially that JScript .NET arrays are much more dynamic than CLR
arrays. JScript .NET arrays can change size, can have elements of any type, and so
on.

 

The
rules for when JScript .NET arrays and CLR arrays may be used in place of each other
are not particularly complicated but still you should exercise caution when doing
so. In particular, when you use a JScript .NET array in a context where a CLR array
is expected you can get unexpected results. Consider this example:

 

function
ChangeArray(arr : int[]) : void

{

print(arr[0]);
// 10

arr[0]
+= 100;

}

var
jsarr : Array = new Array(10, 20, 30);

ChangeArray(jsarr);

print(jsarr[0]);
// 10 or 110?

 

This
might look like it prints out 10 then 110, but in fact it prints out 10 twice. The
compiler is unable to turn the dynamic JScript array into a hard-typed array of integers
so it does the next best thing. It makes a copy of the JScript array and passes the
copy to the function.
 If the function reads the array then it gets the correct
values. If it writes it, then only the copy
is updated, not the original.

 

To
warn you about this possibly unintentional consequence of mixing array flavours, the
compiler issues the following warning if you do that:

 

warning
JS1215: Converting a JScript Array to a System.Array results in a memory allocation
and an array copy

 

You
may now be wondering then why the call to SetValue which
had the literal JScript .NET array did not prompt this warning. The
warning is suppressed for literal arrays
. In the case of literal arrays the compiler
can determine that a literal array is being assigned to a CLR array type. The
compiler then optimizes away the creation of the JScript .NET array and generates
code to create and initialize the CLR array directly
. Since there is then no performance
impact or unexpected copy, there is no need for a warning.

 

Note
that if every element of the source JScript .NET array cannot be coerced to the type
of the hard-typed CLR array then a type mismatch error will be the result. For instance,
if you tried to coerce an array containing strings to an array of integers then this
would fail:

 

var
arr1 : int[] = new Array(10, “hello”, 20); // Type mismatch error at runtime

var
arr2 : int[] = [10, “hello”, 20];          //
Type mismatch error at compile time

 

Note
also that this applies to multidimensional arrays. There
is no syntax for initializing a multidimensional array in JScript .NET:

 

var
mdarr : int[,] = [ [ 1, 2 ], [3, 4] ]; // Nice try, but illegal

 

rectangular multidimensional
array is not an array of arrays. In this case you are assigning a one-dimensional
array which happens to contain arrays to a two-dimensional array of integers. That
is not a legal assignment. If, however, you want a ragged hard-typed
array it is perfectly legal to do this:

 

var
ragged : int[][] = [ [ 1, 2 ], [3, 4] ];

print(ragged[1][1]);
// 4

 

Rectangular
multidimensional arrays are indexed with a comma-separated list inside one set of
square brackets. If you use ragged arrays to simulate true multidimensional arrays
then the indices each get their own set of brackets.

 

Note
that JScript .NET arrays do not have any of the methods or properties of a CLR
array. (Strings, by contract, can be used implicitly as either JScript
.NET strings or as System.String values,
which I’ll talk more about later.)  But
JScript .NET arrays do not have CLR
array fields like Rank, SetValue,
and so on.

 

Next
time I’ll talk a bit about going the other way — using a CLR array where a JScript
array is expected.

Tags JScript JScript .NET Scripting

Comments (5)

You must be logged in to post a comment.

  1. Damit&gt; Note that JScript .NET arrays do not have any of the methods or properties of a CLR array. (Strings, by contract, can be used implicitly as either JScript .NET strings or as System.String values, which I’ll talk more about later.) But JScript .NET arrays do not have CLR array fields like Rank, SetValue, and so on.Log in to Reply
  2. I wonder whether you meant to say here that JScript .NET arrays *have* CLR array fields? This paragraph sounds rather confusing to me…
  3. November 14, 2003 at 10:51 pm
  4. Dan ShappirI found this post very interesting. Recently I’ve added a new module to BeyondJS called BeyondRhino, targeted at the Rhino platform. One of the features this module provides is better interop between JavaScript types and Java types, including arrays and collections.var a = [1,2,3].toJava();OTOH I also added methods to JavaScript arrays that allow access and manipulation of the array object itself via Java’s collection interfaces. For example:returns an List interface on top of the specific array instance. So you can use it to manipulate that instance:Given the highly dynamic structure of JavaScript arrays, it seems to make a lot of sense to enable their use as generalized collection objects.Log in to Reply
  5. BTW the reason for this work is that I want to enable the use of JavaScript as a UnitTesting platform for Java.
  6. l.add(4);
    for ( var i in a ) print(a[i] + ” “); // outputs 1 2 3 4
  7. var a = [1,2,3];
    var l = a.list();
  8. a is now a Java array of integers (type [I…). You can also pass arguments to the toJava() method to control the type of array generated. The only upside of this technique when compared to what you did with JScript.NET is that it makes it clearer IMO that what you get is a copy of the original array.
  9. I also wanted to make it easier to convert between JavaScript and Java arrays. Since I couldn’t modify the behavior of the JavaScript interpreter to automatically convert between array types, I needed to take a more explicit route (well, technically I could modify the behavior since Rhino is open source, but I must admit I’m not feeling quit up to that task right now). My solution was to add a .toJava() method on all JavaScript basic types including arrays. Likewise I added a toJavaScript() function to convert Java arrays to JavaScript arrays. For example:
  10. November 15, 2003 at 3:41 pm
  11. Eric Lippert> Given the highly dynamic structure of JavaScript arrays, it seems to make a lot of sense to enable their use as generalized collection objects.> I want to enable the use of JavaScript as a UnitTesting platform for Java.Log in to Reply
  12. That’s a cool idea, but wouldn’t it be on the face of it easier to use Java as a unit testing platform for Java, rather than making Rhino interoperate better with Java? 🙂
  13. Indeed!
  14. November 17, 2003 at 2:38 pm
  15. Dan Shappir> but wouldn’t it be on the face of it easier to use Java as a unit testing platform for Java, rather than making Rhino interoperate better with Java?There are a couple of advantages in using a language like JavaScript over standard Java:2. It’s easier to construct some data structures using JavaScript than it is using Java. For example, if I want to create a collection containing numbers 1 through 10 that exposes the List interface. All I need to do using BeyondJS is write: (1).to(10).list() Consider the alternative Java code.BTW, It’s not necessarily a putdown of Java to claim that JavaScript may be a better PL for constructing tests than Java. Writing tests, especially dynamic tests, is not the same as writing standard apps. A lot of people are using things like Jython for testing Java, there is even a new PL being design specifically for this purpose: http://groovy.codehaus.org/Log in to Reply
  16. There is one significant disadvantage in using Rhino: the Rhino interactive environment doesn’t do code completion or argument tips. But you can use the standard JavaScript facilities to do introspection on Java objects.
  17. 3. If you look through the Rhino samples you’ll see that it’s also easier to create event handlers than in Java.
  18. 1. Rhino provides an interactive environment, so I can test classes interactively.
  19. Maybe, but it wouldn’t be as fun 😀
  20. November 18, 2003 at 4:26 am
  21. David SmithWouldn’t it just be better to use the real version of javascript instead of jscript which is riddled with problems?It has been my experiance that trying to hack code to try and get it to work differently than it does originally is a bad idea since it is pretty much unsupported by design.
  22. Log in to Reply
  23. Or for that matter instead of trying to hack code for .Net use sun’s ver of Java + IDE?
  24. July 23, 2004 at 1:44 pm

Skip to main content

Follow Us

Popular Tags

C# Scripting JScript VBScript Language Design COM Programming Rarefied Heights Puzzles Rants Performance Security C# 4.0 Non-computer SimpleScript JScript .NET Immutability Code Quality Pages Recursion Books

Archives

The JScript Type System, Part Five: More On Arrays In JScript .NET

As
I was saying the other day, CLR arrays and JScript arrays are totally different beasts.
It is hard to imagine two things being so different and yet both called the same thing.
Why did the CLR designers and the JScript designers start with the same desire —
create an array system — and come up with completely different implementations?

 

Well,
the CLR implementers knew that dense, nonassociative hard-typed arrays are easy to
make fast and efficient. Furthermore,
such arrays encourage the programmer to keep
homogenous data in strictly bounded tables
. That makes large
programs that do lots of data manipulation easier to understand
. Thus, languages
such as C++, C# and Visual Basic have arrays like this, and thus they are the basic
built-in array type in the CLR.

 

Sparse,
associative, soft-typed arrays are not particularly fast but they are far
more dynamic and flexible
 than Visual Basic-style arrays. They make it easy to
store heterogeneous data in any table without
worrying about picky details
 like exactly how big that table is. In other words,
they are scripty. Languages such as JScript
and Perl have arrays like this.

 

JScript
.NET has both very dynamic, scripty arrays
and more strict CLR arrays, making it suitable
for both rapid development of scripts and programming in the large
. But like I
said, making these two very different kinds of arrays work well together is not trivial.

 

JScript
.NET supports the creation of multidimensional hard-typed arrays. As with single-dimensional
arrays, the array size is not part of the
type
. To annotate a variable as containing a hard-typed multidimensional array
the syntax is to follow the type with brackets containing commas. For example, to
annotate a variable as containing a two dimensional array of Strings you would say:

 

var multiarr
: String[,];

 

The
number of commas between the brackets plus one is equal to the rank of the array.
(By this definition if there are no commas between the brackets then it is a rank-one
array, as we have already seen.)

 

A
multidimensional array is allocated with the new keyword
as you might expect:

 

multiarr
= new String[4,5];

multiarr[0,0]
= “hello”;

 

Notice
that hard-typed array elements are always
accessed with a comma-separated list of integer indices
. There must always be exactly
one index for each dimension in the array
. You can’t use the ragged array syntax [0][0].

 

There
are certain situations in which you know that a variable or function argument will
refer to a hard-typed CLR array but you do not actually know the element type or the
rank, just that it is an array. Should you find yourself in one of these (rather rare)
situations there is a special annotation for a CLR array of unknown type and rank:

 

var sysarr
: System.Array;

sysarr
= new String[4,5];

sysarr
= new double[10];

 

As
you can see, a variable of type System.Array may
hold any CLR array of any type and rank. However, there is a drawback. Variables of
type System.Array may
not be indexed directly because the rank is not known
. This is illegal:

 

var sysarr
: System.Array;

sysarr
= new String[4,5];

sysarr[1,2]
= “hello”;  // ILLEGAL, System.Arrays
are not indexable

 

Rather,
to index a System.Array you
must call the GetValue and SetValue methods with
an array of indices:

 

var sysarr
: System.Array;

sysarr
= new String[4,5];

sysarr.SetValue(“hello”,
[1,2]);

 

The
rank and size of a System.Array can
be determined with the Rank, GetLowerBound and GetUpperBound members.

 

Thinking
about this a bit now, I suppose that we could have
detected at compile time that a System.Array was
being indexed, and constructed the call to the getter/setter appropriately for you,
behind the scenes.  But apparently we
didn’t.  Oh well.

 

Next
time: mixing and matching JScript and CLR arrays.

Tags JScript JScript .NET Scripting

Comments (5)

You must be logged in to post a comment.

  1. Dan ShappirSo far the only potential downside I see in this design is that you cannot write a generic function that works for both JScript arrays and CLR arrays if the arrays have a rank higher than 1. I assume the following function will work on both types of arrays:Would have been even nicer if CLR arrays in JScript were somehow made to support the length property if they were one dimensional.BTW, I can’t resist, using the BeyondJS JavaScript library you could write the above code as:Log in to Reply
  2. var sum = arr.fold(“+”);
  3. Anyway, you could not write such a function if arr was, say, two dimensional.
  4. function sum(arr, length) {
    var result = 0;
    for ( int i = 0 ; i < length ; ++i ) result += arr[i];
    return result;
    }
  5. November 13, 2003 at 3:55 am
  6. Eric Lippert> I assume the following function will work on both types of arrays:> you cannot write a generic function that works for both JScript arrays and CLR arrays if the arrays have a rank higher than 1> Would have been even nicer if CLR arrays in JScript were somehow made to support the length property if they were one dimensional.> var sum = arr.fold(“+”);Log in to Reply
  7. I assume that your fold operator calls eval if the thing passed in is not a function object?
  8. Dude, wait for it. I said I’d discuss interoperability in my NEXT blog! 🙂
  9. Yep, but there are no JScript arrays with rank higher than one, so basically this is saying that you can’t write a generic function that handles arrays of different ranks — but wait a minute, that is what System.Array is for! ie, those rare cases where you don’t know the rank at compile time.
  10. Indeed.
  11. November 13, 2003 at 11:14 am
  12. Dan Shappir> Yep, but there are no JScript arrays with rank higher than one> that is what System.Array is for> Dude, wait for it.>I assume that your fold operator calls eval if the thing passed in is not a function object?You can also do “-“.toFunctionUnary() or “-“.toFunctionBinary() to control which version is generated. Here is the implenetation:Log in to Reply
  13. String.prototype.toFunctionUnary = function() {
    eval(“function __unary__(op) { return ” + this + ” op; }”);
    __unary__.op = this.valueOf();
    return __unary__;
    };
    String.prototype.toFunctionBinary = function() {
    eval(“function __binary__(op1, op2) { return op1 ” + this + ” op2; }”);
    __binary__.op = this.valueOf();
    return __binary__;
    };
    String.prototype.toFunction = function() {
    return “,!,~,++,–,new,delete,typeof,void,”.indexOf(“,” + this + “,”) > -1 ?
    this.toFunctionUnary() : this.toFunctionBinary();
    };
  14. BeyondJS implements a mechanism of converting strings to functions:”+”.toFunction() will generate a binary function
    “!”.toFunction() will generate a unary function.
  15. You caught me, I’m the impatient type 😉
  16. You misunderstood me. I wasn’t looking to write a function that would work for any rank. I was looking for a function that would work for, say, a 2D JScript array and a 2D CLR array. While I fully understand the reasons you chose the indexing syntax used for multi-dimensional CLR arrays, I simply pointed out that as result they are not polymorphic with multi-dimensional JScript arrays.
  17. Technically you are correct, but practically you simply create an array of arrays. And the resulting syntax looks just like C++ or Java. That was my point actually, that for a 2D JScript array you write a[1][2] while for a CLR array you write a[1,2].
  18. November 13, 2003 at 12:23 pm
  19. Eric LippertYeah, there’s no interoperation between ragged arrays and two-d arrays. But there is no interoperation between ragged CLR arrays and two-d CLR arrays either! Ragged arrays and rectangular arrays are pretty much separate concepts. In fact, the whole notion of rank of a ragged array is ill-defined — you can have a ragged array that is 3-d in some axes, 2-d in others, 1-d in still others, etc. There is no sensible notion of “rank”, so making them interoperate is more trouble than its worth.Log in to Reply
  20. Your implementation is pretty slick. (A less functional but perhaps more performant approach would be to generate all the unary and binary operator functions once and put them in a lookup table, rather than searching that string every single time and reconstructing the function object every single time.)
  21. November 13, 2003 at 12:51 pm
  22. Dan ShappirYou are quit correct about both points.With regard to BeyondJS, our motivation was always functionality, with performance a consideration but not more. Anyway, fold generates the function once, and then applies it iteratively to all the members. So the performance hit of generating a new function every time is relatively minor when compared to the cost of the loop.
  23. Log in to Reply
  24. With regard to ragged arrays: I always found it amusing that C++ employs the same exact syntax for accessing ragged and contiguous array. So a[1][2] would generate wildly different code base on the definition of a. OTOH it did buy you that polymorphic behavior I mentioned before.
  25. November 13, 2003 at 3:24 pm

The JScript Type System, Part Four: JScript .NET Arrays

As
I mentioned in an
earlier entry
, one of the major differences between JScript .NET and JScript Classic
is that JScript .NET now supports optional type annotations on variables.  The
number of built-in primitive types has also increased dramatically.  JScript
.NET adds value types boolean, byte, char, decimal, double, float, int, long, sbyte, short, uint, ulong and ushort.  In
addition, JScript .NET integrates its type system with the CLR type system — a string
in JScript has all the properties and methods of the string prototype and all
the properties and methods of a System.String.  Backwards
compatibility and interoperability with the CLR were two very important design criteria.

 

The
primitive types are pretty straightforward though.  Some
more interesting stuff happens when we think about how complex types like arrays interoperate
between JScript .NET and the CLR.  I already
discussed
 some of these issues regarding JScript and VBScript arrays, so let’s
quickly review the terminology:

 

sparse array
may have “holes” in the valid indices. A sparse array with three elements might have
elements 0, 10 and 1000 defined but nothing in between. The opposite of a sparse array
is a dense array. In a dense array all
the indices between the lowest and highest indices are valid indices. A dense array
with elements 0 and 1000 has 1001 elements.

 

fixed-size array
has a particular valid range of indices. Typically the size is fixed when the array
is created and it may not be changed. A variable-sized array
does not have any maximum size. Elements may be added or removed at any time.

 

single-dimensional array
maps a single index onto a value. A multi-dimensional array
may require any number of indices to fetch a value.

 

The
number of dimensions an array has is called its rank.
(Other terms such as dimensionality or arity are
occasionally used but we will stick to rank.)

 

static-typed array
or hard-typed array is an array where
every element is of the same type. A dynamically-typed or soft-typed array
may have elements of any type.

 

An associative array
is an array where the indices are strings. A nonassociative array
has integer indices.

 

literal array
is a JScript .NET array defined in the actual source code, much as “abcde” is
a literal string or 123.4 is
a literal number. In JScript .NET a literal array is a comma-separated list of items
inside square brackets:

 

var arr
= [10, 20, “hello”];

var item
= arr[1]; // 20

 

JScript
arrays are sparsevariable-sizedsingle-dimensionalsoft-typed associative arrays.
CLR arrays are the opposite in every way! They are dense, fixed-sizemulti-dimensionalhard-typed nonassociative arrays.
It is hard to imagine two more different data structures with the same name.  Making
them interoperate at all was a pain in the rear, believe me.

 

This
is a pretty big topic, so I think I’ll split it up over a few entries.  Let
me talk a bit about annotation and typing, and we’ll pick up where we left off tomorrow.

 

Traditional
JScript arrays are soft-typed; they can store heterogeneous data:

 

var arr
= new Array();

arr[0]
= “hello”;

arr[1]
= 123.456;

arr[2]
= new Date();

 

CLR
arrays, on the other hand, are hard-typed. Every element of a CLR array is the same
type as every other element. This difference motivates the type annotation syntaxes
for each. In JScript .NET the traditional arrays are annotated with the Array type
and hard-typed CLR arrays are annotated with the type of the element followed by []:

 

var jsarr
: Array = new Array()

jsarr[0]
= “hello”;

 

var sysarr
: double[] = new double[10];

sysarr[0]
= 123.4

 

Note
that CLR arrays are fixed-size, but the size
is not part of the type annotation
; sysarr can
be a one-dimensional array of double of
any size. This is perfectly legal, for example:

 

var sysarr
: double[] = new double[10];

sysarr[0]
= 123.4

sysarr
= new double[5];

 

This
throws away the old array and replaces it with a new, smaller array. But once
a hard-typed array is created it may not be resized.

 

Tomorrow:
true multidimensional arrays in JScript .NET.

 

Tags JScript JScript .NET Scripting

Comments (1)

You must be logged in to post a comment.

  1. How to list the statement of all var ? – Page 2 | keyongtechPingBack from http://www.keyongtech.com/4922739-how-to-list-the-statement/2
  2. Log in to Reply
  3. January 21, 2009 at 8:05 pm

The JScript Type System, Part Two: Prototypes and constructors

A number of readers made some good comments on my article on JScript typing that deserve to be called out in more detail.

 

First, I was being a little sloppy in my terminology — I casually conflated static typing with strong typing, and dynamic typing with weak typing. Thanks for calling me on that. Under the definitions proposed by the reader, JScript would be a dynamically typed language (because every variable can take a value of any type) and a strongly typed language (because every object knows what type it is.) By contrast, C++ is a statically typed language (because every variable must have a type, which the compiler enforces) but also a weakly typed language (because the reinterpret cast allows one to turn pointers into integers, and so on.)

 

Second, a reader notes that one of the shortcomings of JScript is that though it is a strongly typed language (in our new sense) that it is a royal pain to actually determine the runtime type an object. The typeof operator has a number of problems:

 

* null is listed as being of the object type, though technically it is a member of the Null type.

* primitives (strings, numbers, Booleans) wrapped in objects are listed as being of the object type rather than their underlying type.

* JScript, unlike VBScript, does not interrogate COM objects to determine the class name.

* If JScript is passed a variant from the outside world that it cannot make sense of then typeof returns “unknown”.

 

Perhaps there is some other way. Prototype inheritance affords a kind of type checking, for example.

 

Prototype inheritance works like this.  Every JScript object has an object (or possibly null) called its prototype object.  So suppose an object foo has prototype object bar, and bar has prototype object baz, and baz has prototype object null.  If you call a method on foo then JScript will search foo, bar and baz for that method, and call the first one it finds. The idea is that one object is a prototypical object, and then other objects specialize it. This allows for code re-use without losing the ability to dynamically customize behaviour of individual objects.

 

Prototypes are usually done something like this:

 

var Animal = new Object();

// omitted: set up Animal object

function Giraffe(){

// omitted: initialize giraffe object.

}

Giraffe.prototype = Animal;

var Jerry = new Giraffe();

 

Now Jerry has all the properties and methods of an individual Giraffe object AND all the properties and methods of Animal.  You can use IsPrototypeOf to see if a given object has Animal on its prototype chain. Since prototype chains are immutable once created, this gives you a pretty reliable sort of type checking.

 

Note that Giraffe is not a prototype of Jerry. Note also that Animal is not the prototype of Giraffe! The object which is assigned to the prototype property of the constructor is the prototype of the instance.

 

Now, you guys are not the first people to point out to me that determining types is tricky. A few years ago someone asked me what the differences are amongst

 

if (func.prototype.IsPrototypeOf(instance))

 

and

 

if (instance.constructor == func)

 

and

 

if (instance instanceof func)

 

The obvious difference is that the first one looks at the whole prototype chain, whereas the second two look at the constructor, right? Or is that true? Is there a semantic difference between the last two? Actually, there is. Let’s look at some examples, starting with one that seems to show that there is no difference:

 

function Car(){}

var honda = new Car();

print(honda instanceof Car); // true

print(honda.constructor == Car);  // true

 

It appears that instance instanceof func and instance.constructor == func have the same semantics.   They do not.  Here’s a more complicated example that demonstrates the difference:

 

var Animal = new Object();

function Reptile(){ }

Reptile.prototype = Animal;

var lizard = new Reptile();

print(lizard instanceof Reptile); // true

print(lizard.constructor == Reptile); // false

 

In fact lizard.constructor is equal to Object, not Reptile.

 

Let me repeat what I said above, because no one understands this the first time — I didn’t, and I’ve found plenty of Javascript books that get it wrong. When we say

 

Reptile.prototype = Animal;

 

this does NOT mean “the prototype of Reptile is Animal”.  It cannot mean that because (obviously!) the prototype of Reptile, a function object, is Function.prototype.  No, this means “the prototype of any instance of Reptile is Animal”.  There is no way to directly manipulate or read the prototype chain of an existing object.

 

Now that we’ve got that out of the way, the simple one first:

 

instance instanceof func means “is the prototype property of func equal to any object on instance’s prototype chain?”  So in our second example, the prototype property of Reptile is Animal and Animal is on lizard’s prototype chain.

 

But what about our first example where there was no explicit assignment to the Car prototype?

 

The compiler creates a function object called “Car”.  It also creates a default prototype object and assigns it to Car.prototype.  So again, when we way

 

print(honda instanceof Car);

 

the instanceof operator gets the prototype property (Car.prototype) and compares it to the prototype chain of honda.  Since honda was constructed by Car it gets Car.prototype on its prototype chain.

 

To sum up the story so far,

Tags JScript Scripting

Comments (16)

Cancel reply

You must be logged in to post a comment.

  1. Erik Arvidsson says:Just a side note. The prototype object is usually called __proto__ ( [[Protoype]] in the spec) and almost all ECMAScript engines (except the Microsoft ones) allows read write of this private property.
  2. Log in to Reply
  3. November 6, 2003 at 2:40 pm
  4. Jay Hugard says:Can you explain the logic behind the following madness (in JScript Classic), which appears to mean that a string is not always a String but a regexp is always a RegExp?slit instanceof String => false
    sobj instanceof String => true
    slit instanceof Object => false
    sobj instanceof Object => true
    typeof( slit ) => string
    typeof( sobj ) => object
    typeof( slit ) == typeof( sobj ) => falseAs generated by:function evalprintln( s )
    {
    if( !s ) { println(“”); return }
    var out = s + ” => “;
    try{ out += eval(s) } catch( e ) { out += “Error: ” + e.description }
    println(out);
    }var tests = [
    “slit instanceof String”,
    “sobj instanceof String”,
    “slit instanceof Object”,
    “sobj instanceof Object”,
    “typeof( slit )”,
    “typeof( sobj )”,
    “typeof( slit ) == typeof( sobj )”,
    null,
    “relit instanceof RegExp”,
    “reobj instanceof RegExp”,
    “relit instanceof Object”,
    “reobj instanceof Object”,
    “typeof( relit )”,
    “typeof( reobj )”,
    “typeof( relit ) == typeof( reobj )”
    ];Log in to Reply
  5. for( var ix in tests ) { evalprintln( tests[ix] ) }
  6. var slit = “Literal String”;
    var sobj = new String( “String object” );
    var relit = /literal regexp/i
    var reobj = new RegExp( “RegExp object” );
  7. function println(s) { WScript.echo(s) /*System.Console.Out.WriteLine(s)*/ }
  8. relit instanceof RegExp => true
    reobj instanceof RegExp => true
    relit instanceof Object => true
    reobj instanceof Object => true
    typeof( relit ) => object
    typeof( reobj ) => object
    typeof( relit ) == typeof( reobj ) => true
  9. Conversly, what is the recommended way of determining if a value is a string?
  10. November 6, 2003 at 6:01 pm
  11. Peter Torr says:Erik — IIRC, Netscape removed the support for __proto__ from their engine some years ago because it was a bad idea to let people mess with the prototype chain this way. The ECMA standard does not require the property to be exposed, and it is dangerous to do so.
  12. Log in to Reply
  13. November 6, 2003 at 6:32 pm
  14. Peter Torr says:Jay, if you read the ECMA spec you will see that there are string primitives and string objects, just as there are number/boolean primitives and number/boolean objects. Basically you should never use “new String(…)” because it is a waste of time and energy.print(nlit instanceof Number)
    print(nlit instanceof Object)
    print(nobj instanceof Number)
    print(nobj instanceof Object)
  15. Log in to Reply
  16. var nlit = 42
    var nobj = new Number(42)
  17. November 6, 2003 at 6:40 pm
  18. IUnknown says:>JScript, unlike VBScript, does not interrogate COM objects to determine >the class name.Log in to Reply
  19. This gets the award for the most unrelated error message:
    TypeName(myObject)
    throws, when the type information is unavailable:
    Out Of string space: ‘TypeName’
  20. November 7, 2003 at 2:02 am
  21. Eric Lippert says:Can I see a repro for that? If the type name is unavailable, it should return “Unknown” or, if it is an IDispatch, “Object”.Log in to Reply
  22. TypeName does return “Out of string space”, but only when the attempt to allocate the string for the type name fails.
  23. November 7, 2003 at 12:54 pm
  24. Erik Arvidsson says:Peter: I know both the Mozilla engines (C and Java) and the Macromedia engine has support for this. I was pretty sure that Opera supported this as well but I just verifed that they do not.fucntion SubClass( args )
    {
    SuperClass.call( args );
    }
    SubClass.prototype.__proto__ = SuperClass.prototype;Log in to Reply
  25. Well, well… now that we have ECMAScript v4 around the corner there is no real need for this.
  26. I can understand why it is considered dangerous but usage of __proto__ can be very useful. For example it allows you to skip creating an instance that is used as the prototype.
  27. November 7, 2003 at 3:14 pm
  28. Eric Lippert says:> Well, well… now that we have ECMAScript v4 around the corner there is no real need for this.Log in to Reply
  29. That’s news to me. Could you more precisely define “around the corner”?
  30. November 7, 2003 at 5:02 pm
  31. Isaac says:> conflated static typing with strong typing
    “static typing” unhelpfully confuses when checks are made with the kind of checks that are made.Simpler to understand that JScript is a dynamically-checked untyped (safe?) language.Log in to Reply
  32. I think you demonstrated that it’s pretty hard to figure out what the type of a value is in JScript – so why insist on calling it a typed language?
  33. Simpler to understand that C++ is a statically-checked weakly-typed language (and also that some things are dynamically checked in C++).
  34. November 7, 2003 at 5:26 pm
  35. Anonymous says:[quote]Can I see a repro for that? If the type name is unavailable, it should return “Unknown” or, if it is an IDispatch, “Object”. [/quote]Also see this:
    http://groups.google.com/groups?q=typeName+%22Out+of+string+space%22
  36. Log in to Reply
  37. I got this when trying it on a COM object that uses run-time generated type info (ie. CreateDispTypeInfo). This only provides a minimal implementation of ITypeInfo. This is not common(or recommended) so I wouldn’t worry too much. If it helps the COM object is out of process.
  38. November 8, 2003 at 6:05 am
  39. Eric Lippert says:OK, then I have a guess as to what is happening. This is a TERRIBLE way to wait for an out-of-proc object (OOPO) to shut down. I’ll bet that there is a race condition here where the stub is returning a pointer to memory that becomes bad when the OOPO shuts down. So the TypeName method essentially gets passed a pointer to bad memory.So what we’ve got here is a memory corruption bug that is not crashing, but returning a bogus error instead.
  40. Log in to Reply
  41. Now suppose that the bad memory happens to be in a readable committed page, but contains garbage. We assume that the thing is a BSTR, so we look at the value stashed preceding the string body to determine the length. That could be any old number; the odds that the number happens to be larger than the largest remaining heap block are actually very good.
  42. November 8, 2003 at 10:57 am
  43. Anonymous says:The link I posted was just something I found while searching on the error. My situation relates to using run time type info. Have you tested TypeName with run time type info? It is obsolete and I only use it for testing so this discussion is essentially accademic.I think my object was based on the code found here, so if you want to copy and paste:
    http://docs.rinet.ru:8083/VidimyyC/vcu28fi.htm#I25
  44. Log in to Reply
  45. My guess is that something similar is happening, ITypeInfo must be returning bogus data. Assuming that you check all error results this could even be a bug in the implementation of ITypeInfo for CreateDispTypeInfo. As mentioned, this is obsolete and probably hasn’t been looked at for a long time.
  46. November 9, 2003 at 4:13 am
  47. Erik Arvidsson says:”That’s news to me. Could you more precisely define “around the corner”?”Known implemantions of ECMAScript v4:One possible reason for JS2 taking so long might be that ECMA moved all their resources to C# instead?
  48. Log in to Reply
  49. ActionScript

    QTScript

    JScript 7

  50. I guess I was wrong 😥 This has been in the works for way too long…
  51. May 2, 2004 at 7:07 am
  52. Eric Lippert says:A more likely reason is that Waldemar Horwat, who was the primary driving force behind the E4 spec, no longer works for AOL-Time-Warner-Netscape and hence is no longer being paid to drive the spec process forward. (Rumour has it that he’s at google now.)Log in to Reply
  53. Microsoft is still involved in the E4 process, but it is slow going.
  54. May 2, 2004 at 8:23 am
  55. Fabulous Adventures In Coding says:Log in to Reply
  56. October 7, 2004 at 1:32 pm
  57. Intercept instanceof | keyongtech says:PingBack from http://www.keyongtech.com/4947719-intercept-instanceof
  58. Log in to Reply
  59. January 18, 2009 at 12:41 pm