For anyone curious, I built a project in C called tgc (https://github.com/orangeduck/tgc) which has some similarities. It's essentially a stand alone version of the Cello Garbage Collector (http://libcello.org/learn/garbage-collection) and provides garbage collection for stack and heap objects allocated via the given functions.
Yeah split stacks will be a problem. Essentially `tgc` just scans between the address recorded as the "bottom" and the one recorded as the "top". Both of theses can be manually adjusted if you have some better idea of these locations (such as your own spaghetti stack).
The registers are flushed to the stack before the mark is performed (using `setjmp`) so unless the behaviour of the platform is particularly odd in this case it shouldn't be an issue.
I really like this idea, but bezier curves are quite computationally difficult to use. There are many representations of these kind of continuous shapes which are much easier for this kind of processing.
For example I would try converting the bezier representation into an implicit HRBF representation (here is a good explaination in 3D http://rodolphe-vaillant.fr/?e=12). This representation should be much easier to process on the GPU - checking how much the point is inside/outside the glyph should only be one matrix multiplication which would make the computation in the actual shader really really fast.
At first I found it a bit confusion but they're actually pretty great. For me at least 90% of the tasks I want to do with regex can be done with scanf, and Lua's little extension covers the remaining 10% quite well.
I know lots of people often say that it would be nicer to use functional composition for regex instead of strings because strings are too confusing, but I disagree with this. The confusion of regex to me is not from the string representation, it is that some characters are "special" while others are "normal" (including whitespace). At first it appears that most characters are "normal" and so you can start from some example and generalize the string until it matches all the things you want - but once you start putting parenthesis and such in you start to realize that most of the string wont be matched "normally" and it is better to start thinking like a grammar and write it from scratch. This double thinking is pretty annoying.
For this reason, to me the Lua patterns are really the only alternative I've come across to regex that I've liked. They've got nice compact expressive syntax, can really easily do most of the matching tasks I need due to the scanf base, (almost) all the "special" characters begin with %, and the complex cases can still be matched.
I read the tutorial you linked and I see no difference between Lua patterns and regexes, except that '\' has been replaced with '%', and '*?' with '-'. The only thing in common with scanf is adopting '%' as control character, otherwise that's the same old regular expressions: '%d' matches just one digit, not whole signed integer like in scanf.
Author here. I always get asked two questions about libCello.
1. Why?
2. Is anyone using it for anything serious?
The second one is easiest to answer: no. And most people are suprised to hear that I probably wouldn't use it for anything serious either.
If you hadn't noticed from my github page I love C, and while I am also very interested in the things Cello adds such as duck typing and interfaces, and all the syntactic sugar that makes thing really nice and literate; it doesn't interest me enough to choose it over C. Additionally who is going to want to use my project if it uses this weird hacky C library! People are (for good reason) very suspect of dependancies.
That isn't to say I don't like programming in Cello. I'm almost definitely the person who has written the most in it and some things are just a joy to write in it, and look so clear and functional and algorithmic. At some point in the distant future when I find the time I really will attempt something serious such as a web framework. If that takes off we seriously can decide if it really is a good project (hur hur hur).
To be fair, "why" can also be pretty easy to answer depending on who is asking: because I could. Because I thought it was kinda cool and that people would be interested. There seems to be some default position in programming that unless your project is silly or sarcastic people assume you are "advocating" something by doing it, or making some kind of political statement on software development. I didn't work on this project to try and change the world. Nor to create something better than the alternatives. It doesn't change my life if people use Cello or not. I wasn't frustrated with C++, and I wasn't looking for a cylindrical rolly thing for my cart. I just made it for fun.
Well coming from an industry c programmer (embedded, networking, high throughput stuff, where c is the only choice), i am really thinking this could be of a great use. Specially for large products developed and maintained for many years and have big teams, this could be priceless if actually ported / extended. I would probably look at this and try to contribute when i can. Thank you so much, great work
The API allows 'var's to be treated as dynamically typed, so sure it is. More to the point, if you tried to change it to be statically typed, it wouldn't be expressible in the same nice-looking way in C.
I've also recently self published my book http://www.buildyourownlisp.com/ via createspace, having first converted it to an ebook and then to a print book, so I was really interested to see this article to read what was the same and what was different. I did speak to a couple of keen publishers, and I guess ended up self publishing for similar reasons. I wanted more control over the process and the final layout, and some publishers were not happy with having a free version provided online.
Creating the ebook was fairly easy. It ended up being a case of writing a bunch of scripts to fix and tweak various things about the HTML website. Constructing the print book was a lot harder work.
I explored rendering a PDF from the ebook but the font rendering was ulitmately too poor and the configuration of the ebook tools was getting really difficult. Additionally the text layout was not great with lots of code blocks getting split over pages and images in wrong places.
I knew I needed to use a program more suitable for publishing so I tried doing a manual conversion to Latex, but after the first couple of chapters I realized it was going to take too long. Instead I begun on some scripts converting my HTML into Latex automatically. Most of this work was done by `pandoc` and a list of regexes that redefines "unholy". God only knows how it all worked in the end. I found various Latex solutions for the pullouts, and syntax highlighting, that I liked ang which gave me as much control as I required, and in the end it was actually looking pretty nice.
Then came the copy editing, and like in this article it was long, tedious, difficult, and I'm still not completely 100% happy with the final result. It took the form of a bunch of scripts inserting pagebreaks before or after paragraphs and moving text, images and pullouts to balance the pages. Essentially it is very difficult to copy-edit a book with so many code blocks, even with the help of Latex. This is the only part of the process where I felt down on my skills, and that someone who does it for a living would not face the same problems (probably they wouldn't be using Latex in the first place). But in the end I was happy with the interiour - which still looked awesome printed in color - and I think I by far did the best I could.
I designed and made the cover in Photoshop to the vision of it I had always had. I think I did a fairly decently job but certainly it would have been better looked at by a professional. Unfortunately createspace doesn't seem to do a good job of printing the cover, so it didn't look precisely like the supplied image I made. Perhaps this is different when ordering non-proof copies.
The nice thing about my approach was that when people send pull requests and corrections to the website I can automatically integrate them across the ebook and print versions, and if I ever do want to make updates in future the process is relatively painless. I think I'd be happy to self publish again. Createspace was great and I felt good not having to rely on others for the process. The main thing I didn't like about it was the marketing - that is something that does not come naturally to me. I think it is unlikely I will write a programming book again. It was fun, but I felt like finishing it more of an obligation once the ball was rolling. Ultimately I think I made a "good" thing which lots of people read, enjoyed and appreciated, and I know it wasn't just an additional item of waste in the universe due to all the people who have reached out to me personally (thank you so much). But at the end of the day unfortunately it hasn't derived me satisfaction worth the effect it took to produce - which was a lot - and I think that is just due to the nature of the thing, not it's success or position in the world.
Oh, I just saw your post about this a couple of days ago. Your book looks beautiful! I was so tempted to do full color, but didn't think the finances made sense for my book. I'm totally jealous you went that route. :)
How did it end up working out? I assume it's process, not spot color. Is the colored text still sharp enough to be read easily?
> Instead I begun on some scripts converting my HTML into Latex automatically. Most of this work was done by `pandoc` and a list of regexes that redefines "unholy". God only knows how it all worked in the end. I found various Latex solutions for the pullouts, and syntax highlighting.
Smart! I considered this route a little bit, but I spent some of my formative years doing "desktop publishing" back when Aldus PageMaker was still a thing, so I looked forward to doing it a more hands-on, designery way. It worked out well for me -- the time I spent in InDesign was really enjoyable -- but I can definitely see how it wouldn't be the right choice for others.
> Essentially it is very difficult to copy-edit a book with so many code blocks, even with the help of Latex. This is the only part of the process where I felt down on my skills, and that someone who does it for a living would not face the same problems (probably they wouldn't be using Latex in the first place).
It's a relief to hear someone else say this. Trying to keep the code blocks from being split was really really hard. Halfway through the process, I started flipping through my copy of SICP to see how they did it. As far as I can tell, though guys are just absolute wizards. Every page is completely full and yet code blocks are almost never split across pages. Granted, Scheme snippets tend to be fewer lines of code, but they still did an amazing job.
> Unfortunately createspace doesn't seem to do a good job of printing the cover, so it didn't look precisely like the supplied image I made.
I've done some print work before, so I was fully expecting some variance here. Unless you have a carefully calibrated display and keep track of color profiles through the whole pipeline (and they do too), there's always some difference.
In my case, it came out surprisingly close to the image I sent, but I tried to keep the design pretty simple and not use too much detail or color.
> The nice thing about my approach was that when people send pull requests and corrections to the website I can automatically integrate them across the ebook and print versions, and if I ever do want to make updates in future the process is relatively painless.
Yeah, this part will be rough for me. The print version is basically a fork at this point. Any changes I make, I have to manually make in both the markdown (eBook + web) and InDesign (print).
> But at the end of the day unfortunately it hasn't derived me satisfaction worth the effect it took to produce - which was a lot - and I think that is just due to the nature of the thing, not it's success or position in the world.
For what it's worth, I'm really glad you made it, and I'll be buying a copy.
Thanks for the support, and congratuations to yourself on publishing. It looks like you've done a really great job and totally nailed the process and the print version!
> How did it end up working out? I assume it's process, not spot color. Is the colored text still sharp enough to be read easily?
Yeah it isn't spot color and the text looks really great and easy to read. The print quality of the interiour seems very high quality and it really makes it seem alive with the images and colored sections. I think it keeps it fun to read, which was always one of my intentions. I was totally over the moon with the interiour when the first proof copy arrived.
But as you mention financially it isn't going to make much sense. I don't make much more off the print book than the ebook sale, and yet it costs the consumer ten times as much! So in some sense it was kind of vanity project - but I still enjoyed it a lot, and wanted to prove I could do it additionally.
> It worked out well for me -- the time I spent in InDesign was really enjoyable -- but I can definitely see how it wouldn't be the right choice for others.
Actually if someone had mentioned InDesign to me I might have given it a try. I mainly picked Latex because I knew the font rendering and formatting would be awesome and it was something I knew already so I could get started. As you say, InDesign is fun, while working on a complex project in Latex isn't exactly a joy. But I think the end product was still sufficiently professional :)
> I've done some print work before, so I was fully expecting some variance here. Unless you have a carefully calibrated display and keep track of color profiles through the whole pipeline (and they do too), there's always some difference.
Yeah I probably should have been prepared for this. And additionally there was some error in how they were cutting the bleed which looked poor with my cover design. If I did it again I would definintely try to find a professional to consult before tacking the cover for any friendly advice he/she had to offer.
> For what it's worth, I'm really glad you made it, and I'll be buying a copy.
Thanks! I feel bad ending on a downer because I really have gotten a lot of pleasure out of it, so I don't want to paint the process in a negative light. As a learning experience it was really unique and great. There are just too many fun, great and unique areas of computer science to play around with as well!
I'm a big advocate of "worse is better" - in fact I put it in my bio even at the risk of scaring off potential colleagues or employers. The real meaning of "worse is better" is of course subjective and complicated. And as has been pointed out in these comments, "worse is better" is not about creating "worse" software and then marketing it as "better". It is something much more philosophical.
In my eyes "worse is better" is about the mindset of approaching a task. It is about diving right in and learning through production - without being paralysed by the idea of introducing hacks or ugly design. It is the idea that, for the moment, there isn't a need to be worried about covering every edge case or possible failure option. It persuades you to focus on something simple and easy to explain, with a single purpose or intent. It is better to produce something (anything) and see where it takes you.
It also says how important it is to embrace contribution and collaboration. How important it is to, after some threshold, release yourself from feelings of ownership.
But if I had to nail down exactly why I believe "worse" is so successful ("better") it is because those that create "worse" software don't focus on the software - they focus on the idea. From that the software is painfully drawn. The software might suck, but I believe ideas are better. They are more persistant, easily explored, dynamic, and shareable than software. Ideas that are good, simple, and easily taught are far more important than well designed software. That is why they survive.
There are lots of programmers and hackers who don't believe in "worse is better". Sometimes you seen them on HN with a fantastic new programming language (or something) they have designed and built in isolation - perfect in every aspect (at least to them). Nothing quite hurts like their confusion when interest dwindles and their software is forgotten. All they had seen on HN were "worse" links every day, and after years they had provided "better" - to them it is criminal that it hasn't been picked and gained momentum.
Worse is better is not going away, and I think you can either engage yourself in it as a philosophy, or struggle.
"Sometimes you seen them on HN with a fantastic new programming language (or something) they have designed and built in isolation - perfect in every aspect (at least to them). Nothing quite hurts like their confusion when interest dwindles and their software is forgotten."
I suspect you've described Rich Hickey to a tee here. /s
I think this demonstraights a real weakness of Haskell. Several times I've started a new project only to spend several hours trying to work out the correct way to structure what I want. Often it is hours before I even get my first successful compile. This is exactly as shown in this blog post. While some people enjoy this kind of tinkering, for me this really quickly saps all my motivation for the task. I just want to get something, anything, working. I work best jumping directly into a task and understanding it from the inside out, learning from my mistakes. Sometimes if I do find a quick and dirty way to get something working in Haskell - often refactoring it also becomes pretty difficult - which turns me off again. Neither of these are problems I have in other languages.
Arguments for which approach is better aside, it is no wonder lots of people who learn and approach tasks like me, feel a little betrayed by Haskell. I've used it for a bunch of things, I'm past the steep learning curve - but it seems I'm still not reaping the rewards. Is my philosophy of jumping into projects really so bad? Is it Haskell's place to question such an approach, when it has served me so well elsewhere?
It takes practice to be as fluent in FP as in imperative programming. When I've got the basics I still couldn't perform as quickly in Haskell as in other languages. Took me at least a year to become quick and apart from the weak library support (most things are a bit over the top for the beginners), I find Haskell amazingly productive. It's not _that_ different after you get used to it. Not having to write an other for loop ever again in a language which does't support generics alone worths it.
The language itself if you don't venture too far into the depths of the latest research is quite comfortable and clean. The fact that I can express my thoughts effortlessly in a few keystrokes and that the types (ADT, generics, typeclasses) are so descriptive makes it my favourite language for day to day tasks.
At least for the time being... until a dependently typed language becomes usable.
Haskell enforces a programmer to create structure. There's no way around it. And it's (nearly) impossible to hack things together without failing early and often.
As a result, before the problem you're trying to solve can be explored, you're stuck making guesses on how the problem should even be structured. You spend a few hours with one guess, it turns out to not get your far, and you try another.
The unfortunate part is that these guesses aren't letting you get to the meat of the problem effectively; you're stuck trying to solve a meta-problem.
Of course, once you have solved the meta-problem well enough, you can start exploring your problem. Unfortunately, you may find that your meta-solution doesn't actually let you answer questions you didn't know you wanted to answer from the get-go, and now you're back to square-one.
When everything is right, the program is usually very beautiful, safe, and—if you're skilled enough—efficient.
Perhaps not all Haskell programmers have this issue for very non-trivial programs, though I certainly do.
I don't really agree. The greatest structure in most of my Haskell programs is some kind of ErrorT or something. That requires me to structure the way I handle IO and the way that Errorable things happen, but beyond that, `a` is just as generic as any Python type. Sure, a lot of libraries do require you to accept certain premises, which may be a problem, but most of them provide ways to break out of their abstractions: ErrorT provides `runErrorT :: (Monad m, Error e) => ErrorT e m a -> m (Either e a)` which allows me to break out very easily, and due to the composable nature of Haskell, even if the library does not provide a function, it is often far from hard to write one yourself.
I agree that Haskell forces the programmer to create structure, but I don't agree with your meta-problem hypothesis. I think the structure actually is a big part of the real problem, and Haskell just prevents you from implementing incomplete or incorrect solutions.
Those solutions might in another language require a hack or leaky abstractions to work, if they would be possible to get to work at all.
I've walked into problems like the OP has before with Haskell, and when there seems to be no nice way of defining a type structure to model the problem, it usually meant that the idea in my head had a fundamental problem. If I look at this tree I get the feeling that perhaps he just has two problems, and he's looking at it like he's got only one.
And once you do get your type structure neatly in place, often times the implementation will just flow out of your fingertips. And when it does, it will be powerful, flexible and robust.
It's more like your weakness, and I don't see want it has to do with Haskell.
And if you really don't want to think about correct types just use the first came to your head. You can always change them later. And the compiler will tell you every single place your forgot to change.
I agree with this. If it's not natural for you to think ahead, create the structure you need before writing it up, Haskell is not the right language for you. It's good there are languages that fit your needs, nonetheless.
> If it's not natural for you to think ahead, create the structure you need before writing it up, Haskell is not the right language for you.
No, Haskell is probably even more fit here. Haskell is a godsend for prototyping! Because you throw any crap into the editor and with Haskell you'll know immediately what's wrong with said crap. And then when you refactor it later the compiler will guide and help you.
I feel like people just haven't got balls to see many compilation errors and prefer buggy, non-working, but “see! no errors, not like in those haskells!” code.
Please don't inject such language into technical discussions on Hacker News. The last thing we want is to swerve into low-quality flamewars.
Orangeduck, reikonomusha and others have posted fine comments in this thread. If they're wrong, the thing to do is show, not tell, that they're wrong. Then disagreement will make the discussion better instead of worse.
I think part of the problem is that buggy code that compiles produces instant gratification. You see results, even if incomplete. Maybe down the road you'll reach a dead-end due to a problem you didn't foresee, and the buggy code didn't help you find it because it compiled anyway, and you didn't have to think too hard about all cases or about the structure of the problem. And then it may be too late and you'll have to throw everything away and start from scratch.
But in the meantime, you get the feeling you're making progress, even if you're not. I think that's the psychology of the matter.
I'm a Haskell fan (and still a newbie, unfortunately) and I sometimes fall prey to this feeling.
There's a lot of study in psychology about how our mental models of our future selves result in the choices in we make in the present. I know quantifiably from having worked in a both ML family languages and the popular dynamic scripting languages that the amortized time I spend debugging the software is far less with the ML-style languages. Yet with the scripting languages there's definitely a psychological appeal of seeing something appear to "work" ( it really doesn't ) and then debugging it into existence, although the engineer in me knows that this is a really bad way to develop solid software in the long run.
So perhaps rapid prototyping and exploration should be done in a worse-is-better language like Python, then when the elegant structure is thought of, the code can be recast in Haskell?
Do you think to program or program to think? It seems like Haskell is biased to the former.
It's not uncommon for C++ programmers to first prototype in a simpler language like Python, figure out the structure, then reimplement in C++ for performance. No reason that can't be a thing with Haskell too.
Very true. I remember reading in StackOverflow where a guy commented that while programming in Haskell he was thinking more and programming less - he seemed to indicate that this was the most important thing. While design and structure is important, I think the reason he was "thinking so much" is that Haskell forces you to think non-trivially even for simple problems in many cases. One common argument made is that a loop is much more natural in many real-life scenarios rather than recursion or folds - so the mental model of the problem matches closely with the machine model - which is not the case when you are forced to use recursion.
I completely disagree. And I'd go so far as to say that a lot of that criticism comes from a lack of familiarity in functional programming/Haskell compared to imperative programming. The argument of a loop being more realistic is horrible imo, as both refer to performing processes, which are abstract concepts: my mind probably views these differently to yours, and most likely very differently to a lay person, who will never have had to consider the idea.
I gave a talk recently, and I briefly explained how to decode a protocol buffer varint: if the last bit of a byte is true, shift the first 7 bits and repeat the process on the next byte. Does that map better to
getVarInt :: G.Get Int
getVarInt = G.getWord8 >>= getVarInt'
where getVarInt' n
| testBit n 7 = do
m <- G.getWord8 >>= getVarInt'
return $ shiftL m 7 .|. clearBit (fromIntegral n) 7
| otherwise = return $ fromIntegral n
or
def read_varint(self):
run, value = 0, 0
while True:
bits = self.read(8)
value |= (bits & 0x7f) << run
run += 7
if not (bits >> 7) or run == 35:
break
return value
I'm perfectly happy saying it is equally well represented on both of them. I know I much prefer the first, but that's probably just because I prefer recursion to an explicit loop. From the sounds of it, you'd prefer the second, and I believe that's just because you prefer loops to recursion.
In my experience the vast majority of people find explicit loops easier to understand. Thinking in terms of a function that calls itself is a struggle for most people. FP advocates that really care about language adoption should be more willing to recognize that this is a hurdle and not wave it away as a personal preference, IMO.
In my experience, the vast majority of people do not learn a functional language to the point where they are productive in it. My hypothesis is that the correlation between people who finding explicit loops easier to understand and people who do not learn functional languages to the point where they are productive is evidence of causation.
In my experience, people who are ideological about FP often claim that the vast majority of people do not just get FP, and their preferences are misguided and not natural.
Yet, sequential and direct control flow are common in human language, we know how to tell or be told to do something N times. Recursive formulations are harder to explain, and most people don't learn math as easily as they do language.
It's all about precision. To tell someone how to do the dishes seems easy. But give the same instructions to someone who has never done dishes and you quickly see the problem. It goes the other way too. I dare you to try and explain a simple repeating algorithm in prose. No lists or numberings allowed.
Simple recursions work exactly the same as the equivalent iterations. They branch on a condition, execute a step and repeat. How state is handled is the key difference. As Haskell has no concept of a variable, the only way to rebind a name is to recall a function.
I was going to say that a professional in our field who has problems with recursion and the basics of discrete math should take a look in the mirror. But maybe we have managed to raise the abstractions high enough so that one can be productive without knowing the fundamentals of computing. I probably need to broaden my concept of a professional in our field.
Even if someone hasn't done the dishes before, contorting the explanation through recursion is not helpful. Recipes and instruction manuals are not recursive, and are rich in direct control flow and avoid indirect (higher order) control flow like the plague. Recursion for human discourse is just not used often, it is a relatively recent concept to us.
There is plenty of program writing to do that don't involve deep knowledge of maths, in fact I would say a vast overwhelming majority of work companies need to get done are not helped by, and possibly even hindered by, an intricate understanding and application of recursion (keep in mind, recursion is actually dangerous in strict languages). Most of us are more like police dectectives using well worn tooling and intuitive problem solving skills to get the job done. Sometimes we might even need the help of a mathemegician, but not most of the time.
Recipes and other manuals are also extremely vague when compared to computer programs. Humans have such vast amounts of context and prior knowledge available to them that pedantic instructions are not needed. The only prior knowledge the computer has is the rest of the program. For this reason I don't like the comparison of natural language and programming languages very much.
I would argue that we use recursion quite a lot. For example, here's how clean a bunch of dishes. If the bunch is no more, your done. Otherwise, take a dish and clean it. Then clean the rest of the dishes like you did with the first one. We often leave out the end condition as it is often clear from the context.
I'm not advocating a deep knowledge of math as I know from experience that it has very few applications in software development. But to me a professional is someone who is not just skilled but knows the history and fundamentals of his trade. He knows not just that for (i = 10; i > 0; i--) terminates but also has an idea why that is so (and what it means that a program terminates).
Why would recursion be any more dangerous than iteration?
That is not recursion, but basic procedure call; from wiki:
> Recursion is related to, but not the same as, a reference within the specification of a procedure to the execution of some other procedure. For instance, a recipe might refer to cooking vegetables, which is another procedure that in turn requires heating water, and so forth. However, a recursive procedure is where (at least) one of its steps calls for a new instance of the very same procedure, like a sourdough recipe calling for some dough left over from the last time the same recipe was made. This of course immediately creates the possibility of an endless loop; recursion can only be properly used in a definition if the step in question is skipped in certain cases so that the procedure can complete, like a sourdough recipe that also tells you how to get some starter dough in case you've never made it before. Even if properly defined, a recursive procedure is not easy for humans to perform, as it requires distinguishing the new from the old (partially executed) invocation of the procedure; this requires some administration of how far various simultaneous instances of the procedures have progressed. For this reason recursive definitions are very rare in everyday situations.
Recursion quickly causes a stack overflow when you get it wrong in a strict language (and sometimes even when you get it right!), iteration does not (rather, the CPU just spins and memory is safe). Also, iteration is intrinsically less expressive than recursion, meaning it follows to use it via the principle of least force.
Recursion can cause stack overflow in lazy language too. But lazy or strict, that's more of an implementation detail although a very visible one. An iteration that appends to a list will run out of memory just the same. I also don't see how a recursion that runs out of stack space is any more dangerouse than an iteration that goes to an infinite loop.
That is just a loop in my opinion; it would be easier and more clear to express it as a loop. The power of recursion isn't really necessary, nor is it somehow more enlightened.
Exhausting your call stack in most languages is really bad as far as debugging is concerned: it loses the ability to even form a decent error message (stack overflow), and you are left with poor content for diagnosing the problem. Exhausting the heap or having an infinite loop, in comparison, are vastly easier to debug (the latter being much easier than the former, thrashing the VM system is also a pain in the arse, though less so than exhausting the stack).
The problem is a possibly little more fundamental, though. Explaining a looping function to someone can be as easy as "think of how you do the dishes, you take one... repeat until there are no dishes/soap/water/room left."
This is very close to how every repeating process is taught and understood by folks. You take something, and repeat until there is a specific condition. Compared with a recursive solution. Which is of a few forms. One being to take your problem, break it into a series of identical problems and constantly restart with the results of having run a previous problem.
I grant that a recursive solution is not that much more difficult to phrase this way. "Take a dish, clean... start over unless there are no dishes/soap/whatever left." I just don't know if I have ever heard something stated this way.
I don't understand where you have "run += 7" and break if run = 35 in the python code. Does the Python code have more error checking than the Haskell code?
The Haskell code checks this implicitly with the use of the Get monad, I believe. In Python, you have to manually tell the code to not reach for bits that aren't there. In Haskell, the concept of a failure state being implicitly dealt with is used all over.
Also, the reason the Python code is a `while True` with an explicit counter and check at the bottom is because Python lacks a do-while loop
Huh, yeah, didn't notice that implementation difference. In the Python code, the longest valid varint is 5 bytes (want the result to fit into int32 I guess?). In this case the bug is not strictly a problem as the Haskell code is used to read varints from trusted inputs only, whereas the Python is designed to read untrusted input. But good catch
I think the real problem here, and it is a true problem with Haskell development, is perfectionism.
Code in Haskell can be so elegant, and examples of beautiful code are so abundant, that hacking together a bunch of hacks to see if they work doesn't seem like a conceivable way of doing things.
But of course, Haskell can be used that way to figure out and explore your problem. You can throw IORefs around, do everything with ugly IO effects, pass tons of parameters around, etc.
Perfectionism is a problem I had with Scala awhile back. My penance was to program for the next 7 years in C# (at least I didn't go back to java). Sometimes no hope for achieving elegance can be empowering in getting things done.
I don't think Haskell is at all designed for quick and dirty prototyping and exploration. The whole library and tool mindset is against that kind of developer activity.
I've never found a language better suited to quick and dirty prototyping and exploration than Haskell. I was part of a small team that built a core web service from scratch in Haskell. We had no idea what we were doing (feature-wise) when we started, and just put some stuff down. Make it compile, see how writing the next bit of code works out. If it's terrible, refactor. Every part of the Haskell toolchain is about empowering refactoring. Not with silly automated tools - with a set of language features that tell you when you didn't think about something.
Even after the service was up and handling hundreds of requests per second (yes, really - it was hardly the company's first foray, and we had a lot of customers lined up to use this as soon as it went live) we often found that the current design was severely hindering a new feature we wanted to add. So we'd just change the design. It wasn't uncommon for an update to require changes to 20% of the lines in 75% of the files. And it was no big deal. The compiler had our backs, and made sure we made all the changes in a way that made sense. We didn't always get it right - sometimes the system tests caught things the compiler missed, and on rare occasions a bug even slipped into production. Such is life in a startup, right?
But here's the thing. At no point in the process was there a monolithic design done ahead of time. In fact, it can hardly be said that there was any designing done ahead of time. Pretty much all the design work was a result of aggressive refactoring when adding new features. The whole process over years of development was nothing more than turning a quick and dirty exploratory prototype into a real, solid production system.
For contrast, an associated service was written using Rails, because we thought it'd be easy for what that service needed to do. It ended up being a nightmare to refactor and add new features. We were just never sure we got all the mechanical cases when something deep needed to be changed. Between the two, there's just no question which system was better when you start out not having any clue what the product needs to be.
There is more to quick and dirty programming than refactoring. I would say refactoring even doesn't play much if a roll here. What is key is the ability to be in a broken state for long periods of time while still being able to test and executed pieces of the system. This doesn't work in Haskell since Haskell is not just statically typed, it is Statically Typed! Type inference and code generation are slaves to types, and don't handle errors very well. That could be fixed, but it doesn't seem to be a priority (generate code Forman incorrect program seems to be taboo).
More crucially, Haskell programmers tend live in their compiler and not their debugger. The adage "well typed programs don't go wrong" means you spend a lot of time just getting the code to compile, fitting types together, and not much time seeing how things work together. Much of this is because the community emphasizes the compiler, and in fact good graphical debuggers for Haskell aren't there yet (and even difficult to design given lazy evaluation). Bret victor didn't do his inventing in principle talk with Haskell for good reason!
Python and ruby. programmers live in the REPL and debugger, they don't get to observe type errors early like Haskell programmers do, but they immediately get to see real interactions. I can then see why going between Haskell and ruby would be a disaster.
You can compile haskell programs with type errors (in GHC, anway) if you want. They crash when you attempt to execute code that has a type error, but if that's what you want, feel free to use it.
It's not something I've ever needed. It's got little to do with quickly exploring design space.
Edit:
I guess I should include a couple words about why it's not really necessary in real coding. A side effect of good module boundaries is that you can test breaking changes in isolation in the REPL. Change a few things that are tightly coupled at the same time, load their module, don't worry about fixing any other modules until the first one works the way you like. In practice, this means you almost never need the option to defer type errors to runtime.
"real interactions"? It is disingenuous to imply that all Haskell programmers do is see type errors while Python and Ruby programmers have 'real interactions'.
For instance, today I wrote tons of stuff in the IO Monad and had what you call 'real interactions'.
Also, more time spent in the debugger doesn't necessarily mean a faster working program. The psychology of it is more rewarding, but there's nothing necessarily more "real" about it.
Macros in C can sometimes be nasty, but "min" is a pathological case for a macro because it isn't what macros were meant for.
In reality "min" should just be a function, and you should write a separate function for each type you want to define it over. This is how this problem is solved in C, and almost all other languages. It just looks deceptive because C's native operators like "<" and ">" happen to be generic across some number types. This is just for convenience, it isn't how C is meant to be programmed. Pretty much every other language also makes you define different implementations of functions when you have a different type you want it to work on. Lots let you type the same function name, independent of the type - via generics, interfaces, or type-classes - but all call out to different implementations at the end of the day. Very few can derive those implementations.
As far as I am concerned, Macros in C are best used just to save on typing. They get a lot of hate because the syntax looks like functions - so people try to use them as functions. I don't recommend using them as functions, instead use them to save typing - because saving typing and avoiding repeating yourself can only reduce errors.
"min" is a pathological case for a macro because it isn't what macros were meant for.
Tell that to Kernighan and Ritchie. The second macro in K&R (I googled a PDF of the second edition) is
#define max(A, B) ((A) > (B) ? (A) : (B))
Yes, that's lowercase 'max', and they even mention "This macro will work for any data type; there is no need for different kinds of max for different data types, as there would be with functions"
Macros certainly were intended for this, as it was the only way to guarantee that the compiler inlined code (probably the only way it ever inlined function calls)
And for the curious: the first macro they give is
#define forever for(;;)
That certainly is what macros were meant for :-), and doesn't even save on typing.
But yes, in modern C, nobody would use a macro for this.
The issue is for some programmers propensity to use ++ and -- whenever they can. So you end up with
max(a++, b--)
Which expands to
((a++)>(b--)?(a++):(b--))
creating unexpected results. The solution is, don't use return values of `++` and `--` in calls, unless you know it's a function, and always will be. Upper-case macros may help, but then you need to make sure all macros are upper case.
> In reality "min" should just be a function, and you should write a separate function for each type you want to define it over. This is how this problem is solved in C, and almost all other languages.
This is not how it's solved in any languages with any degree of polymorphism. Of course, C is entirely devoid of polymorphism – except for its math operators like `<` and `+` – so having a version of `min` for every type you ever want to take a min of is how you're forced to do things. But no, `min` is not pathological – it's completely normal for math programming. If you look at math libraries written in monomorphic languages like C and Fortran – BLAS and LAPACK, or hell, just math.h – they are full of multiple nearly identical versions of functions that take different argument types and begin and/or end in different letters to indicate these types: `round`, `roundl`, `roundf`, etc. In LAPACK, it's completely conventional – "s" for single float, "d" for double float, "c" for single complex, "z" for double complex [1]. You'll note that the LAPACK naming scheme also encodes the type of matrix it operates on (there are 28 of them).
That's all fine, right? Well, it's fine if you're ok with writing every function four times. And that's only dealing with polymorphism in one argument. Hint: it's not at all uncommon for math functions to take multiple arguments. This is precisely why the first thing that a principled approach to mathematical programming does is allow polymorphism – ideally on multiple arguments. This is why people do math work in dynamic languages with lots of polymorphism, like Mathematica, Matlab, R and Python. Otherwise you can't get any work done without writing everything dozens of times – or struggling with C macro programming that makes this min puzzle look utterly tame. Sussman et al.'s SICM (SICP but for classical mechanics), not content with a dynamic language, immediately builds a multiple dispatch system on top of Scheme. R supports an awkward multiple dispatch system in one of its four object systems. This is why Guy Steele included multiple dispatch in Fortress. This is why Julia has multiple dispatch as its core paradigm. You need it for math. Badly.
exactly my attitude towards them - modern compilers will generally optimise simple functions like "min" so that they're just as efficient as macro equivalents.
For example, for a project that I did, I needed specific access methods for each member of a struct, and for each method to be written with a separate function. With macros, it was as easy as defining a macro to generate said function (given the member name), and then calling said macro for each member to generate the code.
So I get it, and it makes a good point, but this could be more of a symptom than a disease.
When I first started programming C I also tended to just exit the program on getting a bad return code, because I'd never been taught how to properly handle errors - I'd always just replied on exceptions.
Only later did I start to get bitten, and take proper care to handle errors sensibly and explicitly.
As "go" is such a new language I wouldn't be surprised if a similar thing has happened to new programmers migrating over to it.
I find that I wind up "bubbling up" errors of my own to build a stack trace yet still retain control of my program, like:
func LowLevelCode() (int, error) {
// rest of body...
if /* error condition */ {
return 0, errors.New("LowLevelCode error: " + /* error description */)
}
}
func MiddleWare() (interface{}, error) {
// body of code...
nCount, err := LowLevelCode()
if err != nil {
return nil, errors.New("MiddleWare error: " + err.Error())
}
}
func ApplicationCode() {
// Do cool stuff...
obj, err := MiddleWare()
if err != nil {
log.Println(err) // Prints "MiddleWare error: LowLevelCode error: " + ...
// Handle on a high level, retry connections, etc.
}
}
I'm hoping someone comes in and corrects me with a more elegant way of "building up your own stack trace" without having to do this manually and without panic.
There are a lot of error handling libraries out there. On Juju, we're working on one right now. This is the preliminary version: https://github.com/juju/errgo
Basically you just call a function every time you would return an error, and it automatically records the file and line number and lets you add on some context to the message, plus it gives you the option to mask the type of the error (to hide implementation details from your callers).
I wouldn't use errgo right now, it's still under heavy development, but it's similar to a lot of other error handling libraries out there.