I expected PsiElement.firstChild and PsiElement.children.first() to always return the same PsiElement, however, it turns out that (considering KtElements):
for KtBlockExpression they are not the same: firstChild returns a left curly brace and children.first() returns the first expression of the body.
for KtTypeReference they are the same thing.
So the questions is:
Why are they not equivalent in general?
When are the equivalent and when are they different (I am interested mainly in classes derived from KtElement)?
Related
I'm having an issue getting a query to work.
I'm essentially trying to write something like the following SQL, with the literal 5 replaced with a variable:
SELECT *
FROM "my_table"
WHERE 5 BETWEEN "my_table"."minimum" AND "my_table"."maximum"
This is what I have at the moment:
MyModel.where(
Arel::Nodes::Between.new(
my_variable, (MyModel.arel_table[:minimum]).and(MyModel.arel_table[:maximum])
)
)
Please ignore the way I am using arel_table, the actual query has multiple joins and is more complex, but this is the most minimum reproducible example I have to demonstrate the problem.
The error, as in the subject of the question is as follows:
undefined method `and' for #<Arel::Attributes::Attribute:0x00007f55e15514f8>
and method is for Arel::Nodes::Node i.e. MyModel.arel_attribute[:name].eq(Arel::Nodes::Quoted.new('engineersmnky')) This is an Arel::Nodes::Equality and you can chain with and.
That being said you can construct an Arel::Nodes::And for yourself via
Arel::Nodes::And.new([left,right])
Then we can pass this to the Between class like so
Arel::Nodes::Between.new(
Arel::Nodes::Quoted.new(my_variable),
Arel::Nodes::And.new([
MyModel.arel_table[:minimum],
MyModel.arel_table[:maximum]])
)
The Arel::Nodes::Quoted (also: Arel::Nodes.build_quoted(arg)) is not needed in your case since your my_variable is an Integer which can be visited and will be treated as an Arel::Nodes::SqlLiteral but I find it best to let arel decide how to handle the quoting in case your my_variable ends up being some other un-visitable Object
There are other ways to create a Between and other ways to create an And depending on what objects you are dealing with.
between is a Arel::Predication and these predications are available to Arel::Nodes::Attribute objects e.g.
MyModel.arel_table[:minimum].between([1,6])
and as mentioned is available to Arel::Nodes::Node and instances of this class provides a convenience method (create_and) for creating an And so we could do the following:
Arel::Nodes::Node.new.create_and([
MyModel.arel_table[:minimum],
MyModel.arel_table[:maximum]])
There are a number of other ways to hack this functionality together by using other Arel classes but this should get you headed in the right direction.
to is an infix function within the standard library. It can be used to create Pairs concisely:
0 to "hero"
in comparison with:
Pair(0, "hero")
Typically, it is used to initialize Maps concisely:
mapOf(0 to "hero", 1 to "one", 2 to "two")
However, there are other situations in which one needs to create a Pair. For instance:
"to be or not" to "be"
(0..10).map { it to it * it }
Is it acceptable, stylistically, to (ab)use to in this manner?
Just because some language features are provided does not mean they are better over certain things. A Pair can be used instead of to and vice versa. What becomes a real issue is that, does your code still remain simple, would it require some reader to read the previous story to understand the current one? In your last map example, it does not give a hint of what it's doing. Imagine someone reading { it to it * it}, they would be most likely confused. I would say this is an abuse.
to infix offer a nice syntactical sugar, IMHO it should be used in conjunction with a nicely named variable that tells the reader what this something to something is. For example:
val heroPair = Ironman to Spiderman //including a 'pair' in the variable name tells the story what 'to' is doing.
Or you could use scoping functions
(Ironman to Spiderman).let { heroPair -> }
I don't think there's an authoritative answer to this. The only examples in the Kotlin docs are for creating simple constant maps with mapOf(), but there's no hint that to shouldn't be used elsewhere.
So it'll come down to a matter of personal taste…
For me, I'd be happy to use it anywhere it represents a mapping of some kind, so in a map{…} expression would seem clear to me, just as much as in a mapOf(…) list. Though (as mentioned elsewhere) it's not often used in complex expressions, so I might use parentheses to keep the precedence clear, and/or simplify the expression so they're not needed.
Where it doesn't indicate a mapping, I'd be much more hesitant to use it. For example, if you have a method that returns two values, it'd probably be clearer to use an explicit Pair. (Though in that case, it'd be clearer still to define a simple data class for the return value.)
You asked for personal perspective so here is mine.
I found this syntax is a huge win for simple code, especial in reading code. Reading code with parenthesis, a lot of them, caused mental stress, imagine you have to review/read thousand lines of code a day ;(
I am using a MixHash to combine two Hashes with the Bag add (+) operator. This seems to work - but ... I am a bit surprised that the result of the union needs to be re-coerced back to a MixHash.
My guess is that the Bag add (+) infix operator coerces everything to a Bag first and returns the result as a Bag. This may be risky for me as some of my weights are negative (thus the Mix in the first place). Will this properly add negative weights?
Alternatively, is there a Mix add (+) operator?
my MixHash $dim-mix;
for ... {
my $add-mix = $!dims.MixHash;
$dim-mix = $dim-mix ?? ( $dim-mix (+) $add-mix ).MixHash !! $add-mix;
}
dd $dim-mix;
Now I look at this paraphrased code, perhaps there is some formulation of ternary ?? !! that can avoid spelling out $dim-mix in the test since already on the left?
Many thanks for any advice!
my $add-mix = (foo => 0.22, bar => -0.1).Mix;
my $dim-mix;
for ^5 {
$dim-mix (+)= $add-mix;
}
dd $dim-mix; # Mix $dim-mix = ("foo"=>1.1,"bar"=>-0.5).Mix
Obviously I've not used a MixHash, but you can sort that out if you need to after the loop.
(And of course you might be thinking "but isn't a Mix immutable?" It is -- but you have to distinguish variables and values. $dim-mix is a variable, a Scalar variable. Even if you type it -- my Mix $dim-mix; it's still a Scalar variable holding a Mix value. You can always assign to a Scalar.)
I'm starting to get a routine for questions like this where I don't know what's going on but I think I ought to be able to figure it out. Here was my process:
I got your code to run to see what it did. I tried to simplify the ternary. Hmm.
I turned to the doc. There was the doc page for (+). That called it "Baggy addition". That was worrisome given that a Bag only holds (positive) integers.
I turned to the source. I fired off a search of the rakudo sources for "Baggy addition". One result. I focused on the multi with (Mixy:D $a, QuantHash:D $b) signature. This showed me that the result should stay Mixy, i.e. the doc's implication it would or could go Baggy is a red herring.
I returned to the code and started wondering what I could do. When I initially tried to use (+)= to simplify the main assignment the compiler complained expected MixHash but got Mix. I tried a half dozen things that didn't work then just changed the MixHash constraint on $dim-mix to Mixy and it worked.
Then I thought through what was going on and realized that almost all the types were getting in the way of P6 just doing the right thing.
You can add some types back in if you really need them.
(But do you really need them? When types are absolutely necessary they're great. Otherwise, imo, think twice, and then twice again, before introducing them. They can easily make code harder to read, reason about, compose, and slower.)
(Of course there are occasions on which they're not strictly necessary but do really help overall. Imo, as with all things, keep it simple at first and only complexify if you see clear benefits for a particular line of code.)
I was trying to understand the following code:
def() ->commands
if(deferred_passive_abilities != [],
let [{ability: class passive_ability, creature: class creature}] items = [];
let found = false;
map(deferred_passive_abilities,
if(cmd = null, add(items, [value]), [cmd, set(found, true)])
where cmd = value.ability.static_effect(me, value.creature));
if(found,
set(deferred_passive_abilities, items);
evaluate_deferred_passive_abilities(),
set(deferred_passive_abilities, []))
)
Haskell appears to have both let and where, but I didn't learn much by a superficial reading of their haskell docs. They also have a let...in, which I didn't understand but it would be good to know if FFL has that.
So, what is the significance of using let versus where? Was it necessary to use let here? (Also, possibly another question: why does it need those semicolons?)
Using let introduces a variable that can be modified. Note how found and items are modified. By contrast, where always introduces immutable symbols.
Semi-colons are used in FFL to create a command pipeline. Normally in FFL, an entire formula is evaluated, resulting in a command or list of commands, and then the commands are executed.
When a semi-colon is present, everything before the semi-colon is treated as an entirely separate formula to everything after the semi-colon. The first formula is evaluated and executed and then the second formula is evaluated and executed.
Semi-colons effectively allow a much more procedural programming style in FFL, without semi-colons it is a purely functional language.
Never knew of let in FFL before this, must be very rare.
Regardless of the insights, the semicolon has to be absolutely necessary, in order to force execution before using the bound variable. In other words, until used the semicolon, the variable does not exist. Does not have a bound value.
This is a big difference to where, which doesn't need of semicolons.
Given the semicolon is not a construction for complete beginners, I could somewhat recommend beginners about variables to stick in where until understanding the trickery of the semicolons.
I saw this question and it got me wondering.
Ignoring the fact that pretty much all languages have to be backwards compatible, is there any reason we cannot use operators as both keywords and functions, depending on if it's immediately followed by a parenthesis? Would it make the grammar harder?
I'm thinking mostly of python, but also C-like languages.
Perl does something very similar to this, and the results are sometimes surprising. You'll find warnings about this in many Perl texts; for example, this one comes from the standard distributed Perl documentation (man perlfunc):
Any function in the list below may be used either with or without parentheses around its arguments. (The syntax descriptions omit the parentheses.) If you use parentheses, the simple but occasionally surprising rule is this: It looks like a function, therefore it is a function, and precedence doesn't matter. Otherwise it's a list operator or unary operator, and precedence does matter. Whitespace between the function and left parenthesis doesn't count, so sometimes you need to be careful:
print 1+2+4; # Prints 7.
print(1+2) + 4; # Prints 3.
print (1+2)+4; # Also prints 3!
print +(1+2)+4; # Prints 7.
print ((1+2)+4); # Prints 7.
An even more surprising case, which often bites newcomers:
print
(a % 7 == 0 || a % 7 == 1) ? "good" : "bad";
will print 0 or 1.
In short, it depends on your theory of parsing. Many people believe that parsing should be precise and predictable, even when that results in surprising parses (as in the Python example in the linked question, or even more famously, C++'s most vexing parse). Others lean towards Perl's "Do What I Mean" philosophy, even though the result -- as above -- is sometimes rather different from what the programmer actually meant.
C, C++ and Python all tend towards the "precise and predictable" philosophy, and they are unlikely to change now.
Depending on the language, not() is not defined. If not() is not defined in some language, you can not use it. Why not() is not defined in some language? Because creator of that language probably had not need this type of language construction. Because it is better to let things be simpler.