> Having the pointer "doing double-duty as a pointer and a counter" reduces the potential for bugs. If you do otherwise, then you have more variables to think about and they could get out of sync.
If you're just adding 1 to both each time then it's pretty hard to get that wrong. I think storing the original pointer and subtracting at the end has more potential to go wrong.
> I probably don't really get what you're looking for with the continuation stuff. Wikipedia compares it to setjmp/longjmp and goto, which doesn't sound too nice.
It's not well-explained - the terminology is arcane, people use the word to mean different things. But done right it can give you the performance and flexibility of goto but with the clarity of regular function calls, and all your control constructs can just be library functions.
> It's hard to get excited about learning to understand something that is associated with really slow programming languages.
Shrug. Almost all programming languages are fast enough these days. Correctness is often much more of an issue.
> Being "able to create a function at runtime" is normally incompatible with having a small high-performance runtime. In C you have a choice: fake it, or do things the awesome way. You can fake it with macros, possibly including ones that expand out to define functions. The awesome way is to JIT, and yes I've done this: allocate a chunk of memory, write out some binary instruction code, deal with any permissions/cache/pipeline issues, and then call it as a function or use a bit of inline assembly to reach it. Oh, there is another way that might be more your style: create C code on-the-fly, feed it into the compiler to generate a library, dlopen the library, and call your new function.
I don't need anything that complicated. Just the ability to partially apply a function would do. Or even ordinary classes. Which I mean sure, I can emulate by passing a struct consisting of a function pointer and the first argument as data, but that's going to be awful to read.
Functional programming throws away the "clarity of regular function calls" by abusing them or their syntax for control flow. It's an unreadable mess. Having control constructs be library functions seems to be an extension of this, making the situation much worse.
I write code to emulate the modern multi-GHz PC and other things running at about a GHz. It's timing sensitive, so the emulator needs to keep up, and we really want to emulate SMP on one core. My coworkers write code to treat a binary executable as a giant equation, solving it to find inputs which cause crashes. We pay lots of money for giant rooms full of computers running our code.
All programming languages are way too slow.
I'd switch to FORTRAN if it was 2x faster. I'm using C99 with lots of non-standard extensions and inline assembly. We use SSE intrinsics to vectorize things by hand.
Meanwhile, people like you are making my web browser too damn slow. It's 2016, and it feels like 1996. Other than a slight increase in pixels and always getting 24-bit color, there has been no improvement in speed despite the hardware being something like 1000 times faster. WTF. I paid good money for that hardware because I wanted things faster. I didn't buy that hardware to be wasted. Your code is not special. Each programmer tests his code in isolation, giving it the whole machine, but out in the wild it must share the machine with other code and/or with numerous instances of itself.
Sometimes gcc's optimizer will partially apply a function. I've seen the result via disassembler. This beats partially applying functions by hand. For example, the function may do different things if a certain parameter is NULL. The compiler makes a version of the function for when the parameter is NULL, and another for when it is not.
Passing a struct with a function pointer and argument seems a little annoying for a trivial situation, but it makes lots of sense when there are multiple functions and multiple arguments. For the trivial case, one would normally just pass the function pointer and argument separately.
I think I've written code that matches your "partially apply a function" idea pretty well, using macros. Like this:
There exist functions for reading/writing memory with names like read_mem_8 and read_mem_32, for several different sizes. I want to make wrapper functions called read8 and read32 and so on. The wrapper functions check for alignment, check a TLB, check for breakpoints, and then call the wrapped function or do the access directly. I make a macro that expands out to the function I want whenever I "call" the macro. No, I don't mean a regular function-like macro. Mine actually creates a real function. I invoke the macro itself at top level, outside of any function, right after creating the macro. I invoke it for each size that I want. Macro expansion generates a function that I can later call.
Note the lack of a semicolon. These macros expand to actual functions that I can call. I could even take the address of the resulting functions if I wanted to, not that I do.
Invoking the result of the macro expansion looks like so:
write32(cpu,addr,val);
For bigger things, one would put the template-like version of the function in a .h file and then include it repeatedly, each time with different choices. Like so:
#define CHOSEN_BIT_WIDTH 32
#define CHOSEN_ENDIANNESS ENDIAN_BIG
#include "def_stuff.h"
#undef CHOSEN_BIT_WIDTH
#undef CHOSEN_ENDIANNESS
// ... and then again with different choices
> Functional programming throws away the "clarity of regular function calls" by abusing them or their syntax for control flow. It's an unreadable mess. Having control constructs be library functions seems to be an extension of this, making the situation much worse.
Shrug - that's the opposite of my experience. When you're working in a world where functions are functions, you don't need explicit control flow - you just want to express what the return value needs to be, and have the computer do whatever it needs to to make it happen as quickly as possible. Restricting yourself to a call stack model is something you have to do for sanity in a language with pervasive mutability and side effects, but as a C programmer you surely know that it means giving up some performance. In a language where your functions are pure (which is often where you want to be for optimization anyway - compare GCC's SSA form) a lot of the need to explicitly reason about control flow goes away.
> Sometimes gcc's optimizer will partially apply a function. I've seen the result via disassembler. This beats partially applying functions by hand. For example, the function may do different things if a certain parameter is NULL. The compiler makes a version of the function for when the parameter is NULL, and another for when it is not.
I'm asking to express a partially applied function in code, not for performance but for clarity and readability.
> I think I've written code that matches your "partially apply a function" idea pretty well, using macros. Like this:
I don't want to do it at compile time, I want to do it at runtime. A super-trivial example would be something like:
def add(x: Int, y: Int): Int = x + y
def addX(x: Int): Int => Int = add(x, _)
i.e. I want addX(5) to return a (pointer to a) function that takes an int and returns an int that's that int + 5. How do I do that in C?
int add(int x, int y) { return x + y; }
int (*)(int) addX(int x) ???
I can return a struct that contains 5 and a pointer to add, but that requires whatever calls addX to know about the difference between this and a normal function.
Most programs aren't about a return value. They are about global state, including IO and huge data structures. The functional programming hacks for IO are gross. The issue of dealing with huge data structures is generally not solved; when you make a tiny change to a terabyte you have an awkward problem.
It's not standard C, but gcc will let you mark a function as being pure. Even without that, the compiler may notice.
I don't agree that expressing a partially applied function at runtime helps clarity and readability. I suppose tastes differ, and some people have bad taste. :-) The real trouble is that this is not something that is natively supported by the hardware. Your language either interprets a very unnatural machine, or it drags along a compiler.
Interpreting a very unnatural machine is unappealing. It distances you from reality, making it difficult to reason about how your code will perform on the hardware.
Dragging along a compiler is sometimes, rarely, justified. It's appropriate when the partially applied function must run for a long amount of time. Sometimes people even drag along a compiler for a different processor, such as a GPU. We find this to some extent with CUDA and OpenCL.
> Most programs aren't about a return value. They are about global state, including IO and huge data structures.
Not my experience at all. Most of the time if you're using a computer it's because you want to, well, compute. I/O happens but very often it's a simple matter that can be pushed to the edges, and the business logic that you actually need to be thinking about can be cleanly encapsulated.
> The functional programming hacks for IO are gross.
Only because I/O is gross. I find it's more a case of: functional style forces you to expose the ugliness that was always there underneath. Often you uncover things like the family of security vulnerabilities where an application creates a temporary file and then opens it and an attacker can replace it with a symlink in between. If you really want to say "do these I/O operations whenever, it'll all work out" then it's trivial to do that, even in Haskell (you just add "unsafePerformIO $" before all your I/O calls), but most people find it's actually worth explicitly figuring out which order things are supposed to happen in and doing that.
> I don't agree that expressing a partially applied function at runtime helps clarity and readability. I suppose tastes differ, and some people have bad taste. :-) The real trouble is that this is not something that is natively supported by the hardware. Your language either interprets a very unnatural machine, or it drags along a compiler.
Not at all. Every language with classes manages to do this (even Java can do it nowadays), C++ included. It's not hard to implement. What it does require is one more layer of indirection around function calls (if you're really bothered about the overhead you can make it opt-in i.e. "virtual" in C++). I could write my own code to do this in C, but a) I couldn't use the "function(arg1, arg2)" syntax because there's no way to override that (would have to do something like a macro, "VIRTUAL_CALL(function, arg1, arg2)") and b) it wouldn't interoperate with any of the rest of the ecosystem. You might not want to use it in every program, but this is the kind of thing that absolutely needs to be part of a language-wide standard for programs that do want to use it, so that libraries from different people can interoperate with each other.
(FWIW I think the idea of corresponding to the hardware is a false idol - or at least, if there is a way to achieve it, C isn't that. GCC generates something very different from a hand translation into assembly. Even assembly no longer corresponds to what the actual processor will do (microops). Modern processors are inherently parallel and no remotely mainstream language expresses that accurately.)
It's not as simple-looking, but that isn't all bad. It's very clear what is going on. The overhead isn't hidden.
Things needing call-back functions need to support an extra void* of course, and sadly it is often left out.
Understanding how compilers translate C into assembly is a skill that must be practiced. I suggest getting in the habit of looking at the binary. After a while, you will know what to expect. The optimizers are not magic. They do fairly predictable transformations on the code. You can get a feel for when hand-optimization matters, and when it is just pointless. An awareness of cache lines and TLB entries is helpful too.
> Things needing call-back functions need to support an extra void* of course, and sadly it is often left out.
Well when doing the right thing means writing more verbose code, it's no surprise that people do the quick-and-dirty thing (see http://www.haskellforall.com/2016/04/worst-practices-should-... ). Since "everyone knows" that best practice for a callback is to take a function pointer and a void * , wouldn't it be better to have that idiom built into the language? (i.e. a concise syntax for the pair of callback-and-void * , and a concise syntax for "invoking" the pair, passing the extra void * along with the "ordinary" arguments).
If you're just adding 1 to both each time then it's pretty hard to get that wrong. I think storing the original pointer and subtracting at the end has more potential to go wrong.
> I probably don't really get what you're looking for with the continuation stuff. Wikipedia compares it to setjmp/longjmp and goto, which doesn't sound too nice.
It's not well-explained - the terminology is arcane, people use the word to mean different things. But done right it can give you the performance and flexibility of goto but with the clarity of regular function calls, and all your control constructs can just be library functions.
> It's hard to get excited about learning to understand something that is associated with really slow programming languages.
Shrug. Almost all programming languages are fast enough these days. Correctness is often much more of an issue.
> Being "able to create a function at runtime" is normally incompatible with having a small high-performance runtime. In C you have a choice: fake it, or do things the awesome way. You can fake it with macros, possibly including ones that expand out to define functions. The awesome way is to JIT, and yes I've done this: allocate a chunk of memory, write out some binary instruction code, deal with any permissions/cache/pipeline issues, and then call it as a function or use a bit of inline assembly to reach it. Oh, there is another way that might be more your style: create C code on-the-fly, feed it into the compiler to generate a library, dlopen the library, and call your new function.
I don't need anything that complicated. Just the ability to partially apply a function would do. Or even ordinary classes. Which I mean sure, I can emulate by passing a struct consisting of a function pointer and the first argument as data, but that's going to be awful to read.