Loading ...
Sorry, an error occurred while loading the content.

Circular Function Definitions

Expand Messages
  • Felix E. Klee
    If I write code such as the following, then JSLint complains that f is not defined. var sendToServer; function callback(moreToDo) { // do something ... if
    Message 1 of 17 , Dec 31, 2010
    • 0 Attachment
      If I write code such as the following, then JSLint complains that "f" is
      not defined.

      var sendToServer;

      function callback(moreToDo) {
      // do something ...
      if (moreToDo) {
      f();
      }
      }

      function f() {
      sendToServer('xyz', callback);
      }

      What's the most elegant solution to get rid of the error message?

      What I currently do, and what I consider inconsistent, and thus
      confusing:

      var sendToServer,
      f; // to get rid of JSLint error message

      function callback(moreToDo) {
      // do something ...
      if (moreToDo) {
      f();
      }
      }

      f = function () {
      sendToServer('xyz', callback);
      };
    • Erik Eckhardt
      Instead of passing in a flag and then hardcoding the function, pass in a function: function callback(morefn) { ___// do something ... ___if (typeof(morefn) ===
      Message 2 of 17 , Dec 31, 2010
      • 0 Attachment
        Instead of passing in a flag and then hardcoding the function, pass in a
        function:

        function callback(morefn) {
        ___// do something ...
        ___if (typeof(morefn) === 'function') {
        ______moref();
        ___}
        }

        f = function () {
        ___sendToServer('xyz', callback ? f : null);
        };

        You could alternately make f take a parameter that when false causes an
        immediate return. When true or missing, nothing happens. Then:

        ___sendToServer('xyz', function() {f(callback)});

        >


        [Non-text portions of this message have been removed]
      • Felix E. Klee
        ... Are you sure this makes sense? The second last line will always evaluate to: sendToServer( xyz , f); And I don t see how this solves the issue I raised:
        Message 3 of 17 , Dec 31, 2010
        • 0 Attachment
          On Fri, Dec 31, 2010 at 7:21 PM, Erik Eckhardt <erik@...> wrote:
          > function callback(morefn) {
          > // do something ...
          > if (typeof(morefn) === 'function') {
          > moref();
          > }
          > }
          >
          > f = function () {
          > sendToServer('xyz', callback ? f : null);
          > };

          Are you sure this makes sense? The second last line will always evaluate to:

          sendToServer('xyz', f);

          And I don't see how this solves the issue I raised:

          var f;

          function callback(moreToDo) {
          // ...
          }

          f = function () {
          // ...
          };

          That's inconsistent, and thus confusing. Of course one could write:

          var callback, f;

          callback = function (moreToDo) {
          // ...
          };

          f = function () {
          // ...
          };

          Now things are consistent. But is that the way to go?
        • Rob Richardson
          Felix, The primary concern is you re trying to use a function earlier in your code file than when it is defined. If you put var f = function () {... before
          Message 4 of 17 , Dec 31, 2010
          • 0 Attachment
            Felix,

            The primary concern is you're trying to use a function earlier in your code
            file than when it is defined. If you put var f = function () {... before
            function callback(... then JSLint would likely be fine. I understand that
            given the business rules around the code in question that callback() (and
            thus f()) would never be used before it was defined, but JSLint doesn't know
            that.

            The secondary concern is that you're passing in a flag that says whether you
            should use a follow-up function. Perhaps you refactored f() out of
            callback() for readability, perhaps f() is used in more than one place,
            perhaps you never considered putting the contents of f inside callback.
            Alas, the "moreToDo is true means run f()" is a bit confusing. Passing the
            function to run into callback() instead of a flag makes callback() more
            generic, and makes this point a bit more clear, and that was the focus of
            Erik's thoughts.

            Rob


            -----Original Message-----
            From: jslint_com@yahoogroups.com [mailto:jslint_com@yahoogroups.com] On
            Behalf Of Felix E. Klee
            Sent: Friday, December 31, 2010 1:12 PM
            To: jslint_com@yahoogroups.com
            Subject: Re: [jslint] Circular Function Definitions

            On Fri, Dec 31, 2010 at 7:21 PM, Erik Eckhardt <erik@...> wrote:
            > function callback(morefn) {
            > // do something ...
            > if (typeof(morefn) === 'function') {
            > moref();
            > }
            > }
            >
            > f = function () {
            > sendToServer('xyz', callback ? f : null);
            > };

            Are you sure this makes sense? The second last line will always evaluate to:

            sendToServer('xyz', f);

            And I don't see how this solves the issue I raised:

            var f;

            function callback(moreToDo) {
            // ...
            }

            f = function () {
            // ...
            };

            That's inconsistent, and thus confusing. Of course one could write:

            var callback, f;

            callback = function (moreToDo) {
            // ...
            };

            f = function () {
            // ...
            };

            Now things are consistent. But is that the way to go?
          • Felix E. Klee
            On Fri, Dec 31, 2010 at 9:49 PM, Rob Richardson ... No, the opposite is true. See the second example in my original post. That works
            Message 5 of 17 , Jan 1, 2011
            • 0 Attachment
              On Fri, Dec 31, 2010 at 9:49 PM, Rob Richardson <erobrich@...>
              wrote:
              > If you put var f = function () {... before function callback(... then
              > JSLint would likely be fine.

              No, the opposite is true. See the second example in my original post.
              That works fine with JSLint. Changing the order would make JSLint
              complain about "callback" not being defined.

              But that's not really the issue.

              > The secondary concern is that you're passing in a flag that says
              > whether you should use a follow-up function.

              The code is more complicated in reality. There is a lot happening in
              "f", and whether to send another request to the server is determined
              based not just on the state of "moreToDo".

              The code chains XHRs, to download server generated data.

              > Passing the function to run into callback() instead of a flag makes
              > callback() more generic, and makes this point a bit more clear, and
              > that was the focus of Erik's thoughts.

              Erik's code would just generate an infinite loop, because it evaluates
              to:

              f = function () {
              sendToServer('xyz', f);
              };

              But that's *all* besides the point. My question, I think, can be
              summarized to:

              For the sake of consistency, and thus readability, should I always
              define functions as:

              var f;

              // ...

              f = function () {
              // ...
              }

              instead of the short form:

              function f() {
              // ..
              }

              ?

              Furthermore, can this be enforced with JSLint?
            • Felix E. Klee
              On Sat, Jan 1, 2011 at 11:59 AM, Felix E. Klee ... Sorry, that should be: There is a lot happening in callback
              Message 6 of 17 , Jan 1, 2011
              • 0 Attachment
                On Sat, Jan 1, 2011 at 11:59 AM, Felix E. Klee <felix.klee@...>
                wrote:
                > There is a lot happening in "f"

                Sorry, that should be:

                There is a lot happening in "callback"
              • Rob Richardson
                Ah, I see I may have misunderstood. You re proposing the following: var a,b; a = function () { // ... b(); // ... } b = function () { // ... a(); // ... } Is
                Message 7 of 17 , Jan 1, 2011
                • 0 Attachment
                  Ah, I see I may have misunderstood. You're proposing the following:

                  var a,b;

                  a = function () {
                  // ...
                  b();
                  // ...
                  }

                  b = function () {
                  // ...
                  a();
                  // ...
                  }

                  Is this the dilemma at hand?

                  Rob


                  -----Original Message-----
                  From: jslint_com@yahoogroups.com [mailto:jslint_com@yahoogroups.com] On
                  Behalf Of Felix E. Klee
                  Sent: Saturday, January 01, 2011 3:59 AM
                  To: jslint_com@yahoogroups.com
                  Subject: Re: [jslint] Circular Function Definitions

                  On Fri, Dec 31, 2010 at 9:49 PM, Rob Richardson <erobrich@...>
                  wrote:
                  > If you put var f = function () {... before function callback(... then
                  > JSLint would likely be fine.

                  No, the opposite is true. See the second example in my original post.
                  That works fine with JSLint. Changing the order would make JSLint
                  complain about "callback" not being defined.

                  But that's not really the issue.

                  > The secondary concern is that you're passing in a flag that says
                  > whether you should use a follow-up function.

                  The code is more complicated in reality. There is a lot happening in
                  "f", and whether to send another request to the server is determined
                  based not just on the state of "moreToDo".

                  The code chains XHRs, to download server generated data.

                  > Passing the function to run into callback() instead of a flag makes
                  > callback() more generic, and makes this point a bit more clear, and
                  > that was the focus of Erik's thoughts.

                  Erik's code would just generate an infinite loop, because it evaluates
                  to:

                  f = function () {
                  sendToServer('xyz', f);
                  };

                  But that's *all* besides the point. My question, I think, can be
                  summarized to:

                  For the sake of consistency, and thus readability, should I always
                  define functions as:

                  var f;

                  // ...

                  f = function () {
                  // ...
                  }

                  instead of the short form:

                  function f() {
                  // ..
                  }

                  ?

                  Furthermore, can this be enforced with JSLint?
                • Felix E. Klee
                  ... Yes. To me it seems reasonable to - for consistency - avoid the short form: function f() { // ... } And I would like to enforce that in the entire project,
                  Message 8 of 17 , Jan 3, 2011
                  • 0 Attachment
                    On Sun, Jan 2, 2011 at 4:54 AM, Rob Richardson <erobrich@...> wrote:
                    > var a,b;
                    >
                    > a = function () {
                    > // ...
                    > b();
                    > // ...
                    > }
                    >
                    > b = function () {
                    > // ...
                    > a();
                    > // ...
                    > }
                    >
                    > Is this the dilemma at hand?

                    Yes.

                    To me it seems reasonable to - for consistency - avoid the short form:

                    function f() {
                    // ...
                    }

                    And I would like to enforce that in the entire project, with JSLint.
                    However, as JSLint does not offer a corresponding option, I wonder if I
                    am on the wrong track, overdoing things.
                  • Erik Eckhardt
                    My apologies. My code was written too hastily. I thought you were using a Boolean to indicate whether to make the return call to f. The point I was trying to
                    Message 9 of 17 , Jan 3, 2011
                    • 0 Attachment
                      My apologies. My code was written too hastily. I thought you were using a
                      Boolean to indicate whether to make the return call to f.

                      The point I was trying to get across is that you don't need the circularity.

                      There are many ways to solve this. Here's one: Any time you want something
                      to happen after the callback, pass the call to the callback in an anonymous
                      function:

                      f = function() {
                      ___// do stuff
                      ___xhr(params, callback); // plain call to cb func
                      ___xhr(params, function() {callback(); f();}); // circular call back to f
                      }

                      Or pass the after-callback function as a parameter to the callback function
                      (again through use of an anonymous function).

                      or: `callback.returnfn = f; // now check property in that function and call
                      if needed

                      Or use a library like jquery and define a custom event, then bind all the
                      handlers to the event you want, and trigger it any time.

                      Circularity isn't required.

                      Erik

                      On Fri, Dec 31, 2010 at 12:12 PM, Felix E. Klee <felix.klee@...> wrote:

                      >
                      >
                      > On Fri, Dec 31, 2010 at 7:21 PM, Erik Eckhardt <erik@...<erik%40eckhardts.com>>
                      > wrote:
                      > > function callback(morefn) {
                      > > // do something ...
                      > > if (typeof(morefn) === 'function') {
                      > > moref();
                      > > }
                      > > }
                      > >
                      > > f = function () {
                      > > sendToServer('xyz', callback ? f : null);
                      > > };
                      >
                      > Are you sure this makes sense? The second last line will always evaluate
                      > to:
                      >
                      > sendToServer('xyz', f);
                      >
                      > And I don't see how this solves the issue I raised:
                      >
                      > var f;
                      >
                      > function callback(moreToDo) {
                      > // ...
                      > }
                      >
                      > f = function () {
                      > // ...
                      > };
                      >
                      > That's inconsistent, and thus confusing. Of course one could write:
                      >
                      > var callback, f;
                      >
                      > callback = function (moreToDo) {
                      > // ...
                      > };
                      >
                      > f = function () {
                      > // ...
                      > };
                      >
                      > Now things are consistent. But is that the way to go?
                      >
                      >


                      [Non-text portions of this message have been removed]
                    • Felix E. Klee
                      ... No, that was correct. In the example code, I *was* using a boolean. Only in the real code, things are more complex: Based on what the XHR returns some
                      Message 10 of 17 , Jan 4, 2011
                      • 0 Attachment
                        On Tue, Jan 4, 2011 at 7:53 AM, Erik Eckhardt <erik@...> wrote:
                        > I thought you were using a Boolean to indicate whether to make the
                        > return call to f.

                        No, that was correct. In the example code, I *was* using a boolean. Only
                        in the real code, things are more complex:

                        Based on what the XHR returns some calculations are performed, and - if
                        needed - another XHR is made.

                        > The point I was trying to get across is that you don't need the
                        > circularity.

                        What's bad about the circularity?

                        > xhr(params, function() {callback(); f();}); // circular call back to f

                        That would again be an infinite loop. The body of the anonymous function
                        would need to be at least slightly more complex. I don't think this is
                        increases readability.
                      • Erik Eckhardt
                        If your example isn t the full story, please post an example of the full story! One thing that s wrong with the circularity is the cross-dependence as JSLint
                        Message 11 of 17 , Jan 4, 2011
                        • 0 Attachment
                          If your example isn't the full story, please post an example of the full
                          story!

                          One thing that's wrong with the circularity is the cross-dependence as
                          JSLint is showing you. It's not a huge deal, but an annoying one, perhaps.

                          In any case I thought you might find it useful to use some of the techniques
                          I mentioned, where if a function determines that an additional step is
                          needed, it manages it within the function rather than making the callback
                          function have a hard coded reference back. The scheme you're using now seems
                          to make sense but as your application grows you may find that it becomes
                          unworkable. For example, what if you eventually have two functions that may
                          or may not need to be called after an xhr? Are you going to pass two
                          booleans? Or God forbid, a code indicating which functions to run? If from
                          the start you simply passed around single callback functions (which
                          themselves could be a string of functions) this problem would be solved.

                          function myxhr(url, params, callback) {
                          ___//do stuff;
                          doXhr(function() {myxhrreturn(xmlhttp, callback):});
                          }

                          function myxhrreturn(xmlhttp, callback) {
                          ___//process xmlhttp object into a result
                          ___callback(result);
                          }

                          function getsomething() {
                          ___myxhr('http://example.com', 'a=1', function(result) {
                          ______if (result.blah === 'gorp') {
                          _________dosomething;
                          ______} else {
                          _________dosomethingelse();
                          ______}
                          ___}
                          }

                          Now your getsomething function controls everything and all your xmlhttp
                          requests can be handled the same way without having to make a hardcoded
                          call. If you want the logic in the myxhrreturn function, you can put it
                          there, but it's not required. Here's another way to do it:

                          function getsomething() {
                          var cb;
                          if (somecondition) {
                          ______cb = function(result) {dosomething(result)};
                          ___} else {
                          ______cb = function(result) {dosomethingelse(result);};
                          ___}
                          ___myxhr('http://example.com', 'a=1', cb}
                          }

                          See how versatile this is? You only need a single callback, no extra Boolean
                          parameter is needed to indicate whether to call a hard-coded function name.
                          Any time you want more stuff to happen, you can load it into the passed
                          callback function somehow.

                          Erik

                          On Tue, Jan 4, 2011 at 1:47 AM, Felix E. Klee <felix.klee@...> wrote:

                          >
                          >
                          > On Tue, Jan 4, 2011 at 7:53 AM, Erik Eckhardt <erik@...<erik%40eckhardts.com>>
                          > wrote:
                          > > I thought you were using a Boolean to indicate whether to make the
                          > > return call to f.
                          >
                          > No, that was correct. In the example code, I *was* using a boolean. Only
                          > in the real code, things are more complex:
                          >
                          > Based on what the XHR returns some calculations are performed, and - if
                          > needed - another XHR is made.
                          >
                          >
                          > > The point I was trying to get across is that you don't need the
                          > > circularity.
                          >
                          > What's bad about the circularity?
                          >
                          >
                          > > xhr(params, function() {callback(); f();}); // circular call back to f
                          >
                          > That would again be an infinite loop. The body of the anonymous function
                          > would need to be at least slightly more complex. I don't think this is
                          > increases readability.
                          >
                          >


                          [Non-text portions of this message have been removed]
                        • Felix E. Klee
                          On Tue, Jan 4, 2011 at 7:35 PM, Erik Eckhardt ... There may be a misunderstanding. Thus, for your pleasure, below a more real-life
                          Message 12 of 17 , Jan 5, 2011
                          • 0 Attachment
                            On Tue, Jan 4, 2011 at 7:35 PM, Erik Eckhardt <erik@...>
                            wrote:
                            > no extra Boolean parameter is needed to indicate whether to call a
                            > hard-coded function name.

                            There may be a misunderstanding.

                            Thus, for your pleasure, below a more real-life example. Asides from
                            being more verbose, the only difference to my original example is the
                            addition of the parameter "liveCommentary". Together with the flag
                            "matchIsStillRunning" (formerly: "moreToDo") it forms the data returned
                            from the server.

                            var sendToServer; // defined elsewhere

                            function onCommentaryReceived(commentary, matchIsStillRunning) {
                            // write live commentary: ...
                            if (matchIsStillRunning) {
                            requestCommentary();
                            }
                            }

                            function requestCommentary() {
                            sendToServer('Berlin:Munich', onCommentaryReceived);
                            }

                            Naturally, one could rewrite this:

                            var sendToServer; // defined elsewhere

                            function requestCommentary() {
                            sendToServer('Berlin:Munich',
                            function (commentary, matchIsStillRunning) {
                            // write live commentary: ...
                            if (matchIsStillRunning) {
                            requestCommentary();
                            }
                            });
                            }

                            The second form avoids the circular function definition, but is it more
                            readable?
                          • Jordan
                            I think the confusion here is that you shouldn t be using the circular function pattern you are using. In addition, you are using function declarations
                            Message 13 of 17 , Jan 5, 2011
                            • 0 Attachment
                              I think the confusion here is that you shouldn't be using the "circular function" pattern you are using. In addition, you are using function declarations rather than assigning a function statement to a variable.

                              What you want to do, it seems, is immediately run requestCommentary after a matchIsStillRunning response comes back from the server.

                              What I can think of off the top of my head is:

                              var onCommentaryReceived = function (commentary, matchIsStillRunning) {
                              // write live commentary: ...
                              if (matchIsStillRunning) {
                              sendToServer('Berlin:Munich', onCommentaryReceived);
                              }
                              },
                              requestCommentary = function () {
                              return onCommentaryReceived(null, true);
                              };

                              Alternatively, and with less refactoring:
                              var onCommentaryReceived, requestCommentary;
                              onCommentaryReceived = function (commentary, matchIsStillRunning) {
                              // write live commentary: ...
                              if (matchIsStillRunning) {
                              requestCommentary();
                              }
                              };
                              requestCommentary = function () {
                              sendToServer('Berlin:Munich', onCommentaryReceived);
                              };

                              --- In jslint_com@yahoogroups.com, "Felix E. Klee" <felix.klee@...> wrote:
                              >
                              > On Tue, Jan 4, 2011 at 7:35 PM, Erik Eckhardt <erik@...>
                              > wrote:
                              > > no extra Boolean parameter is needed to indicate whether to call a
                              > > hard-coded function name.
                              >
                              > There may be a misunderstanding.
                              >
                              > Thus, for your pleasure, below a more real-life example. Asides from
                              > being more verbose, the only difference to my original example is the
                              > addition of the parameter "liveCommentary". Together with the flag
                              > "matchIsStillRunning" (formerly: "moreToDo") it forms the data returned
                              > from the server.
                              >
                              > var sendToServer; // defined elsewhere
                              >
                              > function onCommentaryReceived(commentary, matchIsStillRunning) {
                              > // write live commentary: ...
                              > if (matchIsStillRunning) {
                              > requestCommentary();
                              > }
                              > }
                              >
                              > function requestCommentary() {
                              > sendToServer('Berlin:Munich', onCommentaryReceived);
                              > }
                              >
                              > Naturally, one could rewrite this:
                              >
                              > var sendToServer; // defined elsewhere
                              >
                              > function requestCommentary() {
                              > sendToServer('Berlin:Munich',
                              > function (commentary, matchIsStillRunning) {
                              > // write live commentary: ...
                              > if (matchIsStillRunning) {
                              > requestCommentary();
                              > }
                              > });
                              > }
                              >
                              > The second form avoids the circular function definition, but is it more
                              > readable?
                              >
                            • Felix E. Klee
                              ... The question is: Why? Is that considered bad practice? If so, a reference please. ... See my original post. There I mentioned that I would like to enforce
                              Message 14 of 17 , Jan 6, 2011
                              • 0 Attachment
                                On Thu, Jan 6, 2011 at 6:27 AM, Jordan <ljharb@...> wrote:
                                > I think the confusion here is that you shouldn't be using the
                                > "circular function" pattern you are using.

                                The question is: Why? Is that considered bad practice? If so, a
                                reference please.

                                > In addition, you are using function declarations rather than assigning
                                > a function statement to a variable.

                                See my original post. There I mentioned that I would like to enforce
                                that convention for the whole project using JSLint. Only I wonder why
                                there is no such option.

                                > What I can think of off the top of my head is:
                                >
                                > [...]
                                > requestCommentary = function () {
                                > return onCommentaryReceived(null, true);
                                > };

                                That would work, but it's confusing to read. At least the function name
                                "onCommentaryReceived" should be changed.

                                > Alternatively, and with less refactoring:

                                See my original post. It's there already.
                              • Felix E. Klee
                                On Fri, Dec 31, 2010 at 6:33 PM, Felix E. Klee ... Just figured that this is akin to: http://en.wikipedia.org/wiki/Mutual_recursion And I
                                Message 15 of 17 , Jan 8, 2011
                                • 0 Attachment
                                  On Fri, Dec 31, 2010 at 6:33 PM, Felix E. Klee <felix.klee@...>
                                  wrote:
                                  > If I write code such as the following

                                  Just figured that this is akin to:

                                  http://en.wikipedia.org/wiki/Mutual_recursion

                                  And I checked: The latest version of JSLint, 2011-01-06, still
                                  complains about an undefined function.
                                • Douglas Crockford
                                  ... ` var sendToServer; ` ` function f() { ` sendToServer( xyz , function (moreToDo) { ` // do something ... ` if (moreToDo) { `
                                  Message 16 of 17 , Jan 8, 2011
                                  • 0 Attachment
                                    --- In jslint_com@yahoogroups.com, "Felix E. Klee" <felix.klee@...> wrote:
                                    >
                                    > If I write code such as the following, then JSLint complains that "f" is
                                    > not defined.
                                    >
                                    > var sendToServer;
                                    >
                                    > function callback(moreToDo) {
                                    > // do something ...
                                    > if (moreToDo) {
                                    > f();
                                    > }
                                    > }
                                    >
                                    > function f() {
                                    > sendToServer('xyz', callback);
                                    > }
                                    >
                                    > What's the most elegant solution to get rid of the error message?


                                    ` var sendToServer;
                                    `
                                    ` function f() {
                                    ` sendToServer('xyz', function (moreToDo) {
                                    ` // do something ...
                                    ` if (moreToDo) {
                                    ` f();
                                    ` }
                                    ` });
                                    ` }
                                  • Felix E. Klee
                                    On Sat, Jan 8, 2011 at 4:05 PM, Douglas Crockford ... Thanks for the suggestion! I guess I ll take this approach and put the lengthy do something into a
                                    Message 17 of 17 , Jan 8, 2011
                                    • 0 Attachment
                                      On Sat, Jan 8, 2011 at 4:05 PM, Douglas Crockford
                                      <douglas@...> wrote:
                                      > ` var sendToServer;
                                      > `
                                      > ` function f() {
                                      > ` sendToServer('xyz', function (moreToDo) {
                                      > ` // do something ...
                                      > ` if (moreToDo) {
                                      > ` f();
                                      > ` }
                                      > ` });
                                      > ` }

                                      Thanks for the suggestion! I guess I'll take this approach and put the
                                      lengthy "do something" into a separate function.

                                      Just thinking about it: I assume that having JSLint to *not* report an
                                      error on circular function definitions is non-trivial. That is, if one
                                      still wants to be warned about cases were an undefined function would be
                                      called, such as:

                                      function f() {
                                      g();
                                      }

                                      f();

                                      function g() {
                                      // do something
                                      }
                                    Your message has been successfully submitted and would be delivered to recipients shortly.