According to Rascal's documentation, the "?" operator can be used to query if a variable is "defined".
For example:
int u=1;
int v; // Defined but uninitialised
u = v?2;
v is uninitialised and therefore u will get the value 2.
However, doing this flags a "Warning: deprecated feature: run-time check on variable initialisation"
Hence the question, what is the non-deprecated way to do what the ? operator did in Rascal?
You can check with the IsDefined operator only things that can in principle be "undefined". Variables are not in that class; they were accidentally and now we are deprecating that behavior. In principle, there exists no null or undefined value in Rascal.
Having said that there are situations with maps and keyword fields of nodes and algebraic constructors where it is possible that a declared name does not exist at runtime. So:
myMap[myKey]?def; // a map does not have to have the key
myCons.myKeywordField?def ; // a keyword field does not have to be set
The isDefined operator is part of the assignment syntax on the left-hand side, as explained here: https://www.rascal-mpl.org/docs/Rascal/Statements/Assignment/IsDefined/
Also, the same syntax can be used as an expression: https://www.rascal-mpl.org/docs/Rascal/Expressions/Values/Boolean/IfDefinedElse/
Again, checking variables for undefinedness does not make sense since variables are always defined in Rascal. It is a static error otherwise. Defined but uninitialized variables are for making matching patterns look more elegant:
int i; int j; // here they are declared with a type
// here they are not defined and may not be used
if (<i, j> := <1,2>) { // here they are bound/defined
// here they can be used
}
// here i and j are not defined again and may not be used
Related
Why does Dart allow this:
void main() {
var A;
A ??= 12;
print(A);
}
The output is 12. But not this:
void main() {
int A;
A ??= 12;
print(A);
}
Here's the error:
lib/project_dart.dart:4:2: Warning: Operand of null-aware operation '??=' has type 'int' which excludes null.
A??= 12;
^
lib/project_dart.dart:4:2: Error: Non-nullable variable 'A' must be assigned before it can be used.
A??= 12;
^
lib/project_dart.dart:5:8: Error: Non-nullable variable 'A' must be assigned before it can be used.
print(A);
^
In this case I have to add the ? after int so it can work but in the previous case it works fine without it the question is WHY?
var tells the compiler "I want you to figure out what type this variable should be" based on the assignment. In this case, there is no assignment to the variable upon declaration, so there is no information for the compiler to use to infer the type. Thus, according to the compiler, the type of A should be dynamic, which could be literally anything, so it's given the default value of null.
int explicitly tells the compiler "I want this variable to be a non-nullable int". Non-nullable variables cannot be given a default value when they are declared without a value, so they must be assigned before they are referenced for the first time, which means you can't do print(A) before A has been definitely given a value.
A ??= 5; is less obvious why it's failing, but think about what it's doing. It checks if A is null, and if it isn't, it assigns it the value of 5. In order to check A, the program needs to reference A, and as we know, A can't be referenced because it's not nullable and hasn't been definitely assigned a value. Additionally, A ??= 5 is redundant because A is non-nullable and therefore can never be null.
Note that a declared variable that hasn't been assigned a value is not going to implicitly contain null. There is no check for whether a variable has been assigned yet, which is why you should be careful when declaring non-nullable variables without immediately assigning them a value. It's easy to find yourself in a race condition where code that will assign the variable may or may not occur before other code that will reference the variable.
I understand that, in Dart, primitives are passed by value and Objects are passed by reference.
So, I expected that
void test(String phrase) {
const _phrase = phrase;
}
would result in error, but
void test(int amount) {
const _amount = amount;
}
wouldn't.
However, both of them throws the same compile-time error: Const variables must be initialized with a constant value.
Is this some not implemented feature or there's a reason behind not accepting function arguments in const variables initialization?
Dart constant variables must be initialized with compile-time constant expressions.
A compile-time constant expression must always have the same value—precisely one value per source location.
Dart doesn't have "constant values" as such. It has constant expressions, which are known to evaluate to precisely one value, and for which it's possible to know this value at compile-time. That allows the compiler to canonicalize those constants values, so different constant expressions evaluating to constant objects with the same state are canonicalized to be the same object.
Your amount variable is not a compile-time constant expression. It can have different values at different times (because it's a function parameter and people might call the function with different arguments), so it cannot be a constant expression.
And therefore it cannot be used to initialize a constant variable, because constant variables can only have one value.
void test(int amount) {
const _amount = amount;
const list = [_amount]; // <- MUST ALWAYS HAVE SAME VALUE
}
In short: Dart constant variables must be initialized with a compile-time constant expression. A constant expression must always have the same value. This is the fundamental rule about Dart constant expressions which most other restrictions are derived from. (For example, a constant variable being used is a constant expression, so it must always be bound to the same value, which is why it must be initialized with a constant expression.)
I believe the answer is that constants are fixed at compile time rather than run time so function arguments cannot be passed to them as the function only gets executed at runtime.
See also here for example https://stackoverflow.com/a/58877374
A final modifier instead of const would work however.
By the way a String is also a primitive I think. See here https://stackoverflow.com/a/58568542
I'm new to Dart. The documentation say: "To test whether two objects x and y represent the same thing, use the == operator. (In the rare case where you need to know whether two objects are the exact same object, use the identical() function instead.)"
So, if type this code:
var foo = 'bar';
var baz = 'bar';
print(identical(foo, baz));
If i've well understood, foo and bar do not reference the same object. So identical() must return false, isn't it ?
But it's not the case, at least in DartPad.
Where is the problem.
In this case foo and bar do reference the same object.
That is because the compiler canonicalizes string literals.
The specification requires most constants to be canonicalized. If you create const Duration(seconds: 1) in two places, it will become the same object. Integers, doubles and booleans are always canonicalized, whether constant or not (the language pretends that there is only one instance per value).
Strings are special in that the specification is not entirely clear on whether they need to be canonicalized or not, but constant strings need to be canonicalized for constants to make sense, and all the compilers do that.
A literal is a constant expression, so string literals are always canonicalized. That means that 'bar' denotes the same object no matter where it occurs in your code.
For several built-in "literals", you will always get identical true for equal values.
bool
String
int
double (I think)
Consider below struct:
typedef struct _Index {
NSInteger category;
NSInteger item;
} Index;
If I use this struct as a property:
#property (nonatomic, assign) Index aIndex;
When I access it without any initialization right after a view controller alloc init, LLDB print it as:
(lldb) po vc.aIndex
(category = 0, item = 0)
(lldb) po &_aIndex
0x000000014e2bcf70
I am a little confused, the struct already has valid memory address, even before I want to allocate one. Does Objective-C initialize struct automatically? If it is a NSObject, I have to do alloc init to get a valid object, but for C struct, I get a valid struct even before I tried to initialize it.
Could somebody explains, and is it ok like this, not manually initializing it?
To answer the subquestion, why you cannot assign to a structure component returned from a getter:
(As a motivation this is, because I have read this Q several times.)
A. This has nothing to do with Cbjective-C. It is a behavior stated in the C standard. You can check it for simple C code:
NSMakeSize( 1.0, 2.0 ).width = 3.0; // Error
B. No, it is not an improvement of the compiler. If it would be so, a warning would be the result, not an error. A compiler developer does not have the liberty to decide what an error is. (There are some cases, in which they have the liberty, but this are explicitly mentioned.)
C. The reason for this error is quite easy:
An assignment to the expression
NSMakeSize( 1.0, 2.0 ).width
would be legal, if that expression is a l-value. A . operator's result is an l-value, if the structure is an l-value:
A postfix expression followed by the . operator and an identifier designates a member of a structure or union object. The value is that of the named member,82) and is an lvalue if the first expression is an lvalue.
ISO/IEC 9899:TC3, 6.5.2.3
Therefore it would be assignable, if the expression
NSMakeSize( 1.0, 2.0 )
is an l-value. It is not. The reason is a little bit more complex. To understand that you have to know the links between ., -> and &:
In contrast to ., -> always is an l-value.
A postfix expression followed by the -> operator and an identifier designates a member of a structure or union object. The value is that of the named member of the object to which the first expression points, and is an lvalue. 83)
Therefore - that is what footnote 83 explains – ->, &, and . has a link:
If you can calculate the address of a structure S having a component C with the & operator, the expression (&S)->C is equivalent to S.C. This requires that you can calculate the address of S. But you can never do that with a return value, even it is a simple integer …
int f(void)
{
return 1;
}
f()=5; // Error
… or a pointer …
int *f(void)
{
return NULL;
}
f()=NULL; // Error
You always get the same error: It is not assignable. Because it is a r-value. This is obvious, because it is not clear,
a) whether the way the compiler returns a value, esp. whether he does it in address space.
b) when the time the life time of the returned value is over
Going back to the structure that means that the return value is a r-value. Therefore the result of the . operator on that is a r-value. You are not allowed to assign a value to a r-value.
D. The solution
There is a solution to assign to a "returned structure". One might decide, whether it is good or not. Since -> always is an l-value, you can return a pointer to the structure. Dereferencing this pointer with the -> operator has always an l-value as result, so you can assign a value to it:
// obj.aIndex returns a pointer
obj.aIndex->category = 1;
You do not need #public for that. (What really is a bad idea.)
The semantics of the property are to copy the struct, so it doesn't need to be allocated and initialized like an Objective-C object would. It's given its own space like a primitive type is.
You will need to be careful updating it, as this won't work:
obj.aIndex.category = 1;
Instead you will need to do this:
Index index = obj.aIndex;
index.category = 1;
obj.aIndex = index;
This is because the property getter will return a copy of the struct and not a reference to it (the first snippet is like the second snippet, without the last line that assigns the copy back to the object).
So you might be better off making it a first class object, depending on how it will be used.
Have this function defined in your module:
module Data
int inc(x) = x + 1;
Type this in the console:
rascal> import Data;
rascal> import List;
This works:
rascal> inc(1);
int: 2
But this does not:
rascal> list[int] y = [1,2,3];
rascal> mapper(y, inc);
|rascal://<path>|: insert into collection not supported on value and int
☞ Advice
But it works if inc(...)'s parameter type is declared:
int inc(int x) = x + 1;
So why does not having this type declaration work for using the inc(...) function directly, but not for passing that function to mapper(...)?
Because Rascal's type checker is still under development, you are not warned if you make a small mistake like forgetting to provide a type for a function parameter. It may still work, accidentally, in some circumstances but you are guaranteed to run into trouble somewhere as you've observed. The reason is that type inference for function parameters is simply not implemented as a feature. This is a language design decision with the intent of keeping error messages understandable.
So, this is not allowed:
int f(a) = a + 1;
And, it should be written like this:
int f(int a) = a + 1;
I consider it a bug that the interpreter doesn't complain about an untyped parameter. It is caused by the fact that we reuse the pattern matching code for both function parameters and inline patterns. [edit: issue has been registered at https://github.com/cwi-swat/rascal/issues/763]
In your case the example works because dynamically the type of the value is int and addition does not check the parameter types. The broken example breaks because the interpreter does checks the type of the function parameter at the call-site (which defaulted to value for the untyped parameter).