synthesizing long parameter strings - json

When consuming a JSON string, the parameters can be deeply nested, making reading/checking tedious:
update(capture_created: params[:data][:object][:created], capture_currency: params[:data][:object][:currency]
...[...] and so on...
In what way can a node params[:data][:object] be represented only once and be thus able to handle the child values as a parameter?

There are a few things you can.
You could grab the inner hash in a local variable as dbugger mentioned:
p = params[:data][:object]
update(capture_created: p[:created], capture_currency: p[:currency], ...)
Or you could use #tap or #then (depending on what return value you want from the expression):
# This evaluates to params[:data][:object]
params[:data][:object].tap do |p|
update(capture_created: p[:created], capture_currency: p[:currency], ...)
end
# This evaluates to whatever update returns
params[:data][:object].then do |p|
update(capture_created: p[:created], capture_currency: p[:currency], ...)
end
If the keys in the nested hash only need to be consistently renamed (i.e. add a "capture_" prefix) then #transform_keys:
update(params[:data][:object].transform_keys { |k| "capture_#{k}" })
is an option. String keys are fine with an ActiveRecord #update call but you could get symbols if you really want them:
update(params[:data][:object].transform_keys { |k| :"capture_#{k}" })
You might want to include a Hash#slice call if you want to ensure that you're only accessing certain keys:
update(params[:data][:object].slice(:created, :currency, ...).transform_keys { |k| :"capture_#{k}" })

Related

What's the difference these two function calling conventions?

Functions can be called in a couple ways:
say(1, 2, 3) # 123
say: 1, 2, 3 # (1, 2, 3)
The latter seems to pass a Positional, but apart from that I don't know how else they differ. Are there any differences that are important to know? What types of situations would you use one over the other?
#jjmerelo's answer covers the basics. This complementary answer, which aims at being somewhat exhaustive but hopefully not exhausting, covers traps, rare cases, and advice.
foo: valuea, valueb, ...
Surprisingly perhaps, this is not a call of a sub or method called foo.
Instead it's a statement that begins with a label, foo:.
The say: line in your question won't work in an ordinary program:
say: <a b c>; # Useless use of constant value a b c ...
The "Useless use" warning means the <a b c> doesn't get used in a useful way. The say: isn't doing anything with the list of values. It's just a label that doesn't do anything.
Presumably you are using something like the Perl 6 REPL. The REPL automatically says the last value in a line if it isn't otherwise used, thus making the line appear to work without a warning.
.a-method:
If a postfix method call using the form .a-method has no arguments other than the invocant (the argument to the left of the ., or the current topic if there isn't an explicit invocant) then you can just write it in the form:
42.say ;
You can optionally append a colon:
42.say: ;
There's no good reason to, but it's consistent with:
.a-method: arg2, arg3, ...
If you want to supply one or more arguments (other than the invocant) to a postfix .a-method call, then you have to pick one of two ways to introduce them.
One way is to write a colon immediately after the method name, before the argument(s). There must be no space between the method name and colon, and there must be space after the colon before the method argument(s).1
For example, the following uses a colon before the Numeric argument in the following method call:
say <abc 2 def ghi> .first: Numeric ; # 2
In the above line the method call expression (.first: Numeric) ends at the statement terminator (;). If there's an enclosing sub-expression such as an array subscript then the method call expression ends at the end of that sub-expression:
say .[1 + .first: Numeric] given <abc 2 def ghi> ; # ghi
The argument list of a colon form method call is also brought to a close by a valid statement modifier like given:
say .first: Numeric given <abc 2 def ghi> ; # 2
a-sub arg1, arg2, ...
This is the corresponding form for subroutine calls. The only format differences are that the sub has no invocant or . before the sub name and you must omit the colon after the sub name.
.a-method( arg2, arg3, ... )
a-sub( arg1, arg2, ... )
The other common form used for both method and sub calls is to immediately follow the method or sub name with parens to delimit arguments. The opening paren must immediately follow, without any space between the routine name and (.
Here's parens used with the .first method:
say 1 + .first(Numeric) given <abc 2 def ghi> ; # 3
This has the advantage that it's arguably prettier than the alternative of using outer parens:
say 1 + (.first: Numeric) given <abc 2 def ghi> ; # 3
If you want to put a sub call directly inside a double quoted string, you need to prefix the sub name with an & sigil and use the postfix parens form:
my #array = <abc 2 def ghi> ;
say "first number is &first(Numeric,#array)" ; # first number is 2
To put in a method call, you again have to use the postfix parens form, and you must also provide an explicit invocant (you can't just write "Some text .a-method()"):
my #array = <abc 2 def ghi> ;
say "first number is #array.first(Numeric)" ; # first number is 2
If there are no arguments (other than the invocant for a method call) you still need to use this form with empty parens if you want to interpolate a sub or method call in a string:
my #array = <abc 2 def ghi> ;
say "no method call #array[3].uc" ; # no method call ghi.uc
say "with method call #array[3].uc()" ; # with method call GHI
say "&rand"; # &rand
say "&rand()"; # 0.929123203371282
.a-method ( arrgh, arrgh, ... ) ;
This won't work.
Because the .a-method isn't followed by a colon, the method call is considered complete.
That means the next thing must be either an expression/statement ender like ;, or a postfix operator that will operate on the result of the method call, or an infix operator that will operate on the result and some following argument.
But ( arrgh, arrgh, ... ) is none of these. So you get a "Two terms in a row" compilation error.
.a-method:( arrgh, arrgh, ... ) ;
.a-method: ( arrgh, arrgh, ... ) ;
In general, DO NOT MIX use of a : with use of parens around arguments as part of a method call. There is no good reason to do so because it will either not work, or work only by accident, or work but very likely confuse readers.
Doing so without a space between the colon and opening paren yields a cryptic compilation error:
This type (QAST::WVal) does not support positional operations
Leaving a space appears to work -- but typically only by luck:
say .first: (Numeric) given <abc 2 def ghi> ; # 2
The (Numeric) is a single value in parens which yields Numeric so this line is the same as:
say .first: Numeric given <abc 2 def ghi> ; # 2
But if there are two or more arguments in parens, things will go awry. Use one of these forms:
say .first: Numeric, :k given <abc 2 def ghi> ; # 1
say .first(Numeric, :k) given <abc 2 def ghi> ; # 1
which correctly yield the array index ("key") of the 2 element rather than:
say .first: (Numeric, :k) given <abc 2 def ghi> ; # Nil
which yields Nil because the .first method doesn't do anything useful with a single argument that's a list of the form (Numeric, :k).
Of course, you may occasionally want to pass a single argument that's a list of values in parens. But you can do so without using a colon. For the sake of clarity, it's my advice that you instead write this as:
invocant.a-method(( valuea, valueb, ... ));
a-sub ( arrgh1, arrgh2, ... ) ;
As just explained for method calls, this passes ONE argument to a-sub, namely the single list ( arrgh1, arrgh2, ... ) which will seldom be what the writer means.
Again, my advice is to instead write this as:
`a-sub( valuea, valueb, ... ) ;`
or:
`a-sub valuea, valueb, ... ;`
if you mean to pass multiple arguments, or if you wish to pass a list as a single argument, then:
`a-sub(( valuea, valueb, ... )) ;`
.a-method : arrgha, arrghb, ...
a-sub : arrgha, arrghb, ...
For the method form this will net you a "Confused" compilation error.
The same is true for the sub form if a-sub takes no arguments. If a-sub takes arguments you'll get a "Preceding context expects a term, but found infix : instead" compilation error.
.&a-sub
There's a call form which lets you call a routine declared as a sub -- but use the .method call syntax. The following feeds the "invocant" qux on the left of the dot as the first argument to a sub called a-sub:
qux.&a-sub
Use a : or parentheses as usual to pass additional arguments to a-sub:
sub a-sub ($a, $b) { $a == $b }
say 42.&a-sub(42), 42.&a-sub(43); # TrueFalse
say 42.&a-sub: 42; # True
(In my original version of this section I wrote that one can not pass additional arguments. I had tested this and thought one could not. But I must have just gotten confused by something. #Enheh's comment led me to retest and discover that one can pass additional arguments just as with ordinary method calls. Thank you #Enheh. :))
a-method( invocant: arg2, arg3, ... )
a-method invocant: arg2, arg3, ...
Called "Indirect object notation" in the design docs, these formats are an undocumented and very rarely seen form of method call in which the call mimics the method declaration -- the method name comes first and then the invocant followed by a colon:
say first <abc 2 def ghi>: Numeric ; # 2
Note that say is a sub call because the next token, first, isn't followed by a colon. In contrast first is a method call because the token after it is followed by a colon.
Footnotes
1 All comments about spaces/spacing in this answer ignore unspacing.
As Raiph tells you above, say: is a label. So you didn't say anything (even though you thought you did) and -- outside use of the REPL -- the compiler will complain that your use of <a b c> was useless:
say: <a b c>; # OUTPUT: «WARNINGS for <tmp>:␤Useless use of constant value a b c in sink context (lines 1, 1, 1, 1, 1, 1)␤»
However, you often can use a : notation instead of parentheses in method calls. Consider the four routine calls below (two subroutine calls then two method calls):
my #numbers = (33, 77, 49, 11, 34);
say map *.is-prime, #numbers ; # simplest subroutine call syntax
say map( *.is-prime, #numbers ); # same meaning, but delimiting args
say #numbers.map( *.is-prime ) ; # similar, but using .map *method*
say #numbers.map: *.is-prime ; # same, but using : instead of parens
These sentences will all return the same (False False False True False).
In general, as you see above with map, you can use () in method calls wherever you would use :, but the opposite is not true; : can be used only in method calls.
Use () if the arguments need to be delimited precisely, as Raiph comments below.
This answer focuses on the basics. See Raiph's answer for more exhaustive coverage of the precise details of routine call syntax. (As an important example, the meaning of these calls normally changes if there's any spaces between the routine name and the colon (:) or opening parenthesis (()).

Perl 6: how to check `new` for invalid arguments?

What is the simplest way to check if invalid arguments are passed to the constructor method new?
use v6;
unit class Abc;
has Int $.a;
my $new = Abc.new( :b(4) );
The ClassX::StrictConstructor module should help. Install it with zef install ClassX::StrictConstructor and use it like so:
use ClassX::StrictConstructor;
class Stricter does ClassX::StrictConstructor {
has $.thing;
}
throws-like { Stricter.new(thing => 1, bad => 99) }, X::UnknownAttribute;
TLDR; If you are just worried about someone accidently mis-typing :a(4) as
:b(4), it might be better to just mark $.a as required.
class ABC {
has Int $.a is required;
}
ABC.new( :b(4) ); # error
# The attribute '$!a' is required, but you did not provide a value for it.
A quick hack would be to add a submethod TWEAK that makes sure any named values that you don't specify aren't there. It doesn't interfere with the normal workings of new and BUILD, so the normal type checks work without having to re-implement them.
class ABC {
has Int $.a;
submethod TWEAK (
:a($), # capture :a so the next won't capture it
*% # capture remaining named
() # make sure it is empty
) {}
}
A slightly more involved (but still hacky) way that should continue to work for subclasses, and that doesn't need to be updated with the addition of more attributes:
class ABC {
has Int $.a;
submethod TWEAK (
*%_ # capture all named
) {
# get the attributes that are known about
# (should remove any private attributes from this list)
my \accepted = say self.^attributes».name».subst(/.'!'/,'');
# ignore any that we know about
%_{|accepted}:delete;
# fail if there are any left
fail "invalid attributes %_.keys.List()" if %_
}
}
Write a custom new
Add this method declaration to your class:
method new ( :$a is required ) { callsame }
The :$a binds to a named argument named a (i.e. a key/value pair whose key is 'a', eg. a => 4).
The is required that follows the parameter name makes the a argument mandatory.
Now calls that do not pass a named argument named a will be rejected:
Abc.new( :b(4) ) ; # Error: Required named parameter 'a' not passed
The body of your new new method calls callsame. It calls the new that your class inherits, namely Mu's new. This routine walks through your class and its ancestors initializing attributes whose names correspond to named arguments:
Abc.new( :a(4) ) ; # OK. Initializes new object's `$!a` to `4`
Abc.new( :a(4) ).a.say ; # Displays `4`
UPD: See Brad's answer for a simpler approach that just adds the is required directly to the existing attribute declaration in the class:
has Int $.a is required; # now there's no need for a custom `new`
ABC.new( :b(4) ); # The attribute '$!a' is required...
Note the shift in the error message from Required named parameter 'a' not passed to attribute '$!a' is required.... This reflects the shift from adding a custom new with a required routine parameter that then gets automatically bound to the attribute, to just adding the is required directly to the attribute.
If that's not enough, read on.
The default new
Accepts any and all named arguments (pairs). It then passes them on to other routines that are automatically called during object construction. You passed the named argument :b(4) in your example code. It was accepted and passed on.
Rejects any and all positional arguments (not pairs).
The new call in your original code was accepted because you only passed a named argument, so no positional arguments, thus satisfying the argument validation directly done by the default new.
Rejecting unknown named arguments (as well as any positional arguments)
method new ( :$a!, *%rest ) { %rest and die 'nope'; callsame }
The *%rest "slurps" all named arguments other than one named a into a slurpy hash. If it is not empty then the die triggers.
See also timotimo's answer.
Requiring positional arguments instead of named
It's almost always both simpler and better to use named parameters/arguments to automatically initialize corresponding object attributes as shown above. This is especially true if you want to make it easy for folk to both inherit from your class and add attributes they also want initialized during new.
But if you wish to over-rule this rule-of-thumb, you can require one or more positional parameters/arguments and call methods on the new object to initialize it from the passed argument(s).
Perhaps the simplest way to do this is to alter the attribute so that it's publicly writable:
has Int $.a is rw;
and add something like:
method new ( $a ) { with callwith() { .a = $a; $_ } }
The callwith() routine calls the inherited new with no arguments, positional or named. The default (Mu) new returns a newly constructed object. .a = $a sets its a attribute and the $_ returns it. So:
my $new = Abc.new( 4 );
say $new.a ; # Displays `4`
If you don't want a publicly writable attribute, then leave the has as it was and instead write something like:
method !a ( $a ) { $!a = $a } # Methods starting `!` are private
method new ( $a ) { with callwith() { $_!a($a); $_ } }

Initialising Sequential values with for loop?

Is there any way to initialize a Sequential value not in one fellow swoop?
Like, can I declare it, then use a for loop to populate it, step by step?
As this could all happen inside a class body, the true immutability of the Sequential value could then kick in once the class instance construction phase has been completed.
Example:
Sequential<String> strSeq;
for (i in span(0,10)) {
strSeq[i] = "hello";
}
This code doesn't work, as I get this error:
Error:(12, 9) ceylon: illegal receiving type for index expression:
'Sequential' is not a subtype of 'KeyedCorrespondenceMutator' or
'IndexedCorrespondenceMutator'
So what I can conclude is that sequences must be assigned in one statement, right?
Yes, several language guarantees hinge on the immutability of sequential objects, so that immutability must be guaranteed by the language – it can’t just trust you that you won’t mutate it after the initialization is done :)
Typically, what you do in this situation is construct some sort of collection (e. g. an ArrayList from ceylon.collection), mutate it however you want, and then take its .sequence() when you’re done.
Your specific case can also be written as a comprehension in a sequential literal:
String[] strSeq = [for (i in 0..10) "hello"];
The square brackets used to create a sequence literal accept not only a comma-separated list of values, but also a for-comprehension:
String[] strSeq = [for (i in 0..10) "hello"];
You can also do both at the same time, as long as the for-comprehension comes last:
String[] strSeq = ["hello", "hello", for (i in 0..8) "hello"];
In this specific case, you could also do this:
String[] strSeq = ["hello"].repeat(11);
You can also get a sequence of sequences via nesting:
String[][] strSeqSeq = [for (i in 0..2) [for (j in 0..2) "hello"]];
And you can do the cartesian product (notice that the nested for-comprehension here isn't in square brackets):
[Integer, Character][] pairs = [for (i in 0..2) for (j in "abc") [i, j]];
Foo[] is an abbreviation for Sequential<Foo>, and x..y translates to span(x, y).
If you know upfront the size of the sequence you want to create, then a very efficient way is to use an Array:
value array = Array.ofSize(11, "");
for (i in 0:11) {
array[i] = "hello";
}
String[] strSeq = array.sequence();
On the other hand, if you don't know the size upfront, then, as described by Lucas, you need to use either:
a comprehension, or
some sort of growable array, like ArrayList.

lua not modifying function arguments

I've been learning lua and can't seem to make a simple implementation of this binary tree work...
function createTree(tree, max)
if max > 0 then
tree = {data = max, left = {}, right = {}}
createTree(tree.left, max - 1)
createTree(tree.right, max - 1)
end
end
function printTree(tree)
if tree then
print(tree.data)
printTree(tree.left)
printTree(tree.right)
end
end
tree = {}
createTree(tree, 3)
printTree(tree)
the program just returns nil after execution. I've searched around the web to understand how argument passing works in lua (if it is by reference or by value) and found out that some types are passed by reference (like tables and functions) while others by value. Still, I made the global variable "tree" a table before passing it to the "createTree" function, and I even initialized "left" and "right" to be empty tables inside of "createTree" for the same purpose. What am I doing wrong?
It is probably necessary to initialize not by a new table, but only to set its values.
function createTree(tree, max)
if max > 0 then
tree.data = max
tree.left = {}
tree.right = {}
createTree(tree.left, max - 1)
createTree(tree.right, max - 1)
end
end
in Lua, arguments are passed by value. Assigning to an argument does not change the original variable.
Try this:
function createTree(max)
if max == 0 then
return nil
else
return {data = max, left = createTree(max-1), right = createTree(max-1)}
end
end
It is safe to think that for the most of the cases lua passes arguments by value. But for any object other than a number (numbers aren't objects actually), the "value" is actually a pointer to the said object.
When you do something like a={1,2,3} or b="asda" the values on the right are allocated somewhere dynamically, and a and b only get addresses of those. Thus, when you pass a to the function fun(a), the pointer is copied to a new variable inside function, but the a itself is unaffected:
function fun(p)
--p stores address of the same object, but `p` is not `a`
p[1]=3--by using the address you can
p[4]=1--alter the contents of the object
p[2]=nil--this will be seen outside
q={}
p={}--here you assign address of another object to the pointer
p=q--(here too)
end
Functions are also represented by pointers to them, you can use debug library to tinker with function object (change upvalues for example), this may affect how function executes, but, once again, you can not change where external references are pointing.
Strings are immutable objects, you can pass them around, there is a library that does stuff to them, but all the functions in that library return new string. So once, again external variable b from b="asda" would not be affected if you tried to do something with "asda" string inside the function.

Lua - Execute a Function Stored in a Table

I was able to store functions into a table. But now I have no idea of how to invoke them. The final table will have about 100 calls, so if possible, I'd like to invoke them as if in a foreach loop. Thanks!
Here is how the table was defined:
game_level_hints = game_level_hints or {}
game_level_hints.levels = {}
game_level_hints.levels["level0"] = function()
return
{
[on_scene("scene0")] =
{
talk("hint0"),
talk("hint1"),
talk("hint2")
},
[on_scene("scene1")] =
{
talk("hint0"),
talk("hint1"),
talk("hint2")
}
}
end
Aaand the function definitions:
function on_scene(sceneId)
-- some code
return sceneId
end
function talk(areaId)
-- some code
return areaId
end
EDIT:
I modified the functions so they'll have a little more context. Basically, they return strings now. And what I was hoping to happen is that at then end of invoking the functions, I'll have a table (ideally the levels table) containing all these strings.
Short answer: to call a function (reference) stored in an array, you just add (parameters), as you'd normally do:
local function func(a,b,c) return a,b,c end
local a = {myfunc = func}
print(a.myfunc(3,4,5)) -- prints 3,4,5
In fact, you can simplify this to
local a = {myfunc = function(a,b,c) return a,b,c end}
print(a.myfunc(3,4,5)) -- prints 3,4,5
Long answer: You don't describe what your expected results are, but what you wrote is likely not to do what you expect it to do. Take this fragment:
game_level_hints.levels["level0"] = function()
return
{
[on_scene("scene0")] =
{
talk("hint0"),
}
}
end
[This paragraph no longer applies after the question has been updated] You reference on_scene and talk functions, but you don't "store" those functions in the table (since you explicitly referenced them in your question, I presume the question is about these functions). You actually call these functions and store the values they return (they both return nil), so when this fragment is executed, you get "table index is nil" error as you are trying to store nil using nil as the index.
If you want to call the function you stored in game_level_hints.levels["level0"], you just do game_level_hints.levels["level0"]()
Using what you guys answered and commented, I was able to come up with the following code as a solution:
asd = game_level_hints.levels["level0"]()
Now, asd contains the area strings I need. Although ideally, I intended to be able to access the data like:
asd[1][1]
accessing it like:
asd["scene0"][1]
to retrieve the area data would suffice. I'll just have to work around the keys.
Thanks, guys.
It's not really clear what you're trying to do. Inside your anonymous function, you're returning a table that uses on_scene's return value as keys. But your on_scene doesn't return anything. Same thing for talk.
I'm going to assume that you wanted on_scene and talk to get called when invoking each levels in your game_level_hints table.
If so, this is how you can do it:
local maxlevel = 99
for i = 0, maxlevel do
game_level_hints.levels["level" .. i] = function()
on_scene("scene" .. i)
talk("hint" .. i)
end
end
-- ...
for levelname, levelfunc in pairs(game_level_hints.levels) do
levelfunc()
end