| Hi astrobe_, interesting points! I disagree with you on a few things: > Looking at the C version, the argument "tile" is used once in the function, to get the pointer to the tile. The pointer could be passed directly: one less local. The argument "tile" is an 8-bit index, which is faster to pass than the 16-bit pointer to the tile. More importantly, that index is used for things other than looking up the tile pointer, such as finding color pairs to recolor the tile. In other words, you could pass the 16-bit tile pointer directly, but you'd still need a way to correlate that pointer with its matching color data, and that system would be slower than using the same 8-bit index for both. > x and y are passed only to calculate an address, this address could be passed directly That would be more efficient and save a handful of cycles when the function is called. This does not answer the original criticism, however. As I mentioned on the page, x and y go away after the pointer is calculated, so I'm not counting those as variables that need to be available in the loop body. You still have over a dozen variables that you can't manage efficiently in Forth. > t0 seems to be always 0, except when it is undefined. Good point! > The t_height, t_with are just offsets in the tile structure. This locals can go away too I don't think I see your point. If you're going for performance, doing one array access and saving the value saves a lot of cycles compared to indexing into the array each iteration, which eats a lot of cycles. > trans_row only exists to set skip_pixel, it seems one of them can go away. The logic seems to be "unless trans_row and something, do something". The C version might actually be more verbose than necessary. You could get rid of one of them at the expense of readability. Or maybe there is a good name that would indicate both purposes. > Finally, the edge_style complicates a lot the logic. Using one function to do different things because it is so simple to "just add another parameter" is typical in many HLL, and often result in awful spaghetti code. I don't think this function is awful spaghetti code. Yes, the logic is complicated. I mention on the page that I encoded the data for some tiles as 1 bit per pixel to save room and decided to make rounded transparent corners in code depending on a flag stored for each tile. This function is a compromise to save memory. Otherwise, you could just encode the tile like any other and have three colors including transparency but take up 8 times more memory. > Actually even in C some follow certain naming conventions - like #defines being all caps, member things being prefixed by m_, etc. And you can do that in Forth too. Once again, "write only" has more to do with the author than wit the language. I really disagree on this one! All caps and so on for constants is nice but still does not tell you how many values, if any, a word pops and how many it leaves behind. That alone earns the "write only" label. For example: CONST_A m_foo CONST_B m_bar. What does m_foo return and what arguments does m_bar take? Thanks for the comments! |
I believe you are victim of premature optimization. You knew Forth would be slow because interpreted, so you feared for performance. Fear often makes you do the wrong things.
The nice thing about Forth is that speed is often correlated to the number of words in a definition, so you can start writing for source/memory compactness all while partitioning the program into meaningful words as much as you can. Then assess the situation on the performance side.
> I really disagree on this one! All caps and so on for constants is nice but still does not tell you how many values, if any, a word pops and how many it leaves behind
Just like in Lua or Go you cannot tell how many values a function returns, and in most dynamically typed languages, what are the types of the input arguments. Furthermore the name alone cannot tell you about the behavior for corner cases either, like what happens if you pass a null pointer as a second argument to a string concatenation function: no-op or crash? You cannot tell until you check out the docs or read the source of the function.
Chuck Moore even claims that "stack pictures" (comments describing the stack effects of a word) are unnecessary; the effects should be obvious from the definition or if it needs more elaborate commentary, it should be documented somewhere else.
Documentation is something people don't take seriously. When you see that the whole documentation of a project consists in Doxygen files this is usually a bad sign. It might be pretty and nice at first glance, but there is more to documentation than documenting the interface of functions and dropping a few lines of introduction on top of that. Documentation should tell a story. And for this story to be well told, you cannot follow the layout of the code.
So to answer your question, what a word does should be obvious from the name or from its definition. Someone said naming things is one of the two hard problems in CS. This is where Forth shows you the real problems and demands thoughtful solutions. 47 characters-long names are certainly not the solution. Documented naming conventions and making sure a word does not do too many things are.