Lessons from smart JSL mistakes

by on Jan.11, 2011, under JMP & JSL

This is a follow-up to my previous post, “A script to check and change a JMP preference ($2).

Background

Marianne Toft asked the JMP scripting group on LinkedIn a question about managing preferences with JSL:

Does anybody know how to read a specific setting from the Preferences? I need to read the status of ‘Use JMP locale settings’ and if 0 then change to 1 and give the user a message to restart JMP.

A $2 solution

I wrote a $2 script that solves the problem, and I sent Marianne a coupon code for a free copy, in thanks for posting such a thought-provoking JSL challenge.

My strategy is described in detail in that previous post, if you’d like to try writing it yourself with a bunch of pointers.

It’s also available as a $2 download if you’d rather just get the final code, fully commented and tested on JMP 8 and 9 on both Windows and Mac. One reason to consider buying this script instead of a tall drip is that the script is abstracted to check and change any preference, not just Use JMP Locale Settings( 0|1 ). That abstraction reveals some scripting tricks that might be helpful in your other scripting projects.

I’ll be delighted if someone else comes up with a more elegant solution, because mine is brute force. That’s because JMP doesn’t return the current preferences as an expression or a string or anything else you could potentially operate on with JSL. It just echoes the preferences to the Log window, so you need to read the text of the Log window and deal with that. If I’m missing something here, someone please enlighten me!

Several smart mistakes and lessons to learn from them

Several other people wrote helpful suggestions. Unfortunately, their code didn’t work, and we can all learn some important things about JSL by studying their smart mistakes.

Mark Anawi suggested using Show Preferences(Use JMP Locale Settings) ) to see the setting for just that preference. It’s a good guess at an argument for the Show Preferences() command, but it’s not actually a supported argument. It doesn’t work. You’ll just get the usual output for Show Preferences() without an argument.

Lesson: when you supply an argument JMP doesn’t understand, sometimes you get errors, but a lot of the time it’s just ignored, silently. You get no indication that you did something that doesn’t work. The only way you can be sure it was ignored is to try it again without the argument, or with a different argument, and check whether anything changed.

Peter Wiebe wrote an elegant solution that uses a For() loop to step through each Arg() of an expression, looking for and if necessary changing the preference in question. His code is concise and beautiful:

jmp_rulz = getpreferences();
For( i = 1, i <= N Arg( jmp_rulz ), i++,
    If( Contains( Char( Arg( jmp_rulz, i ) ),
        "Use JMP Locale Settings(0)" ),
        Preferences( Use JMP Locale Settings( 1 ) );
        New Window( "Restart JMP",
            Panel Box( "Restart JMP",
                Button Box( "OK", << closewindow )
            )
        )
    )
);

His code is far more elegant than mine. It’s an efficient strategy! Unfortunately, it doesn’t work, for several reasons.

Get Preferences() isn’t a command in JMP 8 or 9, so the first line returns an error:

Name Unresolved: get preferences in access or evaluation of 'get preferences' , get preferences()

In the following script, error marked by /*###*/
::jmp_rulz = get preferences() /*###*/

I assume that was just a typo, though. Peter meant Show Preferences(), but as I warned at the outset, that command doesn’t return an expression. It just echoes text to the Log window. So, after running the first line (with “get” changed to “show”), jmp_rulz contains . (missing).

Therefore, N Arg(jmp_rulz)i is never < 0, so the For() loop never runs.

I like Peter’s script better than mine. It should have worked—Show Preferences() really ought to return an expression so that his For() loop can step through it and look for the specific preference in question. Unfortunately it doesn’t. I hope our heroes in JMP R&D will consider improving this in a future version.

Why did Peter think this worked? Probably because when he ran it, he didn’t get any error messages. I didn’t either, and at first I was trying to figure out why his script worked when I’d already proven to myself that Show Preferences() doesn’t return anything into the jmp_rulz global.

He probably also checked the Log window after running it to confirm that the preference was on, saw that it was, and figured that it must have worked. He wouldn’t have expected to see his “Restart JMP” window unless it the setting was off to begin with, so he probably figured it had already been on.

Peter’s a clever scripter, by the way—whenever he posts something on LinkedIn, I make sure to read it, because I know I’ll learn something.

Lesson: Not getting error messages in the Log window doesn’t mean your script works.

Why should we care more than “Oops!”?

OK, we all make mistakes, especially when we’re writing JSL. (We do, right? Please tell me it’s not just me!) No big deal.

So why am I writing about these mistakes? Believe me, I’m not doing this to make anyone feel bad! They were both great suggestions, and they looked like they worked. Both Mark and Peter had good reasons to think they were correct.

I see some important lessons for JMP scripters.

Be aware that JMP has a lot of silent error conditions

Mark’s command Show Preferences(Use JMP Locale Settings) didn’t produce any error messages, and it did echo the current settings to the Log, so it was reasonable for Mark to assume that putting a specific preference as an argument to the command was a valid syntax variation that would show the setting of that specific preference.

