I have a reduce function like this:
ops = rqOps.reduce (p, { commit: id: cid, type: type }, idx, arr) ->
# Do stuff here
p
, {}
which works fine, but now the name of the second argument compiles to _arg. How can I give it a different name? I've tried several different approaches like arg = { commit: id: cid, type: type } and { commit: id: cid, type: type } : arg and { commit: id: cid, type: type } = arg but nothing compiles to the intended result. What's wrong with my syntax?
Why do you care what the second argument is called? Your object destructuring means that you wouldn't work with that argument at all, you'd just work with cid and type instead. The _arg name and even its existence is subject to change and none of your business.
For example, if you have this:
rqOps = [
{ commit: { id: 1, type: 2 } }
{ commit: { id: 2, type: 4 } }
]
ops = rqOps.reduce (p, { commit: id: cid, type: type }, idx, arr) ->
console.log(cid, type)
p
, { }
then you'll get 1, 2 and 2, 3 in the console. If you want the whole second argument then give it a name and unpack it inside the iterator function:
ops = rqOps.reduce (p, arg, idx, arr) ->
{ commit: id: cid, type: type } = arg
#...
Related
Solved
For a first ever macro to write this wasnt the easiest. But I learned a lot, much kudo's to Gama11 who pointed me in the right direction, and the coreteam for such a thing of beauty: Haxe.
And I even added some slick doc field strings, so you get nice info during autocompletion.
Main.hx
var e1:Either<String, Int, Bool> = Either3._1('test');
var e2:Either<String, Int, Bool> = Either3._2(1);
var e3:Either<String, Int, Bool> = Either3._3(true);
var error:Either<String, Int, Bool> = Either3._3('Bool expected, but got a String this will give an error');
Either.hx
package;
#:genericBuild(EitherMacro.build())
class Either<Rest> {}
/*#:genericbuild only works on classes, but
can still override the class with an enum. Funky. */
EitherMacro.hx
package;
#if macro
import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;
using haxe.macro.Tools;
class EitherMacro {
static var eitherTypes = new Map<Int,Bool>();
static function build():ComplexType {
return switch (Context.getLocalType()) {
case TInst(_.get() => {name: "Either"}, params):
buildEitherEnum(params);
default:
throw false;
}
return macro:Dynamic;
}
static function buildEitherEnum(params:Array<Type>):ComplexType {
var numParams = params.length;
var name='Either$numParams';
if (!eitherTypes.exists(numParams)){
Context.defineType(defineType(name, params));
eitherTypes[numParams] = true;
}
return TPath({pack: [], name: name, params: [for (t in params) TPType(t.toComplexType())]});
}
private static inline function defineType(name:String, params:Array<Type>){
var typeParams:Array<TypeParamDecl> = [];
var typeStrings:Array<String>=[];
var numParams = params.length;
var fields:Array<Field>=[];
for (i in 0...numParams) {
var t=i+1;
typeStrings.push(params[i].toString());
}
var constDocStr=typeStrings.join(',');
for (i in 0...numParams) {
var t=i+1;
var typeString:String=typeStrings[i];
typeParams.push({name:'T$t'});
fields.push(
{
name: '_$t',
pos: Context.currentPos(),
doc: 'from $name<$constDocStr> _$t(v: $typeString)',
kind:FFun({
ret: null,
params: [{name:'T$t'}],
expr: null,
args: [
{
name: 'v',
type: TPath(
{
name:'T$t',
params:[],
pack:[]
}
)
}
]
}
)
}
);
}
var docStr:String="Either represents values which are either of type ";
for(k in 0...typeStrings.length){
if(k!=typeStrings.length-1){
docStr+=typeStrings[k]+" or ";
} else {
docStr+=typeStrings[k]+".";
}
}
return {
pack:[],
name:name,
pos:Context.currentPos(),
doc:docStr,
isExtern: false,
meta:null,
kind:TDEnum,
fields:fields,
params:typeParams
}
}
}
#end
Debugging your macro's the easy way
usage of -D dump=pretty dumps typed AST in dump subdirectory using prettified mode. The output from dump=pretty is almost indistuingishable from regular Haxe code. When errors appear, you find iin the root of the dump directory a file called 'decoding_error.txt'. Its contents might look like this:
{
doc: null
fields: null <- expected value
isExtern: null
kind: null <- expected value
meta: null
name: null <- expected value
pack: null <- expected value
params: null
pos: null <- expected value
}
line 3: expected value
line 5: expected value
line 7: expected value
line 8: expected value
line 10: expected value
This made it much easier for me to debug. But the even better way, is way simple... To debug the easiest way, go to your macrofile (in my case EitherMacro.hx) and do
class EitherMacro{
public static function build(){
var fields=Context.getBuildFields();
var type=Context.getLocalType();
trace(type);
for(f in fields){
trace(f);
}
// your other code
/*
If you use #:build)() instead of #:genericbuild
to debug. Make sure the buildfunction returns
Array<Field> and put at the last line
return Context.getBuildFields();
if you use #:genericbuild you must return
ComplexType, and you can add as the line
return macro:Dynamic; if you have no working return yet.
*/
}
}
the output might look like this:
source/EnumBuilder2.hx:18: TEnum(SomeEnum,[TInst(SomeEnum.T1,[]),TInst(SomeEnum.T2,[]),TInst(SomeEnum.T3,[])])
source/EnumBuilder2.hx:20: {name: _1, doc: null, pos: #pos(source/SomeEnum.hx:4: characters 5-14), access: [], kind: FFun({ret: null, params: [], expr: null, args: [{name: v, opt: false, meta: [], type: TPath(<...>), name_pos: #pos((unknown)), value: null}]}), meta: [], name_pos: #pos(source/SomeEnum.hx:4: characters 5-7)}
source/EnumBuilder2.hx:20: {name: _2, doc: null, pos: #pos(source/SomeEnum.hx:5: characters 5-14), access: [], kind: FFun({ret: null, params: [], expr: null, args: [{name: v, opt: false, meta: [], type: TPath(<...>), name_pos: #pos((unknown)), value: null}]}), meta: [], name_pos: #pos(source/SomeEnum.hx:5: characters 5-7)}
source/EnumBuilder2.hx:20: {name: _3, doc: null, pos: #pos(source/SomeEnum.hx:6: characters 5-14), access: [], kind: FFun({ret: null, params: [], expr: null, args: [{name: v, opt: false, meta: [], type: TPath(<...>), name_pos: #pos((unknown)), value: null}]}), meta: [], name_pos: #pos(source/SomeEnum.hx:6: characters 5-7)}
Another good idea with #:genericbuild(), is to first constructed an enum by hand(or whatever type) and after that trace it using a #:genericbuild, or if you got too much errors using #:build. Then you can copy those trace outputs and try use that code to craft the AST in the macro. This will seriously speedup your development, especially in case of complicated macro's. Almost mindlessly ;-)
Your macro has never run.
Replace your build() function with the following to verify
static function build():ComplexType {
trace('build');
return macro:Dynamic;
}
I suppose #:genericBuild only works for class
Let us consider a simple enum implementation with a static method that check whether a value has an associated value (the efficiency of the implementation is not to be regarded here):
enum Letter {
Alpha = -1,
A = 0,
B = 1,
C = 2,
}
impl Letter {
pub fn in_enum(value: isize) -> bool
{
match value {
-1 => true,
0 => true,
1 => true,
2 => true,
_ => false,
}
}
}
Now, let us write a macro for building enums with an equivalent in_enum method. The macro below was written with some guidance from the Serde guide for enum deserialization as numbers, in which matching for enum variant values also occurs.
macro_rules! my_enum {
($name:ident { $($variant:ident = $value:expr, )* }) => {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum $name {
$($variant = $value,)*
}
impl $name {
pub fn in_enum(value: isize) -> bool
{
match value {
$( $value => true, )*
_ => false,
}
}
}
}
}
my_enum!(Letter {
Alpha = -1,
A = 0,
B = 1,
C = 2,
});
Playground.
With version 1.18.0, the compiler won't accept the variant with a negative integer.
error: expected pattern, found `-1`
--> src/main.rs:13:24
|
13 | $( $value => true, )*
| ^^^^^^
This seems to happen regardless of how I write this pattern down in the macro, or whether I use i32 or isize for the value method parameter. Changing the fragment specifier of $value to pat is also out of the question: the compiler will refuse to build the enum, even without negative variant values.
error: expected expression, found `-1`
--> src/main.rs:5:26
|
5 | $($variant = $value,)*
| ^^^^^^
What's surprising about this is that it works without using macros, as well as when I discard the Alpha variant.
Why does this happen?
This is a bug in the compiler and is already fixed in the nightly version as of today (Jul 5, 2017), and released in stable version 1.20.0.
I'm looking to use the sails attribute type 'array' in my app, but I can't find documentation for this anywhere.
I'd like to do the following:
module.exports = {
attributes : {
images: {
type: ['string']
},
location: {
type: ['float','float']
}
}
}
image is an array that will hold a list of image urls and location will hold 2 floats. Will this work in sail's? Else how can I get this to work.
Thanks
PS: I'm working solely with MongoDB
As of Sails 1.0 type array is no longer supported.
The type "array" is no longer supported. To use this type in your model, change
type to one of the supported types and set the columnType property to a column
type supported by the model's adapter, e.g. { type: 'json', columnType: 'array' }
SOLUTION ONE
Set up property to store an images array and a location array...
module.exports = {
attributes : {
images: {
type: 'json',
columnType: 'array'
}
location: {
type: 'json',
columnType: 'array'
}
}
}
SOLUTION TWO
A more elegant solution is to set up a single object to store both filename and location data
module.exports = {
attributes : {
images: {
type: 'json'
}
}
}
Then in your controller you would store object properties as arrays...
let imageData = {
filename: ["image1.jpg", "image2.jpg", "image3.jpg"],
location: [154.123123, 155.3434234, 35.12312312]
};
Images.create({images:imageData});
Some issues when storing data to the json object is that a string like "image1.jpg,image2.jpg,image3.jpg" will store in MongoDb no worries... doh. Ensure that when POSTing you may need to split the data .split(',').
sailsjs provide a function to solve your question,you can try this
module.exports = {
attributes : {
images: {
type: 'string'
},
location: {
type: 'json'
}
}
}
As far as I know, you can only specify it like this:
module.exports = {
attributes : {
images: {
type: 'array'
},
location: {
type: 'array'
}
}
}
See Sails ORM Attributes
For sails 1.0, for array maybe you can try this way i'm using just for sharing.
Also you can replace before update and process native query() and delete the attributes for updating by waterline. Hope this help you.
variants:
{
type: 'json',
columnType: 'array',
custom: (value) =>
{
/*
[
code : unique, string
name : string, maxLength[30]
cost : number, isFinite
price : number, isFinite
default : boolean
]
*/
return _.isArray(value)
&& _.every(value, (variant1) =>
{
return _.countBy(value, (variant2) =>
{
return variant2.code == variant1.code ? 'eq' : 'uneq';
}).eq <= 1
&& _.isString(variant1.name) && variant1.name.length < 30
&& _.isNumber(variant1.cost) && _.isFinite(variant1.cost)
&& _.isNumber(variant1.price) && _.isFinite(variant1.price)
&& _.isBoolean(variant1.default);
});
},
},
You can use type as ref for arrays and objects.
module.exports = {
attributes: {
images: {
type: 'ref'
},
location: {
type: 'ref'
}
}
}
Is it possible to write a macro that generates a function where the number of arguments to this function to be a determined by the macro? For instance, I'd like to write something to make using prepared statements in the Cassandra driver easier.
let prepared = prepare!(session, "insert into blah (id, name, reading ) values (?, ?, ?)", int, string, float);
let stmt = prepared(1, "test".to_string(), 3.1);
session.execute(stmt);
prepare! would need to generate something like (unwrap only here for brevity):
fn some_func(arg1, arg2, arg3) -> Statement {
let mut statement = Statement::new("insert into blah (id, name, reading ) values (?, ?, ?)", 3);
statement.bind_int(0, arg1).unwrap()
.bind_string(1, arg2).unwrap()
.bind_float(2, arg3).unwrap()
}
Two hard things in Rust macros: counting and unique identifers. You have both. Then again, I'm the one writing the answer, so I suppose it's my problem now. At least you didn't ask about parsing the string (which is outright impossible without compiler plugins).
Another impossible thing is mapping types to different methods. You just can't. Instead, I'm going to assume the existence of a helper trait that does this mapping.
Also, Rust doesn't have int, string, or float. I assume you mean i32, String, and f32.
Finally, the way you've written the invocation and expansion don't really gel. I don't see why session is involved; it's not used in the expansion. So I'm going to take the liberty of just pretending you don't need it; if you do, you'll have to hack it back in.
So, with that, here's what I came up with.
// Some dummy types so the following will type-check.
struct Statement;
impl Statement {
fn new(stmt: &str, args: usize) -> Self { Statement }
fn bind_int(self, pos: usize, value: i32) -> Result<Self, ()> { Ok(self) }
fn bind_float(self, pos: usize, value: f32) -> Result<Self, ()> { Ok(self) }
fn bind_string(self, pos: usize, value: String) -> Result<Self, ()> { Ok(self) }
}
struct Session;
impl Session {
fn execute(&self, stmt: Statement) {}
}
// The supporting `BindArgument` trait.
trait BindArgument {
fn bind(stmt: Statement, pos: usize, value: Self) -> Statement;
}
impl BindArgument for i32 {
fn bind(stmt: Statement, pos: usize, value: Self) -> Statement {
stmt.bind_int(pos, value).unwrap()
}
}
impl BindArgument for f32 {
fn bind(stmt: Statement, pos: usize, value: Self) -> Statement {
stmt.bind_float(pos, value).unwrap()
}
}
impl BindArgument for String {
fn bind(stmt: Statement, pos: usize, value: Self) -> Statement {
stmt.bind_string(pos, value).unwrap()
}
}
// The macro itself.
macro_rules! prepare {
// These three are taken straight from
// https://danielkeep.github.io/tlborm/book/
(#as_expr $e:expr) => {$e};
(#count_tts $($tts:tt)*) => {
<[()]>::len(&[$(prepare!(#replace_tt $tts ())),*])
};
(#replace_tt $_tt:tt $e:expr) => {$e};
// This is how we bind *one* argument.
(#bind_arg $stmt:expr, $args:expr, $pos:tt, $t:ty) => {
prepare!(#as_expr <$t as BindArgument>::bind($stmt, $pos, $args.$pos))
};
// This is how we bind *N* arguments. Note that because you can't do
// arithmetic in macros, we have to spell out every supported integer.
// This could *maybe* be factored down with some more work, but that
// can be homework. ;)
(#bind_args $stmt:expr, $args:expr, 0, $next:ty, $($tys:ty,)*) => {
prepare!(#bind_args prepare!(#bind_arg $stmt, $args, 0, $next), $args, 1, $($tys,)*)
};
(#bind_args $stmt:expr, $args:expr, 1, $next:ty, $($tys:ty,)*) => {
prepare!(#bind_args prepare!(#bind_arg $stmt, $args, 1, $next), $args, 2, $($tys,)*)
};
(#bind_args $stmt:expr, $args:expr, 2, $next:ty, $($tys:ty,)*) => {
prepare!(#bind_args prepare!(#bind_arg $stmt, $args, 2, $next), $args, 3, $($tys,)*)
};
(#bind_args $stmt:expr, $_args:expr, $_pos:tt,) => {
$stmt
};
// Finally, the entry point of the macro.
($stmt:expr, $($tys:ty),* $(,)*) => {
{
// I cheated: rather than face the horror of trying to *also* do
// unique identifiers, I just shoved the arguments into a tuple, so
// that I could just re-use the position.
fn prepared_statement(args: ($($tys,)*)) -> Statement {
let statement = Statement::new(
$stmt,
prepare!(#count_tts $(($tys))*));
prepare!(#bind_args statement, args, 0, $($tys,)*)
}
prepared_statement
}
};
}
fn main() {
let session = Session;
let prepared = prepare!(
r#"insert into blah (id, name, reading ) values (?, ?, ?)"#,
i32, String, f32);
// Don't use .to_string() for &str -> String; it's horribly inefficient.
let stmt = prepared((1, "test".to_owned(), 3.1));
session.execute(stmt);
}
And here's what the main function expands to, to give you a frame of reference:
fn main() {
let session = Session;
let prepared = {
fn prepared_statement(args: (i32, String, f32)) -> Statement {
let statement = Statement::new(
r#"insert into blah (id, name, reading ) values (?, ?, ?)"#,
<[()]>::len(&[(), (), ()]));
<f32 as BindArgument>::bind(
<String as BindArgument>::bind(
<i32 as BindArgument>::bind(
statement, 0, args.0),
1, args.1),
2, args.2)
}
prepared_statement
};
// Don't use .to_string() for &str -> String; it's horribly inefficient.
let stmt = prepared((1, "test".to_owned(), 3.1));
session.execute(stmt);
}
This question already has an answer here:
Why is this match pattern unreachable when using non-literal patterns?
(1 answer)
Closed 5 years ago.
I am doing some simple stuff with rust... just touching some ground you know.
So I was playing with command line arguments, and I can't go past this:
use std::os::args;
fn main(){
let arg1 = args().get(1).to_str();
let help_command = "help";
if args().len() == 1 {
println!("No arguments.");
}
else if args().len() == 2 {
match arg1 {
help_command => println!("Do ..."),
_ => println!("no valid argument")
}
}
}
I can't compile... The error is:
main.rs:17:4: 17:5 error: unreachable pattern
main.rs:17 _ => println!("no valid argument")
^
error: aborting due to previous error
Also, I am using Rust 0.11.0-pre-nightly.
EDIT: Also, if I go with this approach:
match arg1 {
"help" => { /* ... / },
_ => { / ... */ },
}
It throws another error:
error: mismatched types: expected collections::string::String but found &'static str (expected struct collections::string::String but found &-ptr)
You can't use variables on Rust's match patterns. The code is being interpreted as binding whatever value is on arg1 as a new variable called help_command, and thus the catch-all pattern never matches.
You can use the literal string to match arg1:
match arg1 {
"help" => { /* ... */ },
_ => { /* ... */ },
}
Or use a guard:
match arg1 {
command if command == help_command => { /* ... */ },
_ => { /* ... */ }
}
If you are concerned about the type safety and/or repetition with using strings directly, you can parse the command into an enum:
enum Command {
HelpCommand,
DoStuffCommand
}
fn to_command(arg: &str) -> Option<Command> {
match arg {
"help" => Some(HelpCommand),
"do-stuff" => Some(DoStuffCommand),
_ => None,
}
}
Working example
Update (thanks #ChrisMorgan): It is also possible to use a static variable:
static HELP: &'static str = "help";
match arg1 {
HELP => { /* ... */ },
_ => { /* ... */ },
}
About the error reported in the question edit: Rust has two kinds of strings: &str (string slice) and String (owned string). The main difference is that the second is growable and can be moved. Refer to the links to understand the distinction better.
The error you are encountering is due to the fact that string literals ("foo") are of type &str, while std::os::args() is a Vec of String. The solution is simple: Use the .as_slice() method on the String to take slice out of it, and be able to compare it to the literal.
In code:
match arg1.as_slice() {
"help" => { /* ... */ },
_ => { /* ... */ },
}