In Flex 3,
var anInstance : MyClass = new MyClass();
with (anInstance)
{
property1 = "fred";
property2 = 5;
propert3 = 7;
}
does NOT flag "propert" as a non-existent property name. I thought this was a remainder of the evil JavaScript object behavior (referring to a property name of an object implicitly creates it), but it seems to be a side-effect of the "with".
This makes me very sad as the "with" was a little reminder of Delphi (except it works correctly there).
Am I missing something here?
From reading the documentation:
Actionscript apparently bubbles out for scope resolution on embedded variables (not surprising, since the syntax doesn't require an explicit dereference symbol like "." or "->" to indicate which variable names should be "withed".) So you effectively are creating a variable at global scope named propert3.
EDIT after thinking about why this "problem" exists -
Javascript is the epitome of non-strict typing. And Actionscript, being a strict superset of Javascript, can't enforce strict typing except as declared by its own extensions to the language - which means it must support untyped variables.
Some classes are dynamic (e.g. movieclip) and can have properties added to them at runtime:
http://flexblog.faratasystems.com/?p=95
Related
A property that is bound to an expression is updated when something in the expression changes.
This is called a dependency.
EDIT:
To clarify:
I'm interested in details on how Qt determines a list of dependencies
Dependencies on simple bindings such as x: y are more or less obvious
The question is about less obvious cases such as x: myItemId["y"] and x: myFunction(z) where myFunction(p) { if (p) return myItemId.y }
Sometimes QML engine is able to detect change even if the expression is a function call without arguments, other times it cannot do that (for example mapToItem(item,0,0).x).
Another example of imperfection is that setting JS array item value without reassigning the array itself doesn't normally produce onXxxxxChanged signal or update anything referring to that array value.
An expression with unused result (x: {myForcedDependency; return myActualCalculation()}) is sometimes suggested to force a dependency.
According to this KDAB article and Qt source code, a binding expression is not only evaluated but any properties "accessed" during that are "captured" in something called a "guard", then every guard properties onXxxxxChanged() signals are connected, but actual details of this process are unclear.
So my questions are:
Are there any defined rules of dependency resolution?
How does it really work?
How deeply does QQmlEngine/V8 scan "accesses" into functions called by the binding expression and what may prevent it from doing that?
Is dependency-detection only based on the first attempt at property resolution?
Are all possible code paths checked even if execution never reached there yet?
Are non-trivial accesses determined in those cases, such as object["property"] syntax?
What if some unexecuted code is (currently) erroneous (and does not produce an error but cannot be properly analyzed)?
How can the dependency resolution process be influenced?
Is there a way to avoid or block a dependency?
As far as I understand an intermediate "filter" property that only actually changes its value when it's necessary to update is the intended way, correct?
Is there an intended way to force a dependency?
Is manually emitting "XxxxxChanged" signal the correct/supported way to force an update?
Is adding an unused reference a legal/intended way to do it or undefined behavior based on the current implementation quirk?
Any information would be useful, although I did read the official documentation on QML properties, QML bindings and JavaScript expressions and didn't find any concrete explanation - if you refer to the official documentation please quote relevant parts.
Please note that I'm not asking you to test if any of this works on your system, but if it's supposed to work - if it can be relied on
It makes more sense if you just think of bindings as connected signals. If you have something like this:
property int x: y
It's just like doing this in C++:
connect(this, &SomeClass::yChanged, [this]() { x = y; });
The same goes for expressions:
property int x: y + z
would be equivalent to:
connect(this, &SomeClass::yChanged, [this]() { x = y + z; });
connect(this, &SomeClass::zChanged, [this]() { x = y + z; });
And the same with function calls:
property int x: someFunc()
function someFunc() {
return y;
}
The only time bindings don't update is when there is no onChanged signal to connect to, or the onChanged signal doesn't get emitted for whatever reason.
property int x: cppObject.invokable()
In the above case, the only property that x is able to connect to is cppObject. If invokable references other properties, those won't be connected to x and therefore the binding won't update.
property var array: [1, 2, 3]
property int x: array[0]
function updateArray() {
array = [2, 4, 6]
arrayChanged() // Manually call the onChanged signal to update `x`
}
var properties do not notify by default (for some reason). So in this case, we have to manually call the changed signal, but then the binding will still work.
For a property var, onChanged is emitted only when there is a direct assignment to the var itself, not to a property of some object it refers to. This also excludes modification of array contents, as JS arrays are JS objects.
This is consistent with QML being a JS extension. In JS you can modify prop in this code, because const only means variable will always refer to the same object:
const variable = { prop: 'value' };
Just like only direct assignments to const variables are regarded as change attempts when JS enforces const, QML only emits onChanged on direct assignments to a property var.
Coming from C++, I like to compare JS variables with object value to pointers:
SomeClass *variable = new SomeClass();
SomeClass *const variable = new SomeClass(); //const pointer to mutable object
Again, a change in the referred object is not regarded as a change in the variable.
With the following snippet I cannot retrieve gString from a map:
def contents = "contents"
def gString = "$contents"
def map = [(gString): true]
assert map.size() == 1 // Passes
assert gString.hashCode() == map.keySet().first().hashCode() // Passes, same hash code
assert map[gString] // Fails
How on earth is that possible?
Assertion message clearly shows that there's something seriously wrong with Groovy:
assert map[gString] // Fails
| ||
| |contents
| null
[contents:true]
It's not the same question as Why groovy does not see some values in dictionary?
First answer there suggests:
You're adding GString instances as keys in your map, then searching for them using String instances.
In this question I clearly add GString and try to retrieve GString.
Also neither Why are there different behaviors for the ways of addressing GString keys in maps? nor Groovy different results on using equals() and == on a GStringImpl have an answer for me. I do not mutate anything and I do not mix String with GString.
tl;dr: You seem to have discovered a bug in Groovy's runtime argument overloading evaluation.
Answer:
map[gString] is evaluated as map.getAt(gString) at runtime straightforwardly via Groovy's operator overloading mechanism. So far, so good, but now is where everything starts to go awry. The Java LinkedHashMap class does not have a getAt method anywhere in it's type hierarchy, so Groovy must use dynamically associated mixin methods instead (Actually that statement is sort of reversed. Groovy uses mixin methods before using the declared methods in the class hierarchy.)
So, to make a long story short, Groovy resolves map.getAt(gString) to use the category method DefaultGroovyMethods.getAt(). Easy-peasy, right? Except that this method has a large number of different argument overloads, several of which might apply, especially when you take Groovy's default argument coercion into account.
Unfortunately, instead of choosing DefaultGroovyMethods.getAt(Map<K,V>,K), which would seem to be a perfect match, Groovy chooses DefaultGroovyMethods.getAt(Object,String), which coerces the GString key argument into a String. Since the actual key is in fact a GString, the method ultimately fails to find the value.
To me the real killer is that if the argument overload resolution is performed directly from code (instead of after the operator resolution and the category method selection), then Groovy makes the right overload choice! That is to say, if you replace this expression:
map[gString]
with this expression:
DefaultGroovyMethods.getAt(map,gString)
then the argument overloading is resolved correctly, and the correct value is found and returned.
There's nothing wrong with Groovy. A GString is not a String. It is mutable and as such should never be used as a key in a map (like any other mutable object in Java).
Learn more about this in the docs: http://docs.groovy-lang.org/latest/html/documentation/index.html#_gstring_and_string_hashcodes
Can someone tell me the exact difference between node() and element() types in XQuery? The documentation states that element() is an element node, while node() is any node, so if I understand it correctly element() is a subset of node().
The thing is I have an XQuery function like this:
declare function local:myFunction($arg1 as element()) as element() {
let $value := data($arg1/subelement)
etc...
};
Now I want to call the function with a parameter which is obtained by another function, say functionX (which I have no control over):
let $parameter := someNamespace:functionX()
return local:myFunction($parameter)
The problem is, functionX returns an node() so it will not let me pass the $parameter directly. I tried changing the type of my function to take a node() instead of an element(), but then I can’t seem to read any data from it. $value is just empty.
Is there some way of either converting the node to an element or should am I just missing something?
EDIT: As far as I can tell the problem is in the part where I try to get the subelement using $arg1/subelement. Apparently you can do this if $arg1 is an element() but not if it is a node().
UPDATE: I have tested the example provided by Dimitre below, and it indeed works fine, both with Saxon and with eXist DB (which is what I am using as the XQuery engine). The problem actually occurs with the request:get-data() function from eXist DB. This function gets data provided by the POST request when using eXist through REST, parses it as XML and returns it as a node(). But for some reason when I pass the data to another function XQuery doesn’t acknowledge it as being a valid element(), even though it is. If I extract it manually (i.e. copy the output and paste it to my source code), assign it to a variable and pass it to my function all goes well. But if I pass it directly it gives me a runtime error (and indeed fails the instance of test).
I need to be able to either make it ignore this type-check or “typecast” the data to an element().
data() returning empty for an element just because the argument type is node() sounds like a bug to me. What XQuery processor are you using?
It sounds like you need to placate static type checking, which you can do using a treat as expression. I don't believe a dynamic test using instance of will suffice.
Try this:
let $parameter := someNamespace:functionX() treat as element()
return local:myFunction($parameter)
Quoting from the 4th edition of Michael Kay's magnum opus, "The treat as operator is essentially telling the system that you know what the runtime type is going to be, and you want any checking to be deferred until runtime, because you're confident that your code is correct." (p. 679)
UPDATE: I think the above is actually wrong, since treat as is just an assertion. It doesn't change the type annotation node(), which means it's also a wrong assertion and doesn't help you. Hmmm... What I really want is cast as, but that only works for atomic types. I guess I'm stumped. Maybe you should change XQuery engines. :-) I'll report back if I think of something else. Also, I'm curious to find out if Dimitre's solution works for you.
UPDATE #2: I had backpedaled here earlier. Can I backpedal again? ;-) Now my theory is that treat as will work based on the fact that node() is interpreted as a union of the various specific node type annotations, and not as a run-time type annotation itself (see the "Note" in the "Item types" section of the XQuery formal semantics.) At run time, the type annotation will be element(). Use treat as to guarantee to the type checker that this will be true. Now I wait on bated breath: does it work for you?
EXPLANATORY ADDENDUM: Assuming this works, here's why. node() is a union type. Actual items at run time are never annotated with node(). "An item type is either an atomic type, an element type, an attribute type, a document node type, a text node type, a comment node type, or a processing instruction type."1 Notice that node() is not in that list. Thus, your XQuery engine isn't complaining that an item has type node(); rather it's complaining that it doesn't know what the type is going to be (node() means it could end up being attribute(), element(), text(), comment(), processing-instruction(), or document-node()). Why does it have to know? Because you're telling it elsewhere that it's an element (in your function's signature). It's not enough to narrow it down to one of the above six possibilities. Static type checking means that you have to guarantee—at compile time—that the types will match up (element with element, in this case). treat as is used to narrow down the static type from a general type (node()) to a more specific type (element()). It doesn't change the dynamic type. cast as, on the other hand, is used to convert an item from one type to another, changing both the static and dynamic types (e.g., xs:string to xs:boolean). It makes sense that cast as can only be used with atomic values (and not nodes), because what would it mean to convert an attribute to an element (etc.)? And there's no such thing as converting a node() item to an element() item, because there's no such thing as a node() item. node() only exists as a static union type. Moral of the story? Avoid XQuery processors that use static type checking. (Sorry for the snarky conclusion; I feel I've earned the right. :-) )
NEW ANSWER BASED ON UPDATED INFORMATION: It sounds like static type checking is a red herring (a big fat one). I believe you are in fact not dealing with an element but a document node, which is the invisible root node that contains the top-level element (document element) in the XPath data model representation of a well-formed XML document.
The tree is thus modeled like this:
[document-node]
|
<docElement>
|
<subelement>
and not like this:
<docElement>
|
<subelement>
I had assumed you were passing the <docElement> node. But if I'm right, you were actually passing the document node (its parent). Since the document node is invisible, its serialization (what you copied and pasted) is indistinguishable from an element node, and the distinction was lost when you pasted what is now interpreted as a bare element constructor in your XQuery. (To construct a document node in XQuery, you have to wrap the element constructor with document{ ... }.)
The instance of test fails because the node is not an element but a document-node. (It's not a node() per se, because there's no such thing; see explanation above.)
Also, this would explain why data() returns empty when you tried to get the <subelement> child of the document node (after relaxing the function argument type to node()). The first tree representation above shows that <subelement> is not a child of the document node; thus it returns the empty sequence.
Now for the solution. Before passing the (document node) parameter, get its element child (the document element), by appending /* (or /element() which is equivalent) like this:
let $parameter := someNamespace:functionX()/*
return local:myFunction($parameter)
Alternatively, let your function take a document node and update the argument you pass to data():
declare function local:myFunction($arg1 as document-node()) as element() {
let $value := data($arg1/*/subelement)
etc...
};
Finally, it looks like the description of eXist's request:get-data() function is perfectly consistent with this explanation. It says: "If its not a binary document, we attempt to parse it as XML and return a document-node()." (emphasis added)
Thanks for the adventure. This turned out to be a common XPath gotcha (awareness of document nodes), but I learned a few things from our detour into static type checking.
This works perfectly using Saxon 9.3:
declare namespace my = "my:my";
declare namespace their = "their:their";
declare function my:fun($arg1 as element()) as element()
{
$arg1/a
};
declare function their:fun2($arg1 as node()) as node()
{
$arg1
};
my:fun(their:fun2(/*) )
when the code above is applied on the following XML document:
<t>
<a/>
</t>
the correct result is produced with no error messages:
<a/>
Update:
The following should work even with the most punctuential static type-checking XQuery implementation:
declare namespace my = "my:my";
declare namespace their = "their:their";
declare function my:fun($arg1 as element()) as element()
{
$arg1/a
};
declare function their:fun2($arg1 as node()) as node()
{
$arg1
};
let $vRes := their:fun2(/*)
(: this prevents our code from runtime crash :)
return if($vRes instance of element())
then
(: and this assures the static type-checker
that the type is element() :)
my:fun(their:fun2(/*) treat as element())
else()
node() is an element, attribute, processing instruction, text node, etc.
But data() converts the result to a string, which isn't any of those; it's a primitive type.
You might want to try item(), which should match either.
See 2.5.4.2 Matching an ItemType and an Item in the W3C XQuery spec.
Although it's not shown in your example code, I assume you are actually returning a value (like the $value you are working with) from the local:myFunction.
I want to set arrayCollection #2 = to arrayCollection #1 via a function in flex 3. I pass both array collections to a function and set arrayCollection #2 = arrayCollection #1. However, it seems to not be passing arrayCollection #2 by reference because after the function call, arrayCollection #2 has not been changed. My understanding is that it should be passed by reference and work, am I doing something wrong? Below is the code:
var AC1:ArrayCollection = new ArrayCollection;
var AC1.addItem(someObject);
var AC2:ArrayCollection = new ArrayCollection;
setAC2(AC1,AC2);
// AC2 is not set to AC1 after the function
private function setAC2(_ac1:ArrayCollection, _ac2:ArrayCollection):void
{
_ac2 = _ac1;
}
Please see Evaluation Strategy.
AS uses "pass by object" / "pass by object-sharing". That is, the "object" is passed (not a copy, clone or duplicate) and any modifications to the object are shared.
However, the assignment _ac2 = _ac1 only changes the value of the [function's local] parameter variable and will have no affect on any variables during the function invocation. The only thing passed in are the values ("objects") which result from the evaluation of the variables (or any arbitrary expression) used in the function invocation.
This is because, as stated above, the strategy used is "pass by object" and not (as the documentation states "pass by reference", which really means, "pass by value [of the reference]" or just ... "pass by object"). That is, the term "pass by reference" is actually misused and hence, confusing. (It is misused in a number of languages and documentation. It is an uphill battle trying to getting to a common meaning.)
If it were really "pass by reference" then assigning a new value to _ac2 would propagate out. (Before posing a comment saying how AS is "pass by reference", please see the link at top and consider that "pass by reference" covers the case of C#'s out/ref, VB's ByRef, TSQL's output and C++'s (reference) & -- these notions are not in AS, Javascript, or Java). However, as correctly noted in the original post (and additional self-reply), it is not the case -- conclusion: AS does not support "pass by reference"; furthermore, the documentation (confusingly) uses the term "pass by reference" to mean "pass by object" / "pass by object-sharing".
There are several ways that the change can be propagate out, ordered by order of (my) preference:
Return the new applicable value: AC2 = doSomeTransformation(AC1). This generally the cleanest. Avoid side-effects and surprising code. Multiple values can be returned if wrapped in an object (or array) as appropriate.
Use a closure: doSomeTranformation(AC1, function (newValue) { AC2 = newValue }) where doSomeTransformation might look like: function doSomeTransformation(_ac1, finished) { ...; finished(_ac1) }. I generally only use this when the callback "runs in context" of the function itself or when writing code in a CPS-style.
Mutate an object (AS is "pass by object", after all). This is very icky, but it will work.
var blah = {AC2: null}; doSomeTransformation(ac1, blah); ...; laterOn(blah.AC2) where doSomeTransformation might look look like function doSomeTransformation(_ac1, b) { ...; b.AC2 = _ac1; }. Not recommended in general.
Happy coding.
Applicable excerpts, from Evaluation Strategy:
"call by reference": (my main argument for "call by reference" being used incorrectly is that it already has a well-defined meaning; the overloaded term adopted by some languages such as AS and Python just adds confusion)
In call-by-reference evaluation (also referred to as pass-by-reference), a function receives an implicit reference to a variable used as argument, rather than a copy of its value. This typically means that the function can modify the variable used as argument- something that will be seen by its caller.
"call by object" / "call by object-sharing": (but pay heed to where it acknowledges the inconsistency/localization of these terms; the term "call by reference" is often misused to imply these semantics and "call by value [of the reference]" is used in some contexts to also mean the same thing)
The semantics of call-by-sharing differ from call-by-reference in that assignments to function arguments within the function aren't visible to the caller (unlike by-reference semantics), so e.g. if a variable was passed, it is not possible to simulate an assignment on that variable in the caller's scope. However since the function has access to the same object as the caller (no copy is made), mutations to those objects, if the objects are mutable, within the function are visible to the caller, which may appear to differ from call-by-value semantics.
THe function arguments in ActionScript pass by value, not by reference. It is absolutely the same as in Java. You can read in details here.
The problem I am seeing is
var AC1.addItem(someObject);
Try adding the item within a function.
var AC1:ArrayCollection = new ArrayCollection;
var AC2:ArrayCollection = new ArrayCollection;
addItemToArrayCollection( AC1 );
setAC2(AC1,AC2);
// AC2 should be pointing to the ArrayCollection that AC1 is pointing to.
private function setAC2(_ac1:ArrayCollection, _ac2:ArrayCollection):void
{
_ac2 = _ac1;
}
private function addItemToArrayCollection( arrayCollection:ArrayCollection ):void
{
arrayCollection.addItem( someObject );
}
You can add a breakpoint in after the the assignment and see that AC2 should have the same Object as AC1.
I believe that #Constantiner is on the right track, but I think his explanation is lacking detail; so I'm going to try to explain with a bit more depth; as best I understand it. Ya'll can correct me if I'm wrong.
As stated in the docs:
In ActionScript 3.0, all arguments are
passed by reference, because all
values are stored as objects. However,
objects that belong to the primitive
data types, which includes Boolean,
Number, int, uint, and String, have
special operators that make them
behave as if they were passed by
value.
So, an ArrayCollection is definitely an object, and not a primitive type, so it should be passed by reference and act like it was passed by reference. But, what is your reference variable to the ArrayCollection. Conceptually it just a pointer to some memory space that contains the actual Collection data. Here is my attempt at some ASCII art:
|---|
ac1(variable)--> | | (ActualArrayCollection1)
|---|
|---|
ac2(variable)--> | | (ActualArrayCollection2)
|---|
to repeat, ac1variable is a pointer to some memory space. ac2variable is a pointer to some different memory space. When you pass one of them into a method as an argument it is passed by reference. So, inside the method, you have something like this:
ac1(variable)--> |---|
ac1(argument)--> | | (ActualArrayCollection1)
|---|
ac2(variable)--> |---|
ac2(argument)--> | | (ActualArrayCollection2)
|---|
So both ac1variable and ac1argument point at the same memory space; because they each contain the same pointer value. However, ac1variable and ac1argument are actually holding different memory spaces. They are not the same.
When the method runs this line:
_ac2 = _ac1;
You get something like this:
ac1(variable)--> |---|
ac1(argument)--> | | (ActualArrayCollection1)
ac2(argument)--> |---|
ac2(variable)--> |---|
| | (ActualArrayCollection2)
|---|
When the method's execution ends, the two arguments go away, and the original pointer variables remain unchanged. If you want to do a direct assignment like this inside a method, you can access the global variable using the this keyword. This should do it:
this._ac2 = _ac1;
Of course, that can defeat the purpose of encapsulation inside a method if you're accessing class level variables.
I'm sure an expert on compiler design and such things will eat this for breakfast and spit it up. I hope my ASCII art is consistent across multiple browsers / machines / OSes / etc..
As the title suggests, is there any documentation on the FlexBuilder "expressions" tab, and what expressions it can accept?
As far as I can tell, it can show the value of variables, but that's it: comparisons, function and method calls all fail:
alt text http://img.skitch.com/20100614-t1gpdbrn1qnwy2eqr3gnw54d1d.png
Edit: this is specific to FB3 — Flex Builder. Apparently FB4 — Flash Builder — is slightly less incompetent.
It depends if you're using FlexBuilder 3 or FlashBuilder 4. FB 3 has pitiful expressions capability. You can basically access variables and their member properties, period. And not always all the member properties.
FB 4 gives you the ability to evaluate real expressions, like
getStyle("vertical-align")
getStyle("vertical-align") + "foo"
parseInt(getStyle("padding-left"))
etc.
It shows the values of variables, and the results of expressions as at the current breakpoint in the debugger.
eg:
public function testMethod():void {
var a:String; // <-- Set breakpoint here
a = "Hello";
a = "World";
}
public function testMethodB():void {
var b:String = "Another String";
}
You can create an expression for a and watch the value change over time.
However, the variable must have a value within the context. For example, setting the breakpoint where indicated, and defining an expresssion for b would show an error.