avatarEmma Boudreau

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

10283

Abstract

ctions, and what their technical definitions are, lets look at some examples of implementing closure functions in a few different languages. First on our list is Python, which is a bit of a weird case. Similarly to Java, Python uses the lambda syntax in order to define anonymous functions, which is quite handy. Just thinking about how often you have written an anonymous function inside of another function as a Data Scientist, it might shed some light on how useful these functions are. Also, all of the code I am about to show is going to be available by file inside of my random code repository, here is a link:</p><div id="fae8" class="link-block"> <a href="https://github.com/emmettgb/Random_Code/tree/main/closure_examples"> <div> <div> <h2>Random_Code/closure_examples at main · emmettgb/Random_Code</h2> <div><h3>Just a bunch of random blobs. Contribute to emmettgb/Random_Code development by creating an account on GitHub.</h3></div> <div><p>github.com</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/0*EU6EjoNqa_GqGccj)"></div> </div> </div> </a> </div><p id="7c2e">Anyway, here is our previous example replicated in Python:</p><div id="ffec"><pre><span class="hljs-keyword">def</span> <span class="hljs-title function_">example2</span>(<span class="hljs-params">n</span>): h = <span class="hljs-keyword">lambda</span> : <span class="hljs-built_in">print</span>(n)</pre></div><div id="f89d"><pre> <span class="hljs-function"><span class="hljs-title">return</span>(<span class="hljs-variable">h</span>)</span></pre></div><div id="313b"><pre><span class="hljs-variable">ourfunc</span> = <span class="hljs-function"><span class="hljs-title">example2</span>(<span class="hljs-number">5</span>)</span> <span class="hljs-function"><span class="hljs-title">ourfunc</span>()</span></pre></div><div id="3178"><pre>5</pre></div><p id="b10d">This example is quite easy to understand. We create the function h with lambda, then we return it. Python makes this really easy, it is mostly just understanding lambda to understand this syntax. If you do not have a solid understanding of lambda, by the way, I did write a whole article on it! Here is a link!:</p><div id="c888" class="link-block"> <a href="https://towardsdatascience.com/scientific-python-with-lambda-b207b1ddfcd1"> <div> <div> <h2>Scientific Python With Lambda</h2> <div><h3>The exact meaning a proper usage of Python’s Lambda function: Python’s greatest syntax for scientific programming.</h3></div> <div><p>towardsdatascience.com</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/1*-3pSkzHphelxvz7GwFG8FQ.jpeg)"></div> </div> </div> </a> </div><p id="12bf">Basically, all that lambda does for Python is allow the language to create anonymous functions. Julia also has this ability, there might be more ways to create them, but here are two examples:</p><div id="d97b"><pre><span class="hljs-function"><span class="hljs-title">f</span> = <span class="hljs-params">()</span> -></span> <span class="hljs-number">5</span> f()</pre></div><div id="6bab"><pre>5</pre></div><div id="edd2"><pre><span class="hljs-keyword">begin</span> <span class="hljs-number">5</span></pre></div><div id="e345"><pre><span class="hljs-keyword">end</span></pre></div><p id="e8da">The begin block will return the function to whatever is put before it, which is an important thing to note. I have a whole article on the concept of anonymous functions, which themselves are often times closures when used in the context of another function if you would like some elaboration on this:</p><p id="ec96"><a href="https://towardsdatascience.com/what-on-earth-is-an-anonymous-function-f8043eb845f3">https://towardsdatascience.com/what-on-earth-is-an-anonymous-function-f8043eb845f3</a></p><p id="64f3">When it comes to doing the same in C, unfortunately, we will quickly realize that it is somewhat hard to figure out the return type. However, this can be made a bit easier by using <a href="https://savannah.gnu.org/projects/libffcall/">GNU FFCALL</a>. However, there is a way you can do it without FFCALL, and it is pretty easy too, the biggest problem is getting that return type. Once again, here are our functions, this time replicated in C:</p><div id="6c86"><pre><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><stdio.h></span></span> <span class="hljs-function"><span class="hljs-keyword">typedef</span> <span class="hljs-title">int</span> <span class="hljs-params">(*fptr)</span><span class="hljs-params">()</span></span>;</pre></div><div id="4215"><pre><span class="hljs-function">fptr <span class="hljs-title">example2</span> <span class="hljs-params">(<span class="hljs-type">int</span> n)</span> </span>{ <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">h</span> <span class="hljs-params">()</span> </span>{ <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%d"</span>, n); } <span class="hljs-keyword">return</span> h; }</pre></div><div id="b7d7"><pre>int main() { fptr h <span class="hljs-operator">=</span> example2(<span class="hljs-number">5</span>)<span class="hljs-comment">;</span> h()<span class="hljs-comment">;</span> return <span class="hljs-number">0</span><span class="hljs-comment">;</span> }</pre></div><p id="09aa">Allow me to explain the code a bit here. We start by making a function pointer, *fptr, and then we use that as a return type, so we are returning a pointer to the function h. This works fine, and effectively cretes a closure in C. Funnily enough, a lot of people have used Apple’s C compiler for its “ block” syntax, and used FFCALL and even GCC extensions in order to achieve what can be done with this pointer definition. I am not sure there is a need to go further, as many of the examples that will further this in other languages are going to be very similar to the first two, just either with some more syntax around it, or a different call structure (with the exception of functional languages like SML, for example — but… <b>No</b>.)</p><h1 id="befa">A real-world use-case</h1><p id="3a15">Now that we are past looking at closures as a concept, allow me to show you an instance where I used closures in Julia to do something that was actually pretty awesome. The code I am about to showcase is a part of my Toolips.jl project. The project is pretty early on right now because there is just a lot on my plate at the moment, and the amount of other open-source packages I am also working on in my free time is <b>overwhelming.</b> The project is meant to be a modular web-development framework that works as both a back-end tool and a front-end tool, but does all of the front-end via meta-programming functional JavaScript. Anyway, if you are interested in viewing the project or maybe giving it a star (I appreciate that!) here is a link to the Github repository:</p><div id="ba94" class="link-block"> <a href="https://github.com/ChifiSource/Toolips.jl"> <div> <div> <h2>GitHub - ChifiSource/Toolips.jl: A Julia-Based JavaScript Compatibility Web-Development Framework</h2> <div><h3>toolips.js is built on a functionally programmed reactive observable library that evaluates via JavaScript expressions…</h3></div> <div><p>github.com</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/0*D9zEikDzDtxUbLhY)"></div> </div> </div> </a> </div><p id="49f6">Anyway, there are two core ideas that i wanted to portray with this package, and I think they are both great ideas for web-development.</p><ul><li>Data and returns should be function-ized,</li><li>Generator types should be stored and individually called for each service.</li></ul><p id="bdf9">With the first point I made here, it should be quite obvious that functions are used as data a lot. There is a similar technique to the ones that I use inside of OddFrames.jl used here to have some private methods, which are also closure functions, which is why I am showing them:</p><div id="454b"><pre>mutable struct ToolipServer ip::String port::<span class="hljs-type">Integer</span> routes::AbstractVector remove::<span class="hljs-keyword">Function</span> <span class="hljs-keyword">add</span>::<span class="hljs-keyword">Function</span> <span class="hljs-keyword">start</span>::<span class="hljs-keyword">Function</span> <span class="hljs-keyword">function</span> ToolipServer(ip::String, port::Int64) routes <span class="hljs-operator">=</span> [] <span class="hljs-keyword">add</span>, remove, <span class="hljs-keyword">start</span> <span class="hljs-operator">=</span> funcdefs(routes, ip, port) <span class="hljs-keyword">new</span>(ip, port, routes, remove, <span class="hljs-keyword">add</span>, <span class="hljs-keyword">start</span>) <span class="hljs-keyword">end</span></pre></div><div id="271c"><pre><span class="hljs-keyword">function</span> ToolipServer() <span class="hljs-keyword">port</span> = <span class="hljs-number">8001</span> ip = <span class="hljs-string">"127.0.0.1"</span> ToolipServer(ip, <span class="hljs-keyword">port</span>) <span class="hljs-keyword">end</span> <span class="hljs-keyword">end</span></pre></div><p id="4780">There is a lot to look at here, but the important part is the part I have made bold. This part creates the closure functions by calling funcdefs(), which looks like this:</p><div id="04c9"><pre><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">funcdefs</span>(<span class="hljs-params">routes::<span class="hljs-variable constant_">Abstract