This is a type of mistake I make all the time: I come up with a syntax improvement in my imagination or in my failing memory, one that isn’t real, and it seems to work, so I think it is real.

But it’s not. There are a lot of situations where JMP is silent about errors in JSL. Sending an unrecognized argument to a command is a typical example. Another is when you put something that needs to be evaluated—say, an arithmetic expression to set a parameter from a global—inside an object message or platform launch command, and it turns out that the particular message or command you’re using is not actually capable of evaluating its arguments. (JMP is inconsistent that way, unfortunately. Sometimes you can get away with it, sometimes you can’t, and you can only find out by testing.)

Be sure you know which kinds of errors are silent (and deadly)

This article would go on for a hundred pages if I tried to list every possible error and whether JMP will report an error message or not, but here are some of the most important things to know:

Some typical mistakes that usually produce error messages:

  • Operators that don’t exist, e.g. Until() instead of While()
  • Globals that don’t exist or haven’t been assigned values yet (or have had their values cleared with Clear Globals()
  • Using = instead of == as a conditional test, e.g. If(a=1, write("a"), write("not a")) would assign 1 to a, but JMP alerts you to the fact that you probably meant to test whether a equals 1 with a==1

Some typical mistakes that do not produce error messages:

  • Messages that don’t exist, e.g. Distribution[1]<<close; instead of Distribution[1]<<close window; just silently pretends to run, returning the name of the object (in this example, Distribution[]) as if it had done something to it
  • Arguments that don’t exist, e.g. Show Preferences(Initial Splash Window) instead of Show Preferences(All) simply runs as if you hadn’t included the unrecognized argument
  • Scoping mistakes, e.g. inside Formula(), any variable that you don’t explicitly scope, such as by putting :: before a global’s name, is going to be interpreted as a column name, and unless that leads to an error condition or some kind of unexpected behavior that you notice, you will never realize your mistake
  • Conditional mistakes, e.g. For() loops or other loops that never need to iterate simply don’t iterate, and they don’t tell you that they haven’t—unless you write some code to report on the iteration, e.g. you could set some global inside the For() loop and then check the global after the For() loop

Is it a bug when JMP doesn’t report an error?

Not necessarily.

In the case of invented messages not being flagged, my opinion is yes, that should be reported as an error. It’s too dangerous to think you’re accomplishing something and never realize JMP’s just ignoring you. But consider what the JMP developers are up against: JSL is so flexible that you can send lists of messages like obj << {mesg1, mesg2, …, mesgN}, and you can send messages to an obj << mesg group, and that’s just for starters. It would be hard to make the error-checking and reporting bulletproof.

What about invented arguments? Well, again, I’d say yes, if JMP can’t interpret something, it should say so. But again, consider what the developers are up against: most, but not all, commands in JSL can take arbitrarily complex expressions inside their parentheses. For example, myList={}; InsertInto(myList, 4) is a straightforward way to put something in a list, but you could also have it calculate the thing it needs to insert, e.g. myList={}; InsertInto(myList, 1+3). That calculation could be incredibly complex, involving If() tests and For() loops and all kinds of insanity. Any number of things could go wrong inside those calculations. How is JMP’s InsertInto() supposed to know that the problem is you made up an argument and not that you attempted a calculation that didn’t work out?

What about unscoped, mis-scoped, or insufficiently scoped variables? Hard to say. The name-resolution and scoping details of JSL keep improving with each new version of JMP, so that careful scripters can get better and better control of these kinds of problems, but for most scripters (and I include myself), the bigger problem is that we don’t realize when we’re not scoping carefully enough. For example, inside a Formula expression, any variables that aren’t scoped otherwise will get interpreted as operators (if one by that name exists) or as columns (if one by that name exists). If either of those exist, then the global you thought you were calling is not going to come into play. For example:

x=1; column("Age Next Year") << set formula(x + :Age)

That should add 1 to each Age column’s value and put it in the Age Next Year column, right? Right. As long as you don’t also have a column named “x”! If you do, it’s going to add the value in the Age column to the value in the x column and put it in the Age Next Year column; more precisely, it will do that for each row.

Or what about this:

For=1; column("Age Next Year") << set formula(x + :Age)

Will that work? Well, if you want to play along, open good old Big Class.jmp, create an Age Next Year column, and try these two scripts. First try the one with x=1. That’ll work fine since Big Class doesn’t have an x column. Now try the one with For=1.

What happens?

Do you think it worked? Well, you might—after all, the Age Next Year column has the right answers in it, and you didn’t get an error message in the Log.

But did it? Let’s check more carefully. Change the first bit to For=5 and run it again. Do you get values like 17, 18, 19 as you should?

No!

Do you get error messages?

No!

What’s going on? Remember, For is an operator! JMP sees a For() operator even though its () parentheses and arguments aren’t there. Since For has no arguments, it doesn’t do anything, and then it can’t add :Age to it because there’s no “it” to add it to, so the column is unchanged. JMP doesn’t think anything is wrong, because it didn’t see anything it couldn’t interpret. It knows For()! It just didn’t realize that by For what you really meant was 5!

The first time, when For=1, we didn’t realize the problem, because the Age Next Year column already had the answers we expected and we didn’t have any way of knowing they didn’t change because of an error rather than because the answers were the same. The second time, when we were expecting different answers, we had a chance of noticing our mistake—but only if we checked those answers!

What do you do if you actually do want to use a global that shares a name with an operator or a column? You scope it. Change it to ::For inside the Formula(), and you’re back in business.

So, it’s not clear that JMP is in the wrong when errors don’t get messages. It’s not necessarily a bug, although it certainly can be frustrating! We can all hope that these kinds of errors will be trapped better in future versions of JMP, but in the meantime, we need to take that responsibility ourselves.

Tips on interpreting error messages

JMP’s error reporting is quite helpful, but it can be frustrating at times. Here are a few tips:

  • Sometimes it’s hard to find the /*###*/ in the Log to see where your error is. Don’t forget about Edit/Find, or the keyboard shortcut Ctrl-F or Command-F!
  • Sometimes the /*###*/ will be in a misleading place. Start by investigating the thing that comes right before it, but if you’re sure that’s not in error, see if you can find some other problem that’s causing that thing to get read in some way other than what you meant, e.g. if you had a comma instead of a semicolon, it might be getting read as the “else” action in an If(), where you meant it to be an extra command in the “then” action.
  • When you get error messages about an unexpected or a missing ) or , (parentheses or commas), if the /*###*/ marker doesn’t help you, try getting some help from the Reformat Script utility. Try reformatting the whole script, and see where the cursor sits after you get the error message again. If you don’t see the problem nearby, try selecting chunks of the script and reformatting those. Keep going until you find the chunk of script that won’t format; your problem is inside that chunk somewhere.
  • Notice that the Reformat Script utility does helpful auto-indenting of your code, so that things happening inside other things will be indented further than their containers, and the closing ) will appear vertically-aligned to the command and opening ( that JMP thinks it corresponds to. Make sure that JMP’s alignment of )s matches what you think you’re doing. Getting messed up inside the parentheses is an especially common mistake when you’re writing custom display boxes.
  • Also try using the fence-matching feature: double-click on a parenthesis, and the Script Editor should highlight everything up to the matching parenthesis. See if it matches what you think it should; if it doesn’t, you’ve got one missing or extra somewhere. Note: one problem with the Script Editor’s fence-matching is that it won’t always select all the way up to a matching parenthesis; it’ll just do nothing when you double-click. This seems to happen when the chunk is too big—if the matching parenthesis is scrolled too far off-screen, JMP doesn’t even try. I hope this will be improved in a future version of JMP. In this situation, using an external programmer’s editor (such as jedit on Windows or BBEdit on Mac) can be helpful.

