How to represent callback functions in Blockly

1,479 views
Skip to first unread message

Andrew Stratton

unread,
May 8, 2015, 8:42:42 AM5/8/15
to blo...@googlegroups.com
Hi Everyone
  I have an issue with how to represent a called back function with parameters - essentially, I am trying to Blockify (?!) a call to an http request handler.

  I'm using a 'simplified' block generation library that I am developing for a specific project - where the developers aren't blockly knowledgeable, so I've used a defineRule below as a helper (factory) function to create blocks - the javascript function is a standard blockly function that is injected into the generated block.

var STATEMENT = 'STATEMENT';
defineRule
({
    name
: 'When UI Message', next:false, previous:false,
   
interface: [ {statement:STATEMENT} ],
    javascript
: function(block) {
       
var statement = getStatement(block, STATEMENT);
       
var result = "quando.whenHttpRequest('update', function(params, response) {\n"
           
+ statement
           
+ "});\n";
       
return result;
   
}
});


  The problem I have is with the use of the callback parameters (i.e. params and response) by the statement blocks.  I could add two new blocks (or variables) to represent params and response - but if I do this, they will also be available within other blocks as well - but the code they generate will fail at run time.

  Alternatively I can wrap the parameters with run time functions - e.g. so I could put a 'Respond' block - this could fail at run time when not appropriate... But again this is a run time issue.

  Is there any way to have the scope of the block allow a 'sort' of mutator like aspect to offer 'local' parameters JUST within the block.

  Or would this be done (instead) by validating the use of the parameters as 'invalid' when used in other blocks?

  Hope this makes sense
    Best wishes
      Andy

Brian Geppert

unread,
May 8, 2015, 4:25:30 PM5/8/15
to blo...@googlegroups.com
I'm currently working on adding async support to JS Interpreter and Blockly (I have it working for my purposes).  I'm currently assuming that async functions return something that's then()-able.  I'm open to ideas on what the API should look like for this.  I'm currently concerned that JS Interpreter's run() and step() methods will be affected by the presence of async methods.  I'm not sure that the best solution is to automatically resume run() calls once the async function resolves, I'm currently thinking it'd be cleanest to add a separate runAsync() method.  This approach results in asynchronous operations appearing synchronous to the end user.  I've not yet spent any time thinking about how to make async blocks work outside of the JS Interpreter context.

Cheers,
Brian

Mark Friedman

unread,
May 8, 2015, 6:10:59 PM5/8/15
to blo...@googlegroups.com
App Inventor (which uses Blockly) has implemented the sort of thing you're talking about for handling local variables and event handlers that have parameters.  Looking at their source might help you.  You can ask for more help on their open source developers forum.

Someday, maybe they'll find their way to upstreaming their Blockly changes so folks like you can more easily do this sort of thing ;-)

-Mark

--
You received this message because you are subscribed to the Google Groups "Blockly" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blockly+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Andrew Stratton

unread,
May 12, 2015, 8:05:25 AM5/12/15
to blo...@googlegroups.com
There doesn't seem to be a standard Blockly way of doing this.  I would welcome views on how parameters within function can safely be used (though for my specific purpose I might not able to to use a generic solution).

So far, I can think of a few ways I might achieve this:
  1. Make Response a drop down option of a logger block (and similar) which is invalid (disabled/removed) when not in context (e.g. select between Log [Console||Response]). Similarly, make params an alternative to another 'readable' object - e.g. Config. This way, a valid block can be always dropped in - but the choices within the block are limited to the context that the block is contained by. This would work, but is bit clunky - relying on a similar block type.
  2. Ignore the problem until code generation - when the code generator deliberately fails with an 'author friendly' response. This isn't 'BAD', since it doesn't interrupt the Author's flow, and it also stops incorrect code being generated.  Unfortunately this seems to step outside of the current Blockly philosophy (I maybe wrong here) by requiring the code generator to keep track of the context within which blocks exist. This could become a whole problem in itself.
I have already discounted the following options based on my current (not very large) knowledge of Blockly:
  1. Add/remove the response/params from the toolbox when in the scope of the relevant block - since Blockly doesn't know which is the current block - this is not (I believe) possible.
  2. Restrict the response/params block access (i.e. using field types) - and update (on the fly) the blocks that are allowed to contain them. Again, since Blockly doesn't have a target until a block is 'dropped', this would only allow blocks to 'bounce off' the 'wrong' destination. This sounds unfriendly to the Authors (my end users of the blocks).
  3. Use the validation to make the response/params block invalid when not in the scope (contained by) of the correct block. Unfortunately, I don't believe this would work until the block was in place - at which it might be invalid, but could be left in place - and then might generate (invalid) code.
  4. Ignore the problem until run time - when either the program fails, or logs an error/warning and swallows the incorrect access - carrying on as usual. I HATE THIS OPTION - even though it's probably the easiest.
Best wishes and thank you for the response so far

  Andy Stratton

Andrew Stratton

unread,
May 12, 2015, 10:24:16 AM5/12/15
to blo...@googlegroups.com
Just found that Neil had already recommended a way with the 'break' block - at Forum Post

:)

Andrew Stratton

unread,
May 13, 2015, 4:52:33 AM5/13/15
to blo...@googlegroups.com
Success - thank you Neil again :)

My code is a bit different from standard blockly code - but this may help someone in future - I have a factory that builds blocks from json - this is the part that handles a list of block ids that a block must exist within:

            if (_exists(json.valid_in)) {
                blockly
.Blocks[id].onchange = function() {
                   
var legal = false;
                   
var check_block = this;
                   
do {
                       
if (json.valid_in.indexOf(check_block.type) !== -1) {
                            legal
= true;
                       
}
                        check_block
= check_block.getSurroundParent();
                   
} while (!legal && check_block);
                   
if (legal) {
                       
this.setWarningText(null);
                   
} else {
                       
this.setWarningText(
                           
"Block must exist within one of " + json.valid_in);
                   
}                    
               
};
           
}

The factory method is passed the json like this (for reference), where RULE_WHEN_VITRINE is a block that must be a parent of the Reply block - but this can be a list of blocks...:

        defineAction({
            name
: 'Reply UI',
            valid_in
: ['quando_' + RULE_WHEN_VITRINE],
           
// TODO prefix needs separating
           
interface: [
               
{name: SUCCESS_MENU, title:'',
                    menu
: ['success','fail']
           
}],
            javascript
: function(block) {
               
var json = {};
                json
.response = getMenu(block, SUCCESS_MENU);
               
return "response.write('"+JSON.stringify(json)+"');\n";
           
}
       
});

Hope this helps someone

Best wishes
  Andy Stratton
Reply all
Reply to author
Forward
0 new messages