Warning: What follows is not useful!
While lying in bed one night many moons ago, I thought to myself: can you write a function in Go that counts how many parenthesis it was called with?
In particular, I was interested in seeing if I could count the number of
parenthesis pairs in a Go function call. For instance, imagine a function in Go
named c
, I wanted to know if you could get c()
to return 1, c()()
to
return 2, and so on.
We immediately face an obstacle: how do I write a signature for a function that
can be chain called indefinitely? c()
must return a function that returns a
function that returns a function that returns a function and so on. Sounds like
recursion.
After some tinkering, I found that Go supports recursive function type
definitions. You can define a function type that returns itself! I can create a
type called self
defined as a function that returns self
.
type self func() self
Now I can take a stab at implementing c
like:
func c() self {
return c
}
This will let chain calls to c
indefinitely like c()()()()...()
but doesn’t
actually count the number of calls and even if it was counting the number of
calls, how would I even retrieve that value? For us to be able to call
c()()
, c()
must return a function, but if c()
returns a function, it
cannot return a number. This makes it impossible to write a function
signature for c
such that c()()()()...()
returns an integer.
Okay, well that sucks but this wasn’t supposed to be practical, this was
supposed to be fun and confusing. I will allow myself to do something a little
different on the final call to c
that still makes it look like a bunch of
parantheses but is not necessarily another chained call.
For now, let’s use a global variable to track the number of calls to c
.
type self func() self
var count = 0
func inc() self {
count++
return inc
}
func c() self {
count = 1
return inc
}
Now I can count the number of chained calls, but I still cannot retrieve it from the result of the chain.
One interesting way to retrieve the count as a result of the chain is to attach
a function to the self
type that returns count
. Yes, that is right, you can
attach a function to a function type.
type self func() self
var count = 0
func (self) c() int {
return count
}
func inc() self {
count++
return inc
}
func c() self {
count = 1
return inc
}
This will now let us chain calls to c
indefinitely and retrieve the count as a
result of the function chain like:
c()()().c() == 4
This method for a function thing is interesting… but this ruins the aesthetic! Can we do something prettier?
We could implement another function that takes the function chain then simply
returns count
. Let’s call it C
to keep things confusing.
type self func() self
var count int
func c() self {
count++
return c
}
func C(self) int {
defer func() {
count = 0
}()
return count
}
So now we are able to wrap the chained calls to c
with a call to C
to
retrieve the number of times c
was called.
C(c()()()()) == 4
I find this format more inline with the original goal. This approach also
removes the need for the intermediate inc
function because the outer C
function is reseting the counter.
But what if we are using this in an extremely concurrent system? Using a global variable is a recipe for disaster. There would be so much lock contention. There has to be a way to achieve the same dumb result without using a global variable.
We could change the definition of self
such that we can pass the current count
as a parameter to future calls of c
.
type self func(i ...int) self
func c(i ...int) self {
count := 1
if len(i) > 0 {
count = i[0]
}
return func(i ...int) self {
return c(count+1)
}
}
But now we are stuck again not being able to retrieve the value of count
because it is part of the local function scope.
We can use a pointer instead of a value for count
when passing it to c
. This
way we can capture the value in the outer C
function.
type self func(i ...*int) self
func c(i ...*int) self {
count := new(int)
*count = 0
if len(i) > 0 {
count = i[0]
}
return func(i ...*int) self {
*count++
if len(i) > 0 {
*i[0] = *count
}
return c(count)
}
}
func C(fn self) int {
var count int
fn(&count)
return count
}
See how the function inside of c
sets the value of the pointer if one is
provided. This let’s us make another call to the self chain from C
to capture
the value of count
.
Now C(c()()()()) == 4
without using global variables.
I think I learned from this but I am not sure what it is going to be useful for besides making this blog.