Be sure to test that your code is doing something

The smart JSL mistakes we saw above didn’t report errors, so the scripters didn’t realize anything was wrong. This brings up an important point: you need to test not only that the results you get are correct, but also that you’re getting results!

Mark’s error was to invent an argument. JMP’s response was to ignore the fictional argument. So how did he know it was a nonexistent argument and not just an undocumented one? The only way to be sure is to try the command twice—first without the argument, and again with the argument—and see if the results actually differ. If they don’t, that argument isn’t doing anything. Either it doesn’t exist, or you’re making some kind of a mistake in how you’re using it.

Peter’s mistake was not checking that his For() loop actually did any iterations. Since NArg() was zero, his i counter that started at 1 was never less than zero, so the loop didn’t do a single iteration. JMP skipped right over it. There are several ways to test for this kind of problem. The easiest way is just to put Write() statements inside the For() loop that will report what’s going on to the Log window. As long as you make sure to look at the Log and see that the messages you expect are showing up, that’s good enough.

But if your For() loop is happening as part of a larger program, you might not think to look for the absence of a message, right? In that case it would be better to lay a trap. For example, you could set a global right before the For() loop, do something to that global inside the For() loop, and then test the global after the For() loop. For example:

test=0;
For( i=1, i<6, i++,
    [ do whatever you need to do here ];
    test++;
);
if( test==0, throw( "The For loop did not iterate" ));

(Note: it wouldn’t necessarily be a mistake for a For() loop never to iterate. There might come a situation where you want to write an iteration test that takes advantage of For()‘s ability to decide whether to iterate or not.)

What are your issues with JSL errors?

What kinds of silent-but-deadly errors have you noticed? What kind of errors have you found it hard to track down? What tips do you have for trapping silent errors, or figuring out reported errors?

Please ask your questions and share your experiences in the Comments below. We’ll be happy to follow-up with replies to your comments or at greater length in future blog posts.

Leave a Comment more...

A script to check and change a JMP preference ($2)

by on Dec.23, 2010, under scripts

Marianne Toft asked the JMP scripting group on LinkedIn a question about managing preferences with JSL:

Does anybody know how to read a specific setting from the Preferences? I need to read the status of ‘Use JMP locale settings’ and if 0 then change to 1 and give the user a message to restart JMP.

Several people had helpful suggestions, but unfortunately it’s just not a trivial problem. It’s a surprisingly tricky problem.

