45

We've been in the process of changing how our AS3 application talks to our back end and we're in the process of implementing a REST system to replace our old one.

Sadly the developer who started the work is now on long term sick leave and it's been handed over to me. I've been working with it for the past week or so now and I understand the system, but there's one thing that's been worrying me. There seems to be a lot of passing of functions into functions. For example our class that makes the call to our servers takes in a function that it will then call and pass an object to when the process is complete and errors have been handled etc.

It's giving me that "bad feeling" where I feel like it's horrible practice and I can think of some reasons why but I want some confirmation before I propose a re-work to system. I was wondering if anyone had any experience with this possible problem?

Elliot Blackburn
  • 1,879
  • 2
  • 15
  • 16
  • 23
    Why do you feel like that? Do you have any experience with functional programming? (I'll assume not, but you should take a look) – Phoshi Aug 20 '14 at 09:48
  • 1
    @Phoshi I'll take a look into it thanks. I'm on my placement year from Uni so my experience really isn't very vast. It doesn't sit right with me because we were always taught that private functions can't be called from outside a class and if you want to expose it, it should be public. I can see the power in this method, but I didn't know if that was a good thing. I'll look into some functional programming and go with what this more experienced developer has done! – Elliot Blackburn Aug 20 '14 at 09:56
  • 8
    Then consider this a lesson! What academia teaches and what is actually useful often have surprisingly little overlap. There's a whole world of programming techniques out there that don't conform to that sort of dogma. – Phoshi Aug 20 '14 at 11:18
  • 37
    Passing functions to other functions is such a fundamental concept that when a language doesn't support it, people will go out of their way to create trivial "objects" whose sole purpose is to contain the function in question as a stopgap. – Doval Aug 20 '14 at 11:28
  • 40
    Back in my day we called these pass through functions "Callbacks" – Mike Aug 20 '14 at 14:29
  • 11
    @BlueHat read this http://en.wikipedia.org/wiki/Callback_(computer_programming) – Mike Aug 20 '14 at 14:29
  • 5
    The main reason for avoiding callbacks is that your language or library already supports a better alternative. For example in .net there is the `Task` class which you'd return from the asynchronous method. – CodesInChaos Aug 20 '14 at 16:14
  • @CodesInChaos In .NET, passing functions (a.k.a. treating functions as objects via delegates and delegate types) is used for other things besides asynchronous callbacks -- think LINQ and event handlers. From the [referenced Wikipedia page](http://en.wikipedia.org/wiki/Callback_(computer_programming)) -- _CLI languages such as C# and VB.NET provide a type-safe encapsulating reference, a 'delegate', to define well-typed function pointers. These can be used as callbacks._ but that is certainly not the only use of delegates. – Zev Spitz Aug 20 '14 at 22:29
  • 3
    @ZevSpitz I know, I have a pretty functional programming style. But the OP's example of passing callbacks to a function which run on completion of an asynchronous operation is one that I wouldn't use in .net since Tasks are nicer to use. – CodesInChaos Aug 21 '14 at 07:59
  • 5
    I've seen this taken to an extreme and misidentified as "functional programming", where you see 5, 10 or sometimes more more levels of indentation/callbacks, each using spec-like terminology to introduce dependencies, actors, interactions, etc. It's the furthest thing from practical, maintainable code I have ever seen and will never promote it. – Shaun Wilson Aug 21 '14 at 08:38
  • 4
    @ShaunWilson Of course people will also add 10 levels of indirection with Factories, Builders, Proxies, Strategies, Providers, Services, Managers, Adapters, Decorators, Chains of Responsibilities and convoluted inheritance trees and will call it "object oriented programming". You can write idiotic, convoluted code in any paradigm. – Doval Aug 21 '14 at 11:31
  • I'll comment that this is pretty much the standard in JavaScript. I use [Node.js](http://nodejs.org) (a server-side implementation of JS). Pretty much everything in Node is passing functions to other functions, (a)synchronicity between function calls, etc. @Mike is right about them being 'callbacks'. For some paradigms, passing around functions is the main control flow. Buuuut in OOP, I've never seen that. JavaScript is not *really* an OOP language, so I'm not so sure about this practice in say, Java or C#. Does Java even have higher order functions? No clue. – Chris Cirefice Aug 21 '14 at 15:10
  • 2
    @ChrisCirefice JavaScript *absolutely is* an object-oriented language; it’s not class-based, but “having classes” is *not* the definition of an object-oriented language, “having objects” (and being able to define your own etc. etc.) is. JavaScript has objects, which you can create, manipulate, borrow from (e.g. “inherit,” though you can go beyond inheritance), and so on. It’s object-oriented, it just implements that differently from most object-oriented languages. – KRyan Aug 21 '14 at 16:03
  • That's why I said it's not *really* an OO language. It's more of a prototypical language, in the sense that you define 'classes' through prototypes on objects. Granted, everything except for primitives are objects in JavaScript, I would call JavaScript a hybrid of prototypical, functional and dynamic. OO is not the first word that comes to my mind (it's the last). – Chris Cirefice Aug 21 '14 at 16:08
  • @ChrisCirefice There's no difference between passing functions around and passing objects that contain a single function around, except that the latter is a more convoluted way of doing the former. So to say you've never seen it in OOP languages without first-class functions is only vacuously true. Also, if you want to get really pedantic, classes aren't even objects; they're [abstract data types](http://www.cs.utexas.edu/~wcook/Drafts/2009/essay.pdf). Objects hide their underlying type, so the Java/C# equivalent is an `interface`. Now, what constitutes an OOP *language* is subjective. – Doval Aug 21 '14 at 16:44
  • @Doval Your last sentence was the point I tried to make in my last comment, mine probably not very well worded. My initial point was that I've never seen the main *control flow* being passing around functions in a more (reasonably assumed to be) OOP language like Java or C#. Usually you just call functions on objects (as you do often in JavaScript), but JavaScript, at least in the way that most web development goes, is usually functional (there's rarely a need to build prototypes in client-side code). That is, unless you're building a library. We could take this to chat to discuss further :) – Chris Cirefice Aug 21 '14 at 17:20
  • 1
    @ChrisCirefice Javascript is *really* an OO language. It's a prototypical language, which is a kind of OO language. – David Conrad Aug 21 '14 at 20:49
  • Well, these days I'm programming in JS functionally and doing a lot of clojure and elixir. So thanks to everyone for the thoughts, advice, and education! – Elliot Blackburn Apr 25 '18 at 10:26
  • Get rid of functions that take functions, and you get rid of a fundamental part of JavaScript. Similar situation with AS3, with is JavaScript's cousin, although the situation is more pronounced in JavaScript. – Panzercrisis Jun 18 '21 at 14:43

6 Answers6

90

It isn't a problem.

It is a known technique. These are higher order functions (functions that take functions as parameters).

This kind of function is also a basic building block in functional programming and is used extensively in functional languages such as Haskell.

Such functions are not bad or good - if you have never encountered the notion and technique they can be difficult to grasp at first, but they can be very powerful and are a good tool to have in your toolbelt.

Oded
  • 53,326
  • 19
  • 166
  • 181
  • Thanks for that, I've no real experience with functional programming so I'll look into it. It just didn't sit well because from my little knowledge, when you declare something as a private function then only that class should have access to it and public ones should be called with reference to the object. I'll take a look into it properly and I expect I'll come appreciate it a bit more! – Elliot Blackburn Aug 20 '14 at 09:53
  • 3
    Reading about [continuations](http://en.wikipedia.org/wiki/Continuation), [continuation passing style](http://en.wikipedia.org/wiki/Continuation-passing_style), [monads (functional programming)](http://en.wikipedia.org/wiki/Monad_%28functional_programming%29) should also be useful. – Basile Starynkevitch Aug 20 '14 at 09:58
  • 8
    @BlueHat you declare something to be private if you want control over how it is used, if that use is as a callback for specific functions then that is fine – ratchet freak Aug 20 '14 at 10:00
  • 5
    Exactly. Marking it private means you don't want someone else coming in and accessing it by name any time they like. Passing a private function to someone else because you specifically *want* them to call it in the way they document for that parameter, is absolutely fine. It need not be any different from passing the value of a private field as a parameter to some other function, which presumably doesn't sit badly with you :-) – Steve Jessop Aug 21 '14 at 01:54
  • 2
    Worth noting that if you find yourself writing concrete classes with functions as constructor parameters you are probably _doing it wrong_ tm. – Gusdor Aug 21 '14 at 07:24
  • These types of functions are also common in .NET, using lambda-expressions and delegates. – Mark Aug 22 '14 at 05:25
32

They're not just used for functional programming. They can also be known as callbacks:

A callback is a piece of executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at some convenient time. The invocation may be immediate as in a synchronous callback or it might happen at later time, as in an asynchronous callback.

Think about asynchronous code for a second. You pass in a function that, for example, sends data to the user. Only when the code has completed do you invoke this function with the result of the response, which the function then uses to send the data back to the user. It's a mindset change.

I wrote a library that retrieves Torrent data from your seedbox. You are using a non-blocking event loop to execute this library and get data, then return it to the user (say, in a websocket context). Imagine you have 5 people connected in this event loop, and one of the requests to get someone's torrent data stalls. That will block the whole loop. So you need to think asynchronously and use callbacks - the loop keeps running and the "giving the data back to the user" only runs when the function has finished execution, so there's no waiting for it. Fire and forget.

James
  • 556
  • 3
  • 12
  • 2
    +1 For using the callback terminology. I encounter this term much more often than "higher order functions". – Rodney Schuler Aug 21 '14 at 14:41
  • I like this answer, because the OP seemed to be describing callbacks, specifically, and not just higher order functions in general: "For example our class that makes the call to our servers takes in a function that it will then call and pass an object to when the process is complete and errors have been handled etc." – Ajedi32 Aug 21 '14 at 15:48
  • Protocols are better than callbacks. Protocols support long-term assumptions over a single interaction "surface" between two or more objects. Think like if you have two abstract interfaces looking toward each other. A bidirectional polymorphism. I haven't heard about a good implementation of protocols. For me, experienced for more than 25 years in many languages, this "higher-order functions" are the same as "goto". Harmful. – Brian Cannard Nov 08 '20 at 05:03
12

This is not a bad thing. In fact, it is a very good thing.

Passing functions into functions is so important to programming that we invented lambda functions as a shorthand. For example, one may use lambdas with C++ algorithms to write very compact yet expressive code that allows a generic algorithm the ability to use local variables and other state to do things like searching and sorting.

Object-oriented libraries may also have callbacks which are essentially interfaces specifying a small number of functions (ideally one, but not always). One can then create a simple class that implements that interface and pass an object of that class through to a function. That is a cornerstone of event-driven programming, where framework-level code (perhaps even in another thread) needs to call into an object to change state in response to a user action. Java's ActionListener interface is a good example of this.

Technically, a C++ functor is also a type of callback object which leverages syntactic sugar, operator()(), to do the same thing.

Finally, there are C-style function pointers which should only be used in C. I will not go into detail, I just mention them for completeness. The other abstractions mentioned above are far superior and should be used in languages that have them.

Others have mentioned functional programming and how passing functions is very natural in those languages. Lambdas and callbacks are the way procedural and OOP languages mimic that, and they are very powerful and useful.

6

As already said, it isn't bad practice. It is merely a way of decoupling and separating responsibility. For example, in OOP you would do something like this:

public void doSomethingGeneric(ISpecifier specifier) {
    //do generic stuff
    specifier.doSomethingSpecific();
    //do some other generic stuff
}

The generic method is delegating a specific task - which it knows nothing about - to another object that implements an interface. The generic method only knows this interface. In your case, this interface would be a function to be called.

Philipp Murry
  • 251
  • 1
  • 4
  • 1
    This idiom is simply wrapping the function in an interface, accomplishing something very similar to passing a raw function in. –  Aug 21 '14 at 04:37
  • Yes it is, I just wanted to show that this isn't an uncommon practice. – Philipp Murry Aug 21 '14 at 06:34
3

In general, there's nothing wrong with passing functions to other functions. If you're making asynchronous calls and want to do something with the result, then you'd need some sort of callback mechanism.

There are a few potential drawbacks of simple callbacks though:

  • Making a series of calls can require deep nesting of callbacks.
  • Error handling can need repetition for each call in a sequence of calls.
  • Coordinating multiple calls is awkward, like making multiple calls at the same time and then doing something once they are all finished.
  • There's no general way of cancelling a set of calls.

With simple webservices the way you're doing it works fine, but it becomes awkward if you need more complex sequencing of calls. There are some alternatives though. With JavaScript for example, there's been a shift towards the use of promises (What's so great about javascript promises).

They still involve passing functions to other functions but asynchronous calls return a value that takes a callback rather than take a callback directly themselves. This gives more flexibility for composing these calls together. Something like this can be implemented fairly easily in ActionScript.

fgb
  • 1,200
  • 2
  • 6
  • 10
  • I would also add that unnecessary use of callbacks can make it harder to follow the flow of execution for anyone reading the code. An explicit call is easier to understand than a callback that was set in a distant part of the code. (Breakpoints to the rescue!) Nonetheless, this is how events fire in the browser and other event-based systems; then we need to understand the event emitter's strategy to know when and in what order event handlers will be called. – joeytwiddle Aug 22 '14 at 08:06
-2

having arrays of pointers to functions is actually "best practice" for libraries, and is an absolutely critical and fundamental technique for modular Operating System design. places where the technique is used:

  • Python, (cpython)
  • Apache Portable Runtime Util (fundamental basis of apache2)
  • inside the code generated by c++ compilers for virtual object tables
  • the Windows NT Operating System
  • the linux kernel
  • samba's VFS dynamically-loadable modular API

and many many more. think about this: if you want to design a library that is fully portable and clean, can you use strcpy? can you use malloc? can you use free? what about printf? these are all functions that are part of libc6... so what happens when your code is used on a system where it's unsafe to assume that those exist, or they just simply don't exist?

the answer to that is, you:

  • declare a table of function pointers as a stable API
  • have a first entry in that table which says what version of that API is
  • you then ONLY ever extend that table at the end (you never CHANGE an existing entry)

then for e.g. running on any POSIX system you pre-populate that table with pointers to functions pointing to strncpy, memcpy, malloc, free, and pass that into your application, your library.

this is absolutely standard computer science, standard good practice, and is how Windows NT has managed to have a core base stable API for over... 30 years?

boost is the absolute antithesis of this practice, and as a result is one of the worst offenders in the libre/open world for stability and maintenance. it's common to have five separate versions of libboost installed and yet, when building software that uses it, be unable to successfully link.

lkcl
  • 1