Forget the templates. Use lambdas. Part 3

This time we go to the Lambda Top Gear. Forget the templates. Use lambdas. Part 3, can actually hurt the brain. If not feeling safe or no chaperone present please leave now.

Lambda Top Gear
Lambda Top Gear. Forget the templates.

First and foremost I do think, I have not invented the core ideas in the code presented here.  But, I do not know the original author. If you are the one please let me know and I will state it clearly here.

LISP

Is a “LISt Processing language”. Importance (and beauty) of lists as a key concept in computing is proven way back in 1959. Here we will develop something very close to LISP Lists but using modern C++ lambdas and auto facility.

Thus, not LISP Lists but close approximation of them in modern C++.

Perhaps not an exact copy  (for that please use LISP) but still very functional and eminently usable C++ constructs.

For this journey you need one key ingredient: Open mind.

Mind open to functional concepts, functional programing and “all that functional jazz” you have been listening about. And yes, if you are coming from JavaScript by any chance that will help a lot. RUST or HASKELL too. And now …

The Deep Dive

Fig 0

First the usage

After this call my_list is an lambda. Exactly the one as declared inside  thelist() above. But. After the call it is with the defined closure around it. And that means  the arguments pack, the list() has been called with, is available to it as long as my_list variable exists. So presented in some kind of “lambda implementation pseudo code” the first call to the list would be leaving this situation on the stack after it has happened.

Fig 1

And the variable my_list is the lambda declared as in there above, returned.

Fig 2

Again this is a conceptual view on the lambda. Lambdas are implemented as functors but we will keep it short and sweet and still true without mentioning this too much.

Thus we have our list kept for us in the memory, on the stack.  Opposite to LISP list this is lambda with list atoms (aka elements) preserved and present as parameter pack arguments of the lambda call.

Note: depending on the compiler the lambda arguments are not unpacked as above but might be unpacked from the original list(1, '2', "3", false, 13.0f) call above when and if needed. We just show it this way to make it simpler for you.

Into the known

The three LISP basic blocks are: List, CAR and CDR.  CAR is “head of the list” and CDR is “the tail of the list”.

Thus the requirement for our modern C++ implementation, is this kind of usage:

Fig 3.

First the head()

Fig 4.

Ok. This is a real head hurting code. Let us do it slowly step by step. Remember what was returned as a result of  the list() call above. Yes, just a lambda. Fig 2.

head() call uses that as its one argument. This is the list_lambda in the head() code. And that is given yet another lambda as its only argument. Huh.

So in pseudo code head() returns the following:

Fig 5.

But. my_list is coming from inside the call list(1, '2', "3", false, 13.0f); which is kept on the stack since variable my_list is still here. So, the lambda 

Fig 6.

is the proc_lambda given to the result of  list(1, '2', "3", false, 13.0f); Again in pseudo code situation is this:

Fig 7.

Please understand that the Fig 7 is executed back inside the closure of Fig 1.

I am sure not everyone is happy with this explanations. Again please comment and we will sort it out for you. Now please follow the rest, and then revisit.

The tail() implementation

Fig 8.

Again hopefully the comments should help.  We have list, head and tail let’s make two steps more to increase the usability of Lambda Lists.

Fig 9.

By now, I think you are beginning to like it.  Yes, the list elements  are the params pack of the list call. The rest is just lambdas reaching to it and using it.

Next step is quite useful and conceptually interesting. It opens a door to the rest of the “normal” modern C++ code to use Lambda Lists.

Fig 10

We simply collect all the argument of the list call and return them as tuple set.  And there is a lot of in the std library that uses tuples and nicely operates on them. For example my dbj::print() known how to print the tuples but not how to print the list.

Fig 11

Would it be too cruel if we leave touple_to_list() implementation as an exercise to the reader 🙂

Dbj Lambda Lists
Dbj Lambda Lists in C++ notation

 

 

Leave a Reply