Challenges

First, by default Show Preferences() only shows the preferences that you’ve taken to change. You need to use Show Preferences(all) to see the defaults listed, too.

Second, Show Preferences(SomeSpecificPreferenceHere), although a good guess, doesn’t actually work. You’ll just get the usual output for Show Preferences() without an argument.

Lesson: when you supply an argument JMP doesn’t understand, sometimes you get errors, but a lot of the time it’s just ignored, silently. You get no indication that you did something that doesn’t work. The only way you can be sure it was ignored is to try it again without the argument, or with a different argument, and check whether anything changed.

Third, Show Preferences( [all] ) doesn’t return an expression. It returns missing. The information you need is echoed to the Log window, but it’s not returned as a string or an expression or a list or anything else you’d be able to do some work on. So you need to overcome that.

How to solve the basic problem

In JMP 9, you can Get Log(), which returns a list of strings, one for each line of whatever’s in the log. So to work efficiently, you need to Clear Log() first, then Show Preferences(all), then do Get Log().

Well! Now you have a list of strings, and you need to find the item in that list that is relevant, so you need to put a Contains() test inside a For() loop.

It’s a string, so you have to transform it into an expression with Parse() and some other bookkeeping, so that you can probe its argument with Arg().

Then check whether the argument is the desired one, and if not, change it, by editing the expression with Substitute() and then immediately doing Eval() on the result of that.

Now we need to let the user know we did something, and perhaps that they need to restart. So, put up a New Window() with a Text Box() saying that a preference has changed and they need to restart. Include a Button Box() for “OK” that closes the alert window.

So, that’s the gist of the strategy.

Abstract the script

But, why write all this just for this one specific preference? Why not write it to check and if necessary change any preference?

So, start out with a couple globals that set the preference and the desired value.

But wait! Marianne’s example involves a numeric argument, but other preferences have text arguments; consider Use JMP Locale Settings(0) vs. Report Table Style("Plain"). So we need to make sure our script can work properly whether the particular preference takes a numeric or string value.

And while Marianne’s example requires a restart for the changed preference to take effect, most preferences do not. Might as well set a restartNeeded=1 if it’s important, or 0 if it’s not.

In that alert window, we only need to tell them to restart if it’s important, not if it’s not, so let’s add an If Box() around the Text Box() that only shows the instruction if it’s needed.

If the preference was what we wanted in the first place, we should probably echo something to the Log window, just so we know it ran, if nothing else. We need a Write() statement for that.

Which version of JMP?

But wait, there’s more! The Get Log() command didn’t come along until JMP version 9. If you haven’t been able to upgrade yet, then you need to take another strategy to this whole problem.

There might be a better way to do this, but the only way I could find was to Save Log() to a text file, then Load Text File() to a string, then use Substr() and a bunch of arguments with Contains() to chop out the single preference we care about. From there, the script runs about the same way.

And we need to put all this inside a test for JMP Version(), which returns a string, and usually we need to Trim() it and then do an inequality test, e.g. If( Trim( JMP Version() ) > "9", …), where the first argument is the Get Log() strategy described first, and the second argument uses the Save Log() strategy.

What about nested preferences?

Yet another problem! The script so far deals with top-level preferences with simple, single arguments, either numeric or character. But some preferences are trickier, e.g.,

Preferences(
  Fonts(
    English(
      Monospaced Font("Courier", 10)
    )
  )
);

In this case, the Parse() line will need to be more elaborate, and you’ll have to nest more Arg()s around it to dig down to the particular setting value of interest. Conditioning for such a situation is left as an exercise for the reader, primarily because it’s a hassle, it’s not what Marianne needs, and the syntax of the Preferences() command in JMP is arbitrary enough that we can’t really write code today that will be future-proof; we don’t know how deeply nested a given preference might be, nor what kind of arguments it might expect.

Nevertheless, the core of the script should be useful for anybody needing to query and change most preferences, and the elaboration necessary to adapt this script to a more deeply nested preference isn’t too complicated.

If you have any trouble with it, though, please feel free to contact us at Global Pragmatica LLC®; we’ll be happy to customize this script further for you at a nominal cost.

Purchase the code for $2

A finished, working, version-neutral script that has been tested on JMP 8 and 9 on Mac and Windows is available for purchase and immediate download for $2 US.

I am sending Marianne a coupon code for a complimentary copy, with my thanks for her intriguing question.

While the script itself might not be of immediate use to most readers, I hope that the strategies seen in the script will be of far greater value. Please contact me if you are in any way disappointed in the script, and I will arrange for an immediate bug-fix or refund upon receipt of your concerns.

Editable script: $2.00

1 Comment more...

Erin Vang to moderate upcoming events

by on Dec.09, 2010, under facilitative leadership, localization

I’m pleased to announce that I’ve been asked to facilitate several upcoming roundtable events:

Virtual Roundtable: Shrinking the triangle; Is it possible to achieve good, quick, and cheap in localization? on the web on 20 January 2011

From Chris Raulf:

I recently posted the following question on LinkedIn: How do you achieve good, quick, and cheap in localization?

Having worked in the industry for the last decade (on the customer-side as well as on the vendor-side in production, sales, and marketing), it seems that one has to pick and choose two out of the three: Good, quick, and cheap. My intention by posting this question on LinkedIn was to get the opinions and insights of a wide variety of fellow industry professionals (customer and vendor-side) and, oh boy, it seems like everyone has a strong opinion on this topic.

Well, the LinkedIn discussion is still going on but I’d like to take this discussion to a different level and actually have industry experts talk about it in a virtual Roundtable.

I’m currently working on assembling a panel group of opinionated and expert industry players from the translation-side, localization vendor-side, internationalization-side, content development-side, tools and technology-side, as well as from the customer-side.

So far I’m happy to present the following panelists:

Roundtable facilitator: Erin Vang, Owner of GlobalPragmatica

Renato Beninatto, President of the European Language Industry Association (ELIA)

Jennifer Beaupre, Director of Global Marketing at acrolinx GmbH

Adam Blau, Director of worldwide sales at Milengo

Adam Asnes, President & CEO at Lingoport

Michael Fruhwirth, Owner of Technical Translations

Mylène Vialard, Owner and Senior Translator at Mylene Vialard Translations

Daniel Goldschmidt, Co-founder at RIGI Localization Solution

The goal of this virtual Roundtable is to stimulate debate, learn about our challenges, discuss potential solutions, etc. and see if we can come away with new ideas that will help us cope with the “Fast, Cheap, and Good Quality” triangle.

Localization Technology Roundtable in Palo Alto on 3 February 2011

The best conversations happen when the right people get together

The Localization Technology Round Table brings together 5 industry leaders to present an open technology framework that speeds up time to market and drastically reduces your localization costs.

Together, Lingoport, Acrolinx, Clay Tablet, Milengo and Asia Online will show how advanced, modular localization technology addresses the challenges faced when launching products or services to international markets in multiple languages.

You’ll learn the key considerations when taking an international product from design to launch through, Internationalization,  Information Authoring,  Content Management,  Localization and Translation Automation.

And you’ll learn how this is achievable quickly, and with fewer resources, while maintaining a consistent brand and user experience that builds value, saves time and reduces costs.

  • Access a wealth of localization experience from industry experts
  • Discover new technologies and new ways of working that are already changing the localization landscape
  • Learn strategies that can streamline your localization efforts and help you quickly launch products worldwide
  • Share information with like-minded peers and learn proven practices that you’ll find nowhere else

Agenda for the Localization Technology Round Table in Palo Alto

Speakers’ biographies

Please contact us if you’d like to hire me to moderate and/or facilitate your event.

1 Comment more...

Mandelbrot and music: on listening in fractal dimensions

by on Oct.25, 2010, under random

Benoit Mandelbrot died this month. He was the guy who came up with fractal theory, which led to all those gorgeous computer graphics like this one, from Wikimedia Commons:

A Mandelbrot set

Last week, my friend and contradance bandmate Tina Fields wrote an essay about Mandelbrot’s ideas on her blog, Indigenize! I found it quite thought-provoking, and it surprised me how much I learned from her post, since I’m the one with the math degree. My next surprise was how Tina’s thoughts on this mathematician inspired me to think about listening to music.

This essay is in response to ideas she raises in her essay, so go read hers first and then come back here!

First I’d like to amplify her comment about coastlines by quoting this passage from Mandelbrot’s obituary in the New York Times, about how coastlines played a role in the genesis of his theory:

Dr. Mandelbrot traced his work on fractals to a question he first encountered as a young researcher: how long is the coast of Britain? The answer, he was surprised to discover, depends on how closely one looks. On a map an island may appear smooth, but zooming in will reveal jagged edges that add up to a longer coast. Zooming in further will reveal even more coastline.

“Here is a question, a staple of grade-school geometry that, if you think about it, is impossible,” Dr. Mandelbrot told The New York Times earlier this year in an interview. “The length of the coastline, in a sense, is infinite.”

In the 1950s, Dr. Mandelbrot proposed a simple but radical way to quantify the crookedness of such an object by assigning it a “fractal dimension,” an insight that has proved useful well beyond the field of cartography.

To me, that’s the real genius of his discovery—viewing scale as a dimension. If we measure the coastline or the surface of the broccoli from a mile away we get a much different answer than if we measure it from close up and far different still if we measure under a microscope.

So what is scale, really, but a matter of perspective?

Let’s consider the metaphorical potential: if perspective is a dimension, how does it change the way we view truth about our world? You have some truth, I have some truth, and the differences are not necessarily contradictions but spectral variations along the perspective dimension.

Tina’s big gift to me in her essay isn’t so much her point about Mandelbrot’s focus on verbs rather than nouns, although I enjoy that, too, but her encouraging us to think about new things fractally. The first thing that comes to my mind is Beethoven. (Perhaps I should explain that besides working in statistical software and facilitative leadership, I’m also a professional horn player and hold degrees in music performance and music history.)

