Tell me that it isn't weird to write this https://en.wikipedia.org/wiki/Duff%27s_device
Tell me it isn't strange that array access is the same as pointer deref (aka a[index] is same as index[a])
Tell me that it isn't weird to write this https://en.wikipedia.org/wiki/Duff%27s_device
Tell me it isn't strange that array access is the same as pointer deref (aka a[index] is same as index[a])
It is very strange on first sight that a[index] is same as index[a], but you will never see the second one used in practice. When you are learning, you can write your code using normal array access syntax and solve problems. And then, you can learn to use pointers and deref them. And then one day you might realize that x[y] is *(x+y) and thus y[x] and that it looks funny. But that will be a long time after writing `wc`.
`index[arr]` means `*(index + arr)`.
Which is exactly the same thing as arr[index], no matter how you look at it. The type of arr is taken into account for pointer arithmetic.Whatever works with `arr[index]` will also work with `index[arr]`. You can also use a constant, for example `2[arr]`.
Damn I hate HN formatting.
Thinking more about this, this might fail if the compiler uses post-incrementing load/stores which update the pointer addresses too. Or will it not use them if I am using index[arr] ?
Lets say the load/store updates the pointer too. So after first execution, ptr will index+arr in both cases, but during the next iteration , the former will be ptr[index] while the latter will be ptr[arr].
You need to cast to another pointer type, do arithmetic on that, then cast back to produce an unaligned access.
Duff's device is an abuse of an obscure syntax option nobody expected, and it's also seldom used.
Foldr, on the other hand, is bread and butter in Haskell.
>Tell me it isn't strange that array access is the same as pointer deref (aka a[index] is same as index[a])*
I can tell you, it isn't strange.
In other words, if a is char[]/char* and index is int, C does
*((char*)a + (int)index) and *((int)index + (char*)a)
and not as one could expect *((char*)a + (int)index) and *((int*)index + (char)a)
for a[index] and index[a] respectively.But frankly it's a bad choice if we're talking about C's "esoteric quirks" because both this and Duff's Device are things that you effectively never encounter in the wild (it's the "Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo" of the C language, they're interesting to know about but not of much practical use).
There are much better "features" of C to single out, for instance the two million different meanings for the "static" keyword, the fact that arrays declared as function parameters do not behave like arrays but like pointers (hey, you can put "static" in there too now! But it still doesn't make it work like a proper array), the many surprising undefined behaviors regarding integer overflow, aliasing rules etc...
You could argue that operator[] should not allow a non pointer on the LHS, and you would probably right, but as it stands now, it is just syntactic sugar for operator+ and just dereferencing the while expression.
The reason a lot of people think that haskell's lazy/strict behaviour is quirky is only because they are not familiar with functional programming concepts.
Nobody bats an eye when an article talks about cache lines and how you organize data in a multi-dimensional array (zig-zagging one way is faster than the alternative). But this info is just as difficult to have learnt, 'cept that as a seasoned C programmer, you would've likely to have encountered this already.
the vast majority of programmers won't have any idea about any of that. Arguably (not by me BTW) they don't need.
edit: also, caches are a necessity of modern (and not so modern) computer architectures, every program has to deal with them. Laziness is sort of a self inflicted weakness of Haskell.
edit2: I can't write any Haskell, but I can read a bit of it (like python, it makes for excellent pseudocode I think). Sometimes I fail to understand how some algo could possibly work, but then 'ah, right, laziness'. I'm sure it comes natural after a while, but it is definitely a barrier.
Not even close. Pointer and dereference are something we do all the time in the real world (e.g. a pointer is like the semantic between word and thing) and can have easy to understand examples (the classic storage box with holds a value, and the pointer being the number of the box, etc). Still tons of programmers can't get their head around them...
Lazy/strict is far more challenging, and whereas pointers have no mystery after understanding the basic mechanism, telling how a lazy program behaves and spends memory etc is nigh impossible above a certain complexity -- heck, this is a common lament of seasoned Haskell programmers.
>Nobody bats an eye when an article talks about cache lines and how you organize data in a multi-dimensional array (zig-zagging one way is faster than the alternative).
Many bat an eye. Heck, many not even get pointers, much less cache lines, and optimal access/sharing patterns...
I don't follow, this is not really about programing languages and more about optimizing for a given architecture. Surely this problem would also arise if you're trying to optimize Haskell code? Or, for that matter, assembly, Go, Pascal or any language that lets you have low-level control on memory layout of your data.
It shouldn't be this easy to shoot yourself in the foot when doing simple tasks
But maybe I am biased due to learning more about how computer implementations fundamentally operate (in which case the only surprising thing about a[b] == b[a] is that the C standard allows it) than FP theory and how that abstraction can be baked down into something that the computer can actually execute efficiently.
But most let you avoid them in favor of concepts manageable by an average middle schooler.
Take a look at the optimized code for the C version of wc:
https://opensource.apple.com/source/text_cmds/text_cmds-68/w...
There are a couple special cases for a line count and a total character count, but it's essentially a couple of nested loops.
I agree with you that going deep into performance engineering brings out a lot of esoteric edge cases, but in this case for the C program it wasn't even necessary to go down that path. Basic concepts in C outran or at least matched something heavily optimized in Haskell.
I'm a big fan of higher level languages, and use them almost exclusively myself in my day job, but there's something to be said for basic constructs that just run quickly out of the box. It may be harder to optimize the C ode, but you're also less likely to need to do so.
The few times that I really needed performance out of a particular C/C++ function, I'd enable the compiler's output to generate ASM, and then see what was being generated, and either rework the code (to keep it readable for the next guy), or optimise directly with ASM, and often an iterative mix of the two. Loop unrolling (as in Duff's device) was a common one for array iteration performance improvements. I don't see a lot of "optimized C" in the wc impl. I'd imagine that wc optimised for use in a GPU would be pretty spectacular, even beyond using a few ordinary cores like the Haskell result did... but as you say, it's pretty unnecessary in this case.
Heh... I don't know that I qualify as an 'old timer', but it's been a while since 'whippersnapper' would've applied to me. :-)
> The few times that I really needed performance out of a particular C/C++ function
For me, at least, performance has almost never been a problem, particularly when writing in C.
> I don't see a lot of "optimized C" in the wc impl. I'd imagine that wc optimised for use in a GPU would be pretty spectacular,
Whenever I think of optimized C code, I immediately think of the work done to make Gnu Grep fast.
https://lists.freebsd.org/pipermail/freebsd-current/2010-Aug...
A lot of that boils down to picking an algorithm that lets them defer as much work as possible until you know you need to do it. I suppose that's one of lazy evaluation's claims to fame, but I think the level reached in Grep would be difficult to achieve in a more modern functional lannguage.