Options

Vector</span>, ip::<span class="hljs-variable constant_">String</span>, port::<span class="hljs-variable constant_">Integer</span></span>) <span class="hljs-title">add</span>(<span class="hljs-params">r::<span class="hljs-variable constant_">Route</span></span>) = <span class="hljs-title">push</span>!(<span class="hljs-params">routes, r</span>) <span class="hljs-title">remove</span>(<span class="hljs-params">i::<span class="hljs-variable constant_">Int64</span></span>) = <span class="hljs-title">deleteat</span>!(<span class="hljs-params">routes, i</span>) <span class="hljs-title">start</span>(<span class="hljs-params"></span>) = <span class="hljs-title">_start</span>(<span class="hljs-params">routes, ip, port</span>) <span class="hljs-title">return</span>(<span class="hljs-params">add, remove, start</span>) <span class="hljs-title">end</span></span></pre></div><p id="2dcd">Because these are closure functions, in their respective definitions we are able to use all of the values which are defined in the original function that calls it. This is why the ip, port, and routes are all provided as arguments. The part of this code that references our next function is once again in bold, and here is that function:</p><div id="3e79"><pre><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_start</span>(<span class="hljs-params">routes::<span class="hljs-variable constant_">AbstractVector</span>, ip::<span class="hljs-variable constant_">String</span>, port::<span class="hljs-variable constant_">Integer</span></span>) <span class="hljs-title">server</span> = <span class="hljs-title">Sockets</span>.<span class="hljs-title">listen</span>(<span class="hljs-params">Sockets.InetAddr(<span class="hljs-params">parse(<span class="hljs-params">IPAddr, ip</span>), port</span>)</span>) <span class="hljs-title">println</span>(<span class="hljs-params"><span class="hljs-string">"Starting server on port "</span>, <span class="hljs-keyword">string</span>(<span class="hljs-params">port</span>)</span>) <span class="hljs-title">routefunc</span> = <span class="hljs-title">generate_router</span>(<span class="hljs-params">routes, server</span>) @<span class="hljs-title">async</span> <span class="hljs-title">HTTP</span>.<span class="hljs-title">listen</span>(<span class="hljs-params">routefunc, ip, port; server = server</span>) <span class="hljs-title">println</span>(<span class="hljs-params"><span class="hljs-string">"Successfully started Toolips server on port "</span>, port, <span class="hljs-string">"\n"</span></span>) <span class="hljs-title">println</span>(<span class="hljs-params"><span class="hljs-string">"You may visit it now at http://"</span> * <span class="hljs-keyword">string</span>(<span class="hljs-params">ip</span>) * <span class="hljs-string">":"</span> * <span class="hljs-keyword">string</span>(<span class="hljs-params">port</span>)</span>) <span class="hljs-title">return</span>(<span class="hljs-params">server</span>) <span class="hljs-title">end</span></span></pre></div><p id="b51b">The server is pretty basic at its current moment, but you can already see how the structure is coming along. What is the most important here is both the generate_router() call and the HTTP.listen() call. In HTTP.listen(), which is used to put this socket into an HTTP connection, we provide routefunc as an argument, which is the return of generate_router. This name alone, routefunc, could likely help one to assume what it actually does. The routefunc is the return of the function though, not the function. This is because the function is not static, it needs to change depending on the data that is provided to it. Here is a look at exactly what this looks like:</p><div id="bbf6"><pre><span class="hljs-keyword">function</span> generate_router(routes::AbstractVector, server) route_paths = Dict([route<span class="hljs-variable">.path</span> => route<span class="hljs-variable">.page</span> <span class="hljs-keyword">for</span> route in routes]) routeserver = <span class="hljs-keyword">function</span> serve(http) HTTP<span class="hljs-variable">.setheader</span>(http, <span class="hljs-string">"Content-Type"</span> => <span class="hljs-string">"text/html"</span>) full_path = split(http<span class="hljs-variable">.message</span><span class="hljs-variable">.target</span>, '?') args = <span class="hljs-string">""</span> <span class="hljs-keyword">if</span> length(full_path) > <span class="hljs-number">1</span> args = full_path[<span class="hljs-number">2</span>] <span class="hljs-keyword">end</span> <span class="hljs-keyword">if</span> fullpath[<span class="hljs-number">1</span>] ! in keys(route_paths) write(http, <span class="hljs-keyword">generate</span>(route_paths[<span class="hljs-string">"404"</span>]), args) <span class="hljs-keyword">else</span> write(http, <span class="hljs-keyword">generate</span>(route_paths[fullpath[<span class="hljs-number">1</span>]]), args) <span class="hljs-keyword">end</span> <span class="hljs-keyword">end</span> # serve() <span class="hljs-keyword">return</span>(routeserver) <span class="hljs-keyword">end</span></pre></div><p id="bd7f">I have put both the return and the definition of routeserver into bold so they are easy to see. The function is ran each time a request is made, and the HTTP I/O stream is provided by HTTP as an argument to this function. After that, all this function does is generate the page that corresponds to the uri that someone is requesting. On the surface it is really simple, yet on the outside it becomes really powerful. I think this is a great usage of a closure function, and furthermore, I think it was just a really cool approach to the problem of managing routes and things. I know some might be curious why it is that I decided to go with this technique, rather than using an HTTP.Router, or furthermore why I wanted to develop a web-server in the first place.</p><p id="df41">To answer the first question, it is a lot more difficult to handle many separate routes with the HTTP.Router. Furthermore, the handling of streams could not be anywhere near as clean as it is in this context, and in some cases, custom streams would not even be possible at all. To answer the second question, we are honestly really limited when it comes to web-development frameworks in Julia. We have the option of Mux.jl and Genie.jl when it comes to well-known packages for this task in Julia. Here is a look at Mux’s documentation:</p><div id="fba3" class="link-block"> <a href="https://docs.juliahub.com/Mux/cs9xb/0.7.4/"> <div> <div> <h2>Mux.jl</h2> <div><h3>Mux.jl gives your Julia web services some closure. Mux allows you to define servers in terms of highly modular and…</h3></div> <div><p>docs.juliahub.com</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/)"></div> </div> </div> </a> </div><p id="2e9f" type="7">Yes, that’s right, that is it.</p><p id="5fd5">For a while, Mux.jl was deprecated too, I thought — or maybe that was some other package. Anyway, while we have a nice WebIO/Interact.jl tie-in when it comes to web interactivity, WebIO mimes are somewhat slow to evaluate, which is perfectly understandable — but annoying. They are also much more targeted towards Scientific interactivity, which in all honesty is where I think that Interact.jl belongs. Interact is really really good at <b>that</b>. That is the packages application. Although there have been web-apps, e.g. Pluto.jl, that have been built on this, I think that Interact’s goal was never really to become the back-end for a fully-featured web-app (or maybe it was, I am not sure?)</p><p id="54a1">I was actually a very early adopter of Genie.jl. Genie is probably the best option for Julia web-development right now, there are just some things I wish were different about it. The biggest of these things is just that the whole file-system is one that is really hard to make lightweight. It reminds me a lot of the Django vs. Flask argument, it is a lot easier to say make an API with Flask than it is Django, but it is a lot easier to make a web-app with Django than it is in Flask. Mux.jl has a big focus on being a reactive-front-end tool, and I think it does reasonably well in that application. That being said, we do not really have any micro-frameworks that are truly micro. That being said, I seek to create this — along with a level of modularity that will allow it to be built upon with additional features.</p><h1 id="80a2">Conclusion</h1><p id="04f0">We might have gotten a bit off-topic while discussing Toolips.jl, but the point still stands, closure functions are incredibly useful. I think part of the reason they have been so useful to me is because Julia has such great syntactical expressions and function syntax. It makes it a lot easier to get these definitions out and put them to good use. Furthermore, the fact that you can just use functions as arguments everywhere, evaluate and expression-ize strings, Julia code, and more, all of this adds up to be quite a robust interface for writing functions of this nature. I also like Python’s implementation, I am sure as Data Scientists we have all used lambda before, it is just so handy for processing data, and if you ever used it inside of another function, then you were actually writing a closure function.</p><p id="9e62">I hope that this article was helpful in elaborating on the topic of closure functions. They are really cool, and can certainly come in handy in a lot of different instances. They are also really great for functional programming, which I suppose is a topic that I did not touch on much here, but I think the implications of that are pretty clear. All around, it is certainly something to know and furthermore take advantage of. Therefore, I am glad I was able to share it in detail with you! Thank you for reading this article, and have fun turning all your code into functions!</p></article></body>