Beethoven leads my pantheon, and here’s a bit on why: his compositional technique is extraordinary, and the more you know about musical composition and performance, the more you hear in his work. In addition to doing all the usual classical things—the usual structural designs (four-movement symphonic architecture with movements in sonata, menuet or scherzo, sonata-rondo, etc. forms, linked in a progression of related tonalities, yada yada Haydn, blabbety-blabbety Mozart, blah blah Bach), German-Italianate phrases, symphonic devices of his environment and era—he throws in a few more tricks all his own, chief among them his idea of motivic development.

His every melodic gesture is built up from the smallest motives, e.g. his Fifth Symphony‘s four-note “ba-ba-ba-BOM!” opening. That simple four-note figure is sequenced, layered, mutated, and warped all throughout the first movement, each phrase a new assemblage of basic building blocks, each harmonic gesture arising out of layers and layers of sequences of this tiny musical block and several others.

You can easily find YouTube and other recordings of Beethoven 5 if you’d like to remind yourself how it goes, but I’d recommend buying yourself a great recording. There are many excellent options; one I’d particularly recommend is Bernstein’s with the New York Philharmonic.

All the composers of Beethoven’s time (and throughout most of history, with differing vocabularies, of course) have adhered to various conventions from the largest possible scale (the arc of their developmental style through their lifetimes) down through the structure of each opus, each movement within, etc., down to the smallest-scale assumptions about harmonic structure, idiomatic styles of individual instruments, and so forth, but Beethoven brings it all to a whole new level, honoring all those formal rules while also constructing everything both melodically and harmonically, both vertically and horizontally in each case, out of these tiniest of musical blocks.

(We later see Wagner up Beethoven’s ante with his Romantic adaptation, the leitmotiv, where each character, event, place, and even philosophical concept is represented by its own fragment of musical DNA, all these leitmotivs swirling in a pan-theatrical operatic swamp of continuous through-composition, rejecting while also embracing formal conventions in a megalomaniacal Gesamtkunstwerk.)

Struggling valiantly now to pull back from this tangent to return to fractal theory, I might suggest that we appreciate Beethoven and indeed all music along fractal dimensions. For many, Beethoven’s Fifth Symphony is, simply, its opening four notes and the loud romp to follow. The scale of observation is large; the perspective is simple. “Fun music!”

Indeed, who wouldn’t appreciate it on such simple terms? When I was hospitalized with pneumonia as a second grader, my parents brought me the best of all possible get-well presents: a portable cassette deck, including a cassette of the first movement of my favorite symphony, which dad had recorded by sitting next to the phonograph (mono, of course) holding the mic near the cabinet speakers while the needle rode its groove. I listened to that tape over and over during my several weeks of long days alone in a hospital room. I’m not sure what I heard, exactly, but I know that by the time I was discharged, I could have sung the whole movement. (I wish he’d recorded the whole symphony for me, because I’l never know the rest of it nearly as well.)

As I’ve developed as a musician, I’ve lost touch with how I used to hear music. I often wonder what normal people hear, and I like to ask people to tell me why they like certain music or what they noticed in a concert.I know that I used to hear the pretty music, and while I can tell you to the minute when it all changed, I can’t for the life of me remember what I used to hear.

It changed the summer after eighth grade. I was at orchestra camp, sitting in a muggy auditorium on a hot summer night, and probably intoxicated by the pheromones of my new friends. We listened to a piano quartet recital. First I noticed that I was hearing a group whose intonation was so tight, they made the freshly, expertly-tuned Steinway sound out of tune. All pianos are out of tune, but it was the first time I heard it for myself. Then I realized I was hearing four virtuosi playing the crap out of their instruments as both individuals and as a collective.

Then my trumpeter friend leaned over and said, “You know, we’re never going to hear music like normal people again,” and for only a moment I wondered what he meant. I spent the rest of the concert hearing, seeing, feeling the compositional structure, the interplay of themes, the exploration of key areas, the work of the individuals and their ensemble, and on and on. The only limits to the depth of scale in my listening were my musical intelligence and attention span.

That night was my awakening as a listener. In the decades that followed, my musical intelligence has evolved tremendously, but I still find that the richness of what I hear is limited only by my abilities and attention span.

So, locating my metaphor in the area of musical perception, I might suggest that our listening has a fractal dimension. Anyone can hear the sounds. But our perspective—the granularity of our musical knowledge and the intensity of our focus—determines in how large a range along the fractal dimension we perceive the music; how much we hear of the infinite possibilities depends on how large or small is the scale of our listening.

How do you hear?

As I said, I’ve long since forgotten how I used to hear music. How do you hear music? What do you hear in Beethoven’s Fifth Symphony? Do you have any musical training? How does this affect your listening (or not)? I’d love your answers, reactions, ideas—please comment!

2 Comments more...

Rest in peace, Tina Wuelfing Cargile

by on Sep.10, 2010, under facilitative leadership, localization, program management

My collaborator in “Point/Counterpoint” columns for Multilingual magazine, Tina Wuelfing Cargile, passed away last month after a long illness. Her time on this planet was too short, and my time with her was way too short, so even writing a decent bio is beyond me.

Her LinkedIn profile provides the basics. I’m going to attempt to fill in some of the color that’s missing from the business outlines. Those of you who knew her, just pour yourselves a glass of her favorite, pinot grigio, light a cigarette if you’re a smoker, and use a little imagination as you follow along.

Anyone who knew her would start their description of Tina with her sense of humor. Tina was always cracking a joke, often at her own expense. I can almost hear her explanation right now as my Mini Tina sits on my left shoulder (that’s where the evil angel goes, right?). “Well, of course, Erin—joking at my own expense, I’ve got lots of material!”

Somehow Tina’s adventures always became just a wee bit more absurd than anybody else’s, so her stories could keep us rolling for quite a while. This description of her introduction to country life, from McElroy Translation’s website during Tina’s years as their Business Development Manager, gives a taste of that:

Tina lives with her husband in a small town miles from Austin, where they are quickly filling their 12 acres with dozens of chickens, dogs, cats, geese, turkeys and ducks. She loves gardening, canning, quilting, playing with her “babies,” and listening to the frogs in their pond at night. They enjoy visits from their children and “adopt” their children’s friends, because their idea of family is whoever shows up. She also habitually and gleefully pokes fun at herself, and the description of her current hobbies and lifestyle brought to mind her introduction to country life.

Having spent 35 years in the city and on concrete, she proved totally oblivious to the “real world” on her first date, nearly 20 years ago, with her husband-to-be, a country boy from Honey Grove, Texas. While aware that the excursion involved scouting for arrowheads over fairly rough terrain, Tina:

  1. Wore stiletto heels and dressed in black from head to toe on a 100 degree day
  2. Identified a patch of prickly pear behind a ranch fence as a “cactus farm”
  3. Drank water from the river (who knew?)

He married her anyway.

Her humor wasn’t limited to self-deprecation, though. Recently while struggling with the illness that eventually took her from us, she posted to her friends on Facebook:

Day 7 in hospital: Stockholm Syndrome begins to set in. I’m no better or worse, really, just increasingly dependent upon my captors. Bizarre.

She frequently cracked me up with her quips about meetings that didn’t quite work out:

Reality is sure a lonely place when you can’t get anyone to join you there. What is it I saw on despair.com? At some point, hanging in there just makes you look like an even bigger loser. Worst cumulative score for a business trip EVER! I’ll spare you, which is more than I did for myself.

She grew up outside Washington, D.C., daughter of a test pilot who died in a jet crash in WV in 1957. Knowing that history makes me particularly enjoy this snapshot of Tina taken earlier this year.

We became collaborators in kind of a goofy way, and the better you know Tina, the more fitting that seems. We had both proposed talks at Translation World in Montréal about project management, so the conference organizers asked us to share a session.

That seems reasonable enough on the face of it, but as I read her proposal, I was shaking my head. I was a client-side program manager saying it was time to burn PMBOK (the bible from Project Management Institute, Project Management Book of Knowledge) and start over, and she was in vendor-side sales and saying people should read PMBOK. I thought, “Yeah, right—sharing a session is going to work really well!”

So I wrote to Tina and suggested that since we seemed to be in nearly complete disagreement, did she want to try a point/counterpoint format? I wasn’t sure what to make of her quick agreement, but within a few days we were on the phone trying to write an outline.

What a disaster!

We needed to give a 20 minute talk together, but after two hours on the phone we’d gotten spectacularly nowhere. Instead, we had accomplished the following:

  • We compared menageries—mine maxed out at three Siamese cats and two labrador retrievers, which you’d think would be competitive, but she topped that number in species, to say nothing of headcount.
  • We compared crazy career planning. I have degrees in music performance and have spent twenty-plus years in statistical software, but that was nothing on Tina’s degrees in English leading to a career in the recording industry, typesetting, running a higher ed journal, court reporting, and selling translation.
  • We compared notes on why we both think PowerPoint needs to be blasted off the face of the earth. And then she persuaded me to prepare slides anyway by promising to feature demotivational posters and that video with the cowboys herding cats.
  • We cracked each other up. Over and over again.

Later that week, we made another attempt. We didn’t get much further, but we did promise to send each other drafts, and after a few more email exchanges and seriously unproductive phone calls, we had our presentation. We’d also agreed to recruit Beatriz Bonnet of Syntes Language Group to be our moderator. Since neither of us were doing business with her and we both liked her outspoken style, we figured we could count on her to be neutral and keep us in check.

We finally met in Montréal—the night before we were to give our talk. Tina had arrived two days late thanks to airport closures in Texas. Beatriz had arrived on time, but as far as I know she has yet to be reunited with her luggage—so a few hours before Tina finally landed, Beatriz and I were tromping through the snowdrifts in downtown Montréal in our completely useless dress shoes looking for something for her to wear to our talk. When we got back to the hotel, we met Tina in the bar, where she was seated with a glass of—sing it with me, folks!—pinot grigio. We intended to go over logistics for our talk, but instead we spent several hours cracking each other up and deciding the talk would take care of itself.

It did, despite classic Tina circumstances: although we promised to show up early so we could (finally!) practice our talk together, she was actually late for her own presentation and we had to wing it.

She was late because she’d gotten stuck on the phone, rescuing a client from himself! I came to learn that she did a lot of that: talking clients down from the ledge, talking clients out of absurdly bad ideas, talking clients through technology that was too difficult for them to be buying, talking clients into sticking to plans instead of scrambling things up every few weeks because they didn’t understand the translation process, and so on.

Our talk was well received, and the audience’s questions and comments in the hallway afterward confirmed that they had gotten our point. It turned out that, although we thought we disagreed about the value of classical project management, we actually agreed about it but were looking at it from opposite perspectives. I was viewing it from the perspective of someone who had overdosed on methodology and discovered that facilitating a team’s teamwork was far more effective. She was viewing it from the perspective of someone who’d seen a lot of “project managers” whose training consisted of an endless supply of coffee and too much work. We were coming from opposite extremes—too much vs. not enough—but we met in the middle, recommending some basic tools used in moderation but in combination with compassion and listening skills.

High on the success of our high-wire act, we approached the editors of Multilingual with a proposal that we reprise the chaos in a series of point/counterpoint columns for the magazine. This gave us an excuse to continue getting together when our travels allowed—not that we needed one!

Generating column ideas was no problem. I still have our list, mostly unfinished. The problem was meeting deadlines! We were both overcommitted, so we repeatedly found ourselves trading drafts during the night before our deadlines and finally sending something a few hours after it was due. Editor Katie Botkin was patient with us, though. The results are available to subscribers on the Multilingual website, and they’re reprinted with Multilingual‘s permission on this blog:

I regret that Tina won’t be around to help me write the rest of our columns, because they won’t be nearly as sharp without her point of view. If you review the ones we did write, you’ll see that she was the funny one. She was also the wise one.

And the generous one. When Tina learned that I was leaving SAS after twelve years, she quickly persuaded Shelly Priebe of McElroy Translation to bring me in for interviews. None of us knew what I was interviewing for, exactly, but it was like Tina to work that way: thinking people should know each other and putting them together, and letting the details work themselves out.

Which they did. It was while talking for hours with Shelly in her lakeside guest cottage that I first began to envision Global Pragmatica and understand how my passion for facilitative leadership might fit into the localization industry. It was also Shelly who helped me see that localization was only part of the picture. It’s thanks to Tina that I now count Shelly and others from McElroy among my friends and valued colleagues—Shelly’s now an executive coach and she continues to be generous with her wisdom and encouragement.

I like how Shelly pictures Tina now:

Tina chose to slip away quietly, staying under the radar of our pity or our worry. With lucidity and acerbic wit to the end she passed to the kingdom that awaits her. Will she join the private Catholic school nuns of whom she spoke with such uproarious irreverence? That vision makes me smile. I will continue to smile whenever I think of my friend. Tina outwitted the demons who pursued her.

Tina went on to work with Beatriz at Syntes, and it was Beatriz who sent me the sad news when Tina died. Remembering Tina, Beatriz wrote,

Every time I think of Tina, her wisecracking and self-deprecating sense of humor are the first things that come to mind. She was sharp and witty, and just a lot of fun to be around. And she kept those traits to the end, using her humor to keep her spirits up as she fought the battle against her illness, in typical Tina style. In her last email to me, just a few days before she passed, she wrote, after a funny quote at her own expense and condition: ” ‘Scuse the dark humor. It’s too late to change now!”

Smart, generous, self-reliant, no-nonsense, witty, and just plain fun. That’s how I’ll always remember Tina.

Beatriz passed along a few more Tina gems, too; like the time Beatriz was planning a few days of hard ski-therapy, and Tina replied:

You will never get me on skis. My ankles are like toothpicks.

Or the time Tina emailed the entire office before a group event:

Please be sure to cut a wide parking swath for the confirmed pedestrian driving the really big company van.

Tina, you left us too soon. The world is a more boring place without you. Pinot grigio doesn’t taste right anymore.

Thank you for believing in all of us.

Please share your memories!

A note to Tina’s friends and family: I’d love it if you’d add your thoughts below in the comments. I know Tina would most appreciate your answers to this question: what’s the first time she made you laugh so hard it was embarrassing?

2 Comments : more...

Artwork

Global Pragmatica's artwork includes paintings by Zsuzsi Saper and digital photographs by Erin Vang. Further notes on specific pieces of art are given at the bottom of pages in which they appear. All artwork is copyright 2009–2010 by Global Pragmatica LLC®. All rights reserved worldwide.

© 2009-14 Global Pragmatica LLC®

All content © 2009-14 by Global Pragmatica LLC®. All rights reserved worldwide.

Global Pragmatica LLC® is a registered trademark of Global Pragmatica LLC. The ® symbol indicates USA trademark registration.

Contact Global Pragmatica LLC®

info@globalpragmatica.com
+1 415.997.9671
Oakland, CA 94611