What On Earth Are Closures?

An overview of closure functions and how to use them

(Image by jarmoluk on Pixabay)

Introduction

If there is one thing that is important to every programming language, no matter the paradigm of that language, it is functions. Functions are a universal idea that have been carried from language to language, which makes a lot of sense. Even when we are doing low-level programming with Assembly, we are still using what are essentially functions. Although I am not sure that they are necessarily called functions, as I have always known them as sub-routines (or routines if it is jmped and not called.) If you do not know what on Earth I just said, don’t worry…

It has nothing to do with closures.

My point is; functions are universal in programming. One of the first things you should probably pick up on whenever you go to learn a new programming language in the first place is how to write functions in that language. In some cases, functions can also be incredibly flexible, of course this fluctuates from language to language. In some instances certain syntax is possible, and in others it is not. One surprisingly powerful concept in computer programming is the concept of closure functions. Today I wanted to go over what exactly a closure function is, and why I think they are an incredibly useful technique. Today I will be using Julia, as long-time readers might have expected, but closure functions are entirely possible in many other languages, including (but not limited to):

  • Python
  • C
  • C++

Oh yeah, and SML, a language I am only vaguely familiar with; that implementation might be a little overboard, though, as there kind of are function definitions absolutely everywhere. Also, if you would like to watch me struggle to install that language, and then proceed to struggle every step of the way while programming it, even to a point where I could not figure out how to divide two integers, I have actually wrote an article about trying it out, which you may read here:

Please do not laugh at my despair.

What is a closure function?

In order to get started with understanding why closure functions are so great— especially for Data Science, we should probably start by defining a closure function. If you were to Google the term, you would likely yield an answer akin to

“ A closure is a lexically-scoped name binding with first class functions.”

That description really does not help if you do not understand what lexically-scoped means, or what first-class functions are. Luckily, these are just big names for simple concepts in computer programming. Well, maybe these concepts are not so simple to understand under the hood, but they are a lot easier to understand by looking at code than they are by this description. Lexically-scoped means that the function is able to access a scope that is not traditionally available to it. Consider the typical function scope,

function example_of_scope(x, y)
    mu = mean([x, y])
    x += 5
end

A function definition has a private scope. It can access the scope above it, in this example the global scope, or in Julia, “ Main.” This is because it is firstly a child of that scope, and secondly the scope of the global module Main is always going to be public. That being said, if we then moved below this function, and tried to call the function’s values, we would get an error:

function example_of_scope(x, y)
    mu = mean([x, y])
    x += 5
end

The scope is limited from the top-level scope, in this case the Main module, all the way down to our function, with each division containing more and more definitions. For example, consider we had a module called “ Tacos” inside of Main that held our function, our scope would then look like this:

module Main
# |Main scope
  | module Tacos
   # Tacos scope
     |
      function example_of_scope(x,y)...
end

This is lexical scoping, so basically — most of the languages you have likely used use lexical scoping, the alternative is a dynamic scope, which usually means that all of the name bindings are global. We can access definitions defined in Main and Tacos from example_of_scope, but we cannot access definitions defined in example_of_scope(x, y) from Tacos or Main. Remember, these terms are typically referring specifically to functions, and in most cases a language that is not lexically-scoped can be annoying to work with.

That is one piece of the puzzle down, so what are first-class functions? A first class function is simply a function that can be treated as an argument. In Julia, we can pass they type ::Function and take a function as an argument pretty easily.

example(f::Function, x::Int64) = f(x)

A closure function is a combination of lexical scope, and treating a function more like a regular type. In Julia, this is done very fluidly and with some pretty recognizable syntax, so it is actually a great language to demonstrate this with.

function example2(n)
    h = function hello()
        blahblahblah
    end
end

So now we have the function scope of h, which is defined within the function scope of example2. Remember, an important part of a closure function actually being called a closure function is that this scope needs to be lexical. What this means is that hello can access example2’s scope. All of the values from said scope are saved inside of the function’s definition. As an example, we could access n from within the hello function.

function example2(n)
    h = function hello()
        println(n)
    end
    h
end
ourfunc = example2(5)
ourfunc()
5

Closure functions have also been the back-bone of the object-oriented syntax inside of my package OddFrames.jl, and if you would like to view the context in which they are used in that package, you may look at the constructors’ source code here:

Implementations in different languages

Now that we understand the basics of closure functions, and what their technical definitions are, lets look at some examples of implementing closure functions in a few different languages. First on our list is Python, which is a bit of a weird case. Similarly to Java, Python uses the lambda syntax in order to define anonymous functions, which is quite handy. Just thinking about how often you have written an anonymous function inside of another function as a Data Scientist, it might shed some light on how useful these functions are. Also, all of the code I am about to show is going to be available by file inside of my random code repository, here is a link:

Anyway, here is our previous example replicated in Python:

def example2(n):
    h = lambda : print(n)
    return(h)
ourfunc = example2(5)
ourfunc()
5

This example is quite easy to understand. We create the function h with lambda, then we return it. Python makes this really easy, it is mostly just understanding lambda to understand this syntax. If you do not have a solid understanding of lambda, by the way, I did write a whole article on it! Here is a link!:

Basically, all that lambda does for Python is allow the language to create anonymous functions. Julia also has this ability, there might be more ways to create them, but here are two examples:

f = () -> 5
f()
5
begin
     5
end

The begin block will return the function to whatever is put before it, which is an important thing to note. I have a whole article on the concept of anonymous functions, which themselves are often times closures when used in the context of another function if you would like some elaboration on this:

https://towardsdatascience.com/what-on-earth-is-an-anonymous-function-f8043eb845f3

When it comes to doing the same in C, unfortunately, we will quickly realize that it is somewhat hard to figure out the return type. However, this can be made a bit easier by using GNU FFCALL. However, there is a way you can do it without FFCALL, and it is pretty easy too, the biggest problem is getting that return type. Once again, here are our functions, this time replicated in C:

#include <stdio.h>
typedef int (*fptr)();
fptr example2 (int n)
{
  void h ()
    { printf("%d", n); }
    return h;
}
int main()
{
    fptr h = example2(5);
    h();
    return 0;
}

Allow me to explain the code a bit here. We start by making a function pointer, *fptr, and then we use that as a return type, so we are returning a pointer to the function h. This works fine, and effectively cretes a closure in C. Funnily enough, a lot of people have used Apple’s C compiler for its “ block” syntax, and used FFCALL and even GCC extensions in order to achieve what can be done with this pointer definition. I am not sure there is a need to go further, as many of the examples that will further this in other languages are going to be very similar to the first two, just either with some more syntax around it, or a different call structure (with the exception of functional languages like SML, for example — but… No.)

A real-world use-case

Now that we are past looking at closures as a concept, allow me to show you an instance where I used closures in Julia to do something that was actually pretty awesome. The code I am about to showcase is a part of my Toolips.jl project. The project is pretty early on right now because there is just a lot on my plate at the moment, and the amount of other open-source packages I am also working on in my free time is overwhelming. The project is meant to be a modular web-development framework that works as both a back-end tool and a front-end tool, but does all of the front-end via meta-programming functional JavaScript. Anyway, if you are interested in viewing the project or maybe giving it a star (I appreciate that!) here is a link to the Github repository:

Anyway, there are two core ideas that i wanted to portray with this package, and I think they are both great ideas for web-development.

  • Data and returns should be function-ized,
  • Generator types should be stored and individually called for each service.

With the first point I made here, it should be quite obvious that functions are used as data a lot. There is a similar technique to the ones that I use inside of OddFrames.jl used here to have some private methods, which are also closure functions, which is why I am showing them:

mutable struct ToolipServer
    ip::String
    port::Integer
    routes::AbstractVector
    remove::Function
    add::Function
    start::Function
    function ToolipServer(ip::String, port::Int64)
        routes = []
        add, remove, start = funcdefs(routes, ip, port)
        new(ip, port, routes, remove, add, start)
    end
function ToolipServer()
        port = 8001
        ip = "127.0.0.1"
        ToolipServer(ip, port)
    end
end

There is a lot to look at here, but the important part is the part I have made bold. This part creates the closure functions by calling funcdefs(), which looks like this:

function funcdefs(routes::AbstractVector, ip::String, port::Integer)
    add(r::Route) = push!(routes, r)
    remove(i::Int64) = deleteat!(routes, i)
    start() = _start(routes, ip, port)
    return(add, remove, start)
end

Because these are closure functions, in their respective definitions we are able to use all of the values which are defined in the original function that calls it. This is why the ip, port, and routes are all provided as arguments. The part of this code that references our next function is once again in bold, and here is that function:

function _start(routes::AbstractVector, ip::String, port::Integer)
    server = Sockets.listen(Sockets.InetAddr(parse(IPAddr, ip), port))
    println("Starting server on port ", string(port))
    routefunc = generate_router(routes, server)
    @async HTTP.listen(routefunc, ip, port; server = server)
    println("Successfully started Toolips server on port ", port, "\n")
    println("You may visit it now at http://" * string(ip) * ":" * string(port))
    return(server)
end

The server is pretty basic at its current moment, but you can already see how the structure is coming along. What is the most important here is both the generate_router() call and the HTTP.listen() call. In HTTP.listen(), which is used to put this socket into an HTTP connection, we provide routefunc as an argument, which is the return of generate_router. This name alone, routefunc, could likely help one to assume what it actually does. The routefunc is the return of the function though, not the function. This is because the function is not static, it needs to change depending on the data that is provided to it. Here is a look at exactly what this looks like:

function generate_router(routes::AbstractVector, server)
    route_paths = Dict([route.path => route.page for route in routes])
    routeserver = function serve(http)
     HTTP.setheader(http, "Content-Type" => "text/html")
     full_path = split(http.message.target, '?')
     args = ""
     if length(full_path) > 1
         args = full_path[2]
     end
     if fullpath[1] ! in keys(route_paths)
         write(http, generate(route_paths["404"]), args)
     else
         write(http, generate(route_paths[fullpath[1]]), args)
     end
 end # serve()
    return(routeserver)
end

I have put both the return and the definition of routeserver into bold so they are easy to see. The function is ran each time a request is made, and the HTTP I/O stream is provided by HTTP as an argument to this function. After that, all this function does is generate the page that corresponds to the uri that someone is requesting. On the surface it is really simple, yet on the outside it becomes really powerful. I think this is a great usage of a closure function, and furthermore, I think it was just a really cool approach to the problem of managing routes and things. I know some might be curious why it is that I decided to go with this technique, rather than using an HTTP.Router, or furthermore why I wanted to develop a web-server in the first place.

To answer the first question, it is a lot more difficult to handle many separate routes with the HTTP.Router. Furthermore, the handling of streams could not be anywhere near as clean as it is in this context, and in some cases, custom streams would not even be possible at all. To answer the second question, we are honestly really limited when it comes to web-development frameworks in Julia. We have the option of Mux.jl and Genie.jl when it comes to well-known packages for this task in Julia. Here is a look at Mux’s documentation:

Yes, that’s right, that is it.

For a while, Mux.jl was deprecated too, I thought — or maybe that was some other package. Anyway, while we have a nice WebIO/Interact.jl tie-in when it comes to web interactivity, WebIO mimes are somewhat slow to evaluate, which is perfectly understandable — but annoying. They are also much more targeted towards Scientific interactivity, which in all honesty is where I think that Interact.jl belongs. Interact is really really good at that. That is the packages application. Although there have been web-apps, e.g. Pluto.jl, that have been built on this, I think that Interact’s goal was never really to become the back-end for a fully-featured web-app (or maybe it was, I am not sure?)

I was actually a very early adopter of Genie.jl. Genie is probably the best option for Julia web-development right now, there are just some things I wish were different about it. The biggest of these things is just that the whole file-system is one that is really hard to make lightweight. It reminds me a lot of the Django vs. Flask argument, it is a lot easier to say make an API with Flask than it is Django, but it is a lot easier to make a web-app with Django than it is in Flask. Mux.jl has a big focus on being a reactive-front-end tool, and I think it does reasonably well in that application. That being said, we do not really have any micro-frameworks that are truly micro. That being said, I seek to create this — along with a level of modularity that will allow it to be built upon with additional features.

Conclusion

We might have gotten a bit off-topic while discussing Toolips.jl, but the point still stands, closure functions are incredibly useful. I think part of the reason they have been so useful to me is because Julia has such great syntactical expressions and function syntax. It makes it a lot easier to get these definitions out and put them to good use. Furthermore, the fact that you can just use functions as arguments everywhere, evaluate and expression-ize strings, Julia code, and more, all of this adds up to be quite a robust interface for writing functions of this nature. I also like Python’s implementation, I am sure as Data Scientists we have all used lambda before, it is just so handy for processing data, and if you ever used it inside of another function, then you were actually writing a closure function.

I hope that this article was helpful in elaborating on the topic of closure functions. They are really cool, and can certainly come in handy in a lot of different instances. They are also really great for functional programming, which I suppose is a topic that I did not touch on much here, but I think the implications of that are pretty clear. All around, it is certainly something to know and furthermore take advantage of. Therefore, I am glad I was able to share it in detail with you! Thank you for reading this article, and have fun turning all your code into functions!

Programming
Julia
Python
C
Computer Science
Recommended from ReadMedium