|
Quoting myself from another site:
this page only reprints things I wrote elsewhere at
LtU.
28Aug07
¶
what is state?
context frames value ¶ Anton van Straaten: One of the most basic lessons of PL theory is that nothing has any meaning outside of a context which gives it meaning. Change the context, and the meaning can change. This applies well beyond PL theory. Yes, far beyond PL theory. I like the way you said it. As near as I can tell, it's universal, rarely failing to explain more in studies where a human mind is involved (which seems to be most of them :-). In the 90's for years I ended online messages with this signature: "All values have meaning only against the context of set of relationships." (That's what I'd finally concluded after more than a decade of reasoning about knowledge representation, leading down so many avenues of gnarly logical regress I hate to talk about it.) Lately I say "context frames value" to mean about the same thing. 01Jul07
¶
standing on others feet
burden of proof ¶ Vesa Karvonen: I don't need to prove anything about any correlation between toy benchmark data and the things that I'm advocating, because I'm NOT using data from toy benchmarks to advocate anything. You're correct, with a clear description nicely done. Burden of proof is on the party claiming data means something. But sadly it's often the case folks absurdly claim burden of proof is other way: that meaning-lessness must be proved — or substantiated by evidence attempting to demonstrate a negative (not a good idea). You might call this tactic "proof by denial of service" because you can claim anything you want and require others to work without bound to dismiss something not shown. Personally, I find it pretty irritating when folks say "show evidence my claim means nothing", which is why I'm chiming in to help. It's often done by someone who knows exactly what they're doing, with experience in working others into a lather, by choosing a random series of tactics aiming to elicit an emotional reaction. When you begin to refer to them in your responses, this tells them the emotional part is working. It's both more effective and more civil to avoid verbal reference to antagonists. Whether they actually take offense — or it's just a pose to keep emotions up — one is better off structuring sentences to avoid "you" (except when it means "one"). In contrast, one of the most patronizing things you can do is start a sentence with "Name, " as if you're talking to a wayward child. For what it's worth, you've only been slightly careless in this department, and it shouldn't have had this much effect. But LtU threads tend to go off by themselves, so some extra care is useful. 18Jun07
¶
practical lazy eval
inlined block execution ¶ Scott Johnson: For those unfamiliar with Smalltalk, "ifTrue: ifFalse:" is a 2-argument method supported by many objects, including the Boolean objects true and false. Smalltalk compilers typically optimize such messages to inline them. (It's how I did mine back when, and the only way I've seen it done in other compilers.) Basically a compiler can statically ask about every keyword message: is this a keyword message that looks like those I inline? (Eg, is this message "ifTrue:ifFalse:" and are the two arguments blocks that take zero parameters?) On a match against this, a compiler can assert the receiver is boolean, or fallback on general dispatch when it's not. When the receiver is boolean, the block code is inlined just like the if/else clauses in a C compiler. So representation as a zero arg lambda, or some other kind of continuation, isn't necessary. But the class hierarchy can provide actual implementations in the Boolean class, as well as in subclasses True and False, and this can mislead one into thinking the class code is actually reached at runtime, instead of inlined. Otherwise I vouch for your description. Blocks represent delayed evaluation, which is why they're arguments to control structures, since otherwise every branch would be evaluated as parameters before dispatch. (I think it's common for folks learning Smalltalk at first to ask "why are these blocks?" because the role of delayed evaluation in control structures is not apparent.) 19Jun07
¶
lazy eval advantage
whileTrue and intervals ¶ I meant no slight — I'd assumed you already knew what I'm saying. I was aiming at other folks passing through, who might otherwise suppose ST did control structures inefficiently by dispatch. (Smalltalk has no looping construct either). Once again, this is just for other folks. Smalltalk has whileTrue: which is basically like a while statement in C, and since most looping contructs in C map onto while, I don't suppose much will seem missing. A lot of folks using C and C++ write loops looking like this: for (int i = 0; i < n; ++i) { /*something*/; }
In Smalltalk this can be written using an interval like this: (0 to: (n - 1)) do: [:i | "something". ]
because the to: message to an integer creates an interval. 03Mar07
¶
unused functional programming?
mixed gc and manual allocation ¶ I'm not disagreeing with any part of naasking's remarks. I'm just suggesting a strong either-or opposition of gc or manual allocation is not necessary in a mixel model environment. Not that I can easily point you at one; but it's the model I've been planning around for a while. Hybrid solutions (both gc and manual allocation together in some mix) can target different approaches for different parts of an application. Getting a nice mix, though, would require a mixed language model, with gc at some higher level and manual allocation at a lower one. But with a mixed approach you can segregate your data according to purpose. The really large data sets with predictable lifetimes can use manual allocation. And the painfully hard-to-predict, ambiguous flow of control parts can use gc. A division like this would allow you to make gc memory a minority of total application footprint involved, so having five times (or more) available gc space than live data would be easy to arrange for the smaller gc minority. Presumably the interface between the two would occur at the level of "primitive" definitions, from the point of view of a high level language. You'd be free to push some parts of an application down a lower level to manually control exactly how memory works. For example, you wouldn't really want to do network i/o in memory with non-C semantics anyway. The ideal I'd prefer is a copying gc model in the high level language using full continuations, and a mixed reference counting and vanilla C model at the bottom for primitives, when I really have to control where things go and when. A copying gc can easily have a nice interface to "externally" allocated memory, because a copying gc can easily tell an address lies outside the space of memory managed by copying, and thus should not be copied or changed in any way. Anyway, the gc parts can use the fast pointer bumping style of allocation, and get efficiencies from lower block fragmentation (less bookkeeping is needed) and low enough overheads by having a big enough ratio of free space to live gc data in the part of the address space that's collected. Time pressure in development is big enough that faster development with gc first would be a win, especially when incremental shifts to manual allocation later can optimize exactly those parts most crucial to performance, without introducing a big upfront design requirement before getting a first version done. 15Apr07
¶
best intro language
few want to write software ¶ I can relate, but I had also agreed with Paul's sentiment, that, "What do you hope to achieve by your effort?" is a frequently unasked question that can really matter. In your case, if you didn't have a strong motivation to limit what might work for you, you might have enjoyed better results. Goals can be strong blinders to perception, but this goes without notice much of the time. Folks like you who enjoy learning for the sake of learning might be somewhat rare. Perhaps a more raw and unbiased curiosity streak correlates with high intelligence. A joy in finding out how things work doesn't appeal to everyone (more's the pity). And folks who have it can easily overlook pragmatic goals in others to get a specific unstated but expected payoff. My own sons are 12 and 15, but I haven't started teaching them to program because I can't think of any good reason to give them why they should, and they need one, despite the fact they're both top percentile in math. If I could demonstrate a relation between coding and the video games they play (which I can't) that would be enough for them. But they simply lack raw curiosity in seeing what happens in text displays, since they've been spoiled by high feedback graphic environments. Within my mental simulation (imagining a process of selling them on programming), all plausible avenues I consider end in their boredom and disinterest after 20 minutes or so, following by increasing resistance to give me attention later for more tries. (I'm already well familiar with a pattern of avoidance associated with attempting to instil an interest in something that bores them.) I actually seem to be doing best lately by not explaining what I'm doing, since the mystery of that gibberish I'm writing has a little intrigue. They both want to affect the world in a way that would matter beyond learning an abstract new skill. Here's the motivation I think would work for them: if they could make a thing they enjoyed, and could show a friend and not be ridiculed for pursuing lame triviality, then they might see a purpose in it. The bar of avoiding ridicule from their jaded peers is a rather severe one. If it's not amazing eye candy, and it's just some low impact technical feat, then it had better at least be something no random joe can do just as easily. Just having a blog for example wouldn't do it. I've been thinking my best bet is making it possible for them to program a server in some way, so they can show friends they've altered the way a system behaves that their friends can also use. If it was feasible for them to be makers, and not just users, then this might give them a path to follow suiting their reward oriented focus. 16Apr07
¶
thoughts: best intro language
(sorry this reply is so long) ¶ I did think about all those things, and some others. But in my response here I'll try to get back to the issue that caused me to reply, so I can stay on topic. I was trying to say something about programming languages, and learning them, and goals for application, and the way everyone projects their own goals on others without question. The question of how to inspire my sons to learn programming is more an illustrative problem I see pertaining to the chicken and egg problem of motive and learning. But I appreciate the desire to be helpful with suggestions. Derek Elkins: Perhaps setup a framework so that they could set up some Internet-borne social program. Yes to "framework" but no to "social program" since I see the influence of their peers as a drawback to be tolerated and not an end to be cultivated by amplifying the means they have at hand to replace phones and tools like MySpace when they get to them. (It makes me flinch just to think of what I'd have to do to keep a social site from having things appear that would get me and them in trouble.) I wouldn't mind if they wanted a social site, if motivation made them push harder to get through technical stuff they see as just means -- but there's no reason to make it easy. A week ago I told my older son it was hard to choose the right level to present them with tech info. As an example analogy, I told him I might aim to teach him to build a computer. At one end, I could try to teach him every little suffocating detail about hardware, and of course no substantial headway would occur in reasonable time. Or I could have him call someone on the phone and say "build me a computer with these parts", which would result in a computer being built quickly, but without him having learned anything about doing it himself. The trick in a framework would be getting the level so he was the creator rather than user at all the levels that explain what causes a system to do something. If his part was mostly "asking to have things done" in some pre-defined slots, this would make him a passive user, which I see as not educational. So I'd like him to see (eg) content that appears in socket traffic, because seeing all inputs and outputs -- and writing the code that responds to this -- gives the overview I think is essential to making and completely understanding the part that makes the active decisions. But then there's a big jump from that low level to one high level enough to be interesting as a result they can control. It would be best to develop the framework with them, using a language that kept them away from unnecessary detail. But I'd have to present some initial thing as a fait accompli for them to extend, as a starting example they liked but was not too complex to understand. I've been thinking about semantics of a language plus system that would make it easier to write i/o processing systems at a high level, and it influences what I'd try. Please forgive my not saying anything about it though. I try not to broadcast ideas I have in public anymore, since it doesn't help me get things done, and it increases risk of inspiring others to erect IP roadblocks where I'm going. (Actually, I've been thinking it's convenient to have code that's incomprehensible to others, so that after it's released, it's that much harder for folks to point at bits and claim any parts relate to IP of others -- eg, if you could see the code was good for solving problems in a domain where there were patents. So I say, good luck figuring that out.) I need to sum this up (and on the topic I cited in the first paragraph). I think what you want to do with programming skill should influence a language you choose to learn first, since if you learn a language and still can't do your application, then you've been had somehow. So if I want my sons to program, I should teach them a language to do something they want done, where the two go hand in hand. I just don't have the combination of application and language lined up yet. Current applications they might like have tools involving too much stuff for beginners. I wish others already had server projects for rank beginners so I could refactor them to be simpler still. 04May06
¶
insolvent in palo alto
typed continuations ¶ Paul Snively: Naturally, you know that I'm a big fan of your ideas. How are things in life with respect to work and CFT? I know things have been tough for a while. It's easy to assume folks stop being interested in one's ideas after personal trouble; I'm glad you were not only a fair weather friend. But since life in work and CFT is off topic here, I should keep it really short, like a movie title. So instead of Sleepless in Seattle I might sign myself Insolvent in Palo Alto. (If I still signed myself [name] I might see more greetings -- maybe I should use that name again professionally.) Work could be better, but staying in one place has been my goal for a while now, so I'm fourteen months into a startup that plans to drop stealth mode during the next month or two. However, I plan to make few comments on work even then. Maybe after they announce I'll consider moving on. I should resume a blog to chat in a better place. In this forum, hobby coding in my vanishing free time remains on topic when it's in the interesting programming language vein. I guess I'm gearing up my mind for more implementation. I added a Smalltalk object system to a Lisp dialect once around 13 years ago, and doing it again would be really easy, and this time my product would be enormously better in many ways. Only this time I won't be grafting Smalltalk onto Lisp; a generic VM suiting either equally well is better. Keeping the C++ at the bottom small and understandable is what slows my hand, since I don't want to just spew out an extra thirty thousand lines just because it's easy. At the moment I plan a first draft in trampolined dispatch style to prevent C stack winding, with an interesting representation of continuations to simplify a mix of code in many formats including interpreted parse trees, VM pcode, and C++ primitives, etc. The idea is the same one I had about eight years ago, and was planning for Mithril, where basically code pointers (a "return address" or a "continuation") are typed, so calling or jumping to executable code is generic. While that slows dispatch with indirection through a how-to-dispatch step, it allows you to mix everything and have reified stack backtraces with return addresses of all flavors. And nothing stops you from having C calling into Lisp calling into assembler calling Smalltalk calling into Python, etc. When dispatch is not antagonistic to high level tools like debuggers, it becomes incredibly easy to write them, and incremental just-in-time compilers, or whatever. And you can just experiment with code research you find amusing. Like you could just add a 68K emulator to use as pcode (as a random example) and it would plug-and-play with the other code types. I'm late and really must stop right this second. :-) My girlfriend interrupted the second paragraph above to have a discussion about moving. 02jan07
¶
counting from zero
zero is space centric ¶ My preference for zero based indexing is related to Dijkstra's question rephrased. Anton van Straaten: Dijkstra proposes asking the question "How many elements appear before the one you want to identify?" I prefer the question: "How many members must you skip before the desired spot?" And sometimes I use the name "skip" instead of position or index to emphasize the issue (especially when a data structure has a btree shape such that indexing is skipping-centric). It unifies terminology for current members and future additions to the end. If eos (end of sequence) is the count of members, it's also the position at which new members append when skipping all current members. Since eos and size() are synonymous, this eliminates opportunities for still more off-by-one errors. My view is spatial (relative positions of items occupying space) and constructive, instead of verbal and declarative. I prefer semantics most cleanly expressing accumulation and editing of existing sequences over time -- it seems less messy with zero based positions. Editing is all about calculating where to put things, which involves knowing how much to skip before making cuts. Zero-based distance is clear. Iterating over sequence members seems a minor use case. Instead, if you want to find what causes awkwardness and what doesn't, try expressing insertions and deletions in a sequence, with use cases including empty sequences and appends. Zero based indexing seems to have fewer edge conditions. 27Aug06
¶
runtime: unchecked exceptions
marginal utility of detecting excluded middle ¶ I hope this isn't one of those posts that make me seem strange. In the 70's I was a rare youngster interested in the law of the excluded middle (below, let LEM mean this, and let EM mean excluded middle) and criticisms thereof (cf intuitionist logic). Folks looked at me funny when I talked about such things back then, so I stopped. :-) Ehud Lamm: Defensive programming seems to be what you are talking about. That's very close -- so close I don't want to quibble. (If I meant to quibble, I might say choice of terminology depends on what you're trying to reason about, and defense isn't a perfect fit in this case.) It's best to consider me a clever practitioner with a pragmatic interest in maximizing utility and minimizing costs. I guess a lot of basic economic reasoning tends to inform my seldom-voiced assumptions about what's worth attention in daily work problems. Almost all the time I'm concerned with incremental increase in software value and the incremental decrease in costs. In economic jargon, it helps to know ideas like marginal cost, marginal utility, and opportunity cost. (Here's a short crib note for folks who don't care about economic jargon: those are all basically calculus of value ideas concerned with incremental changes in cost and benefits.) Okay, so in the real world (I mean in work you get in Silicon Valley -- I realize this isn't actually the real world :-), one finds marginal utility in software tends to decrease as you add code. The old code interferes with new code, and reduces the value of new stuff added. Lots of things cause the interference, and you can read all kinds of literature on this. But a cause I think about often, which is seldom noted, concerns the excluded middle (once again, LEM means law of the excluded middle). The idea behind the law of the excluded middle is that a proposition P should be either true or false, exactly, and not have some fuzzy probability value between zero and one. For example, P might be "this code works perfectly" and not-P might be "this code is broken". Anyway, LEM says the excluded middle (EM) is zero sized, and that's what is meant by "excluded". But in software, the EM (excluded middle) is not zero sized, and it tends to increase as you add more code. So the marginal EM of software tends to increase. (I know this sounds a bit weird, no?) It's caused by propositions that kinda-sorta apply, and code that kinda-sorta works with data when the data reaches allowed extremes. That's a slightly more scientific way of saying "software gets chaotic", but now there's something to latch onto, since I'm offering causes of chaos that can be attacked by specific technique: go search and destroy EM if you can test for it. The EM is caused by lack of fit between real work data and usage patterns and the taxonomies (aka classification schemes) intended by the code. Okay, now back to my gratuitous nod to economics, with the addition of one more term: diminshing returns. As you add defensive programming, the returns diminish, which means the marginal value of adding more defensive programming decreases. But if you don't have any defensive programming at all, which is not all that uncommon for lots of software in the wild, then adding a little can have a large payoff to begin with. That is, the marginal cost of writing a little defensive code is small, but decrease in marginal cost from failed software is large. Coders usually call this sort of thing "low hanging fruit" (big bang for the buck), but only provided they conceive of the opportunity. Folks who believe the LEM is true all the time have trouble seeing the point. 04May07
¶
arc in action
freely available vs control ¶ Kay Schluehr: Sounds a bit like your are seeking a DLR. Except that I also want to keep doing what I already do as well, which is completely control optimization of memory, threads, locks etc in Linux content delivery servers with an optimization focus. I just want to use other languages as well, using a common runtime with the C++ code. Decision making parts of servers tend to be complex (async networking, continuations, and error recovery) but not resource intensive, and perfectly suitable for much higher level languages, since then control could be expressed without as many unnecessary low level nuisances. Someone has to make sure memory and threads do the right thing in user space, and professionally that person these years is me, since I'm usually the user space runtime specialist who triages every weird thing. And it seems likely to stay that way, so I use tools I can instrument all the way down, letting me use evidence-based reasoning and scientific method about everything through direct inspection. But the C++ runtime could make it even easier for me to do this, and some other ways of controlling memory would not only make C++ more efficient, but would also optimize runtimes of dynamic language runtimes built on top, since they could rely upon these useful heap guarantees (Luke Gorrie in on the right track here). I just see an opportunity to line all my ducks up in a row, and get C++ standard performance in higher level languages that mostly do the intensive things in C++ primitives, using a better standard library which mutates less, all in a smallish body of code written by one person that avoids corporate twister design. (I say the phrase "corporate twister" to myself all the time to explain why code ends up the way it does. Derivation of the metaphor is pretty obvious.) 07Dec06
¶
metacircular interpreter
Mu: the lost runtime ¶ (This reply is an excuse to place a short explanation of Mu where I can reference it elsewhere.) Such a kernel could be JVM or CLR if they did in fact do everything done by current languages, such as full continuations with spaghetti stacks. The term VM covers a lot of ground. Many folks lump an associated runtime together with the VM when a VM ought to be seen as a part of a runtime. VM can imply instruction set among other things, when runtime need not have one. Or a runtime might have multiple instruction sets and hence multiple VMs. So Mu might be any combination of VM and runtime that seems to win the armchair game described below. For future reference: here's a tiny context-free Mu codename explanation. Pretend you want to bluesky a new spec for a VM or runtime aimed at one or several languages. Call this thought experiment Mu if your Mu ideas assume two goals: [1] Mu must work well for many languages (or better yet, all established languages). [2] Mu must avoid assumptions limiting use or efficiency in some language L you'd rather ignore for your convenience. (Name two programming languages; Mu must work for both: that's the design task, even if infeasible.) Bonus: when you feel moved to humor, you can allude to the earlier invention of this Mu runtime by ancients in some lost civilization of Mu. (We're just struggling to recreate their works.) 26Sep07
¶
bray and erlang
easy as threads ¶ John Stracke: Erlang-style concurrency requires processes to be isolated from each others' variables; that would be hard to add to C. I know. One must often simplify a lot to reduce comment size by a 1000 to one factor to fit in contexts like this. So in my post, by "easy" I meant something like "on the general order of difficulty as". And it's tempting to turn the word "process" into a long exegesis so you might allow my next remark, but I won't. John Stracke: It could be done, but it'd be more work than threads. Depends. Suppose C never had threads before, and your goal was to end up with both the pthreads API and implementation on all major platforms. And suppose you had to write the docs for pthreads, and bring folks up to speed. All that would be included in the cost of "adding threads to C" if you weren't able to ride on history. I'd say that was at least as expensive as what you'd have to do in order to present a partial (as pthreads is partial) implementation of consistent Erlang-ish immutable message passing in lightweight "processes" in C. So I don't know why you bothered to disagree. Now, if I was going to add Erlang style concurrency to another language, I'd actually be implementing it in C (or C++), giving me a "partial implementation" for the language in question only. I expect this will be easier than adding (say) pthreads to C from scratch. So I'd argue in the opposite direction you did. Split the difference? 12Aug07
¶
gc without paging
all or just some ¶ Brandon Moore: If you use some standard GC within a process, collecting that process's heap requires touching more pages than that process's working set. Yes, say for a simple copying gc. Brandon Moore: Once you've gotten around to all the processes, you've touched more pages than the app has in it's working set. But you needn't to get around to all lightweight processes during one gc, and that's my point: they can gc separately. Some processes might rarely if ever need gc — or might create garbage at a much lower rate than other processes — so gc tends to touch memory in processes that are busier. True, by the time you collect all processes, you've touched more than the app's working set. (Whether this qualifies as "much more" might depend on how much extra unused space you have left over after gc. You needn't have more unused memory than the largest process collected.) I think you can arrange things so collecting one process doesn't terribly impact the working set of an app in other processes. And by doing gc more often in processes that generate lots of garbage, gc might correlate with memory in the working set. (Obviously this is only reasoning and not actual data I offer here.) Brandon Moore: just because you want to think about memory management for the cached data doesn't mean you want to do it for the algorithms that maintain the cache too! Yes, I want to handle bulk data in a language efficient at that, but use another language better suited to writing complex code and decision making. The latter can use gc more freely and not impact the cost of data managed in the former. By using lots of lightweight processes that gc independently, you can get away with very simple gc schemes (like stop and copy) with minimal cost and complexity (since you needn't track inter-generation pointers) and still have some of the cost benefits of generational gc, since older processes that make garbage slowly act a little like old generations. And collecting one process at a time gives some of the incremental low latency effect of generational gc collecting younger generations. Sorry for my wordiness. I didn't really mean to have a discussion. I just wanted to contradict one phrase in the paper; but your reply shows it's always possible to rearrange definitions to blur issues. And without data and a study it's just my conjecture that putting busier garbage generation into one process is effective for simulating young generation collection. |
06Jul07
¶
async calls and errs
cps and async ¶ Andris Birkmanis: Does this imply that any continuation-passing scheme is automatically asynchronous? What I like about CPS is how well prepared a continuation is structured to handle async callbacks, so flow of control can look almost synchronous to a programmer. This way, even though the process has not blocked on an async call, the continuation state to resume the other half of call processing has been preserved exactly the way it was, just as if the process had blocked at that point. Lack of decent continuations in C++-based networking apps inspires much code falling under Greenspun's tenth rule of programming: "Any sufficiently complicated C or Fortran program contains an ad-hoc, informally-specified bug-ridden slow implementation of half of Common Lisp." This is why I think sufficiently complex C++ systems should have a real high level language embedded inside them, specificly to handle such things in a less ad hoc manner. 05Jul07
¶
async calls
async in language runtime ¶ (I ended up writing more than I planned. I took the day off to spend with my kids, and now I'm spending time on this; I probably shouldn't spend a lot of discussion time on async support in languages, since that's fairly involved and I have my own version to code myself. I just don't want to leave you hanging.) To tackle parts separately, I split this quoted sentence in two: Andris Birkmanis: I am asking about it in the context of building a programming language... That's a hard case. Async support in a bottom (or middleware) platform layer is tough because you must avoid constraining what apps do on top of your layer. You need to define a thread or process execution model, or else your async call mechanism won't have a context for defining what you mean. Your language needs a concept of independent flows of execution control, so async messages between flows can be observed to not always be in sync; otherwise you won't have async messaging. (Unless you only want it observable from outside.) Denis Bredelet-jido: ...where asynchronous calls are a primary mechanism for concurrency. It sounds like you want async messages to be more primary than a process or thread model, which is normally what defines concurrent activity. I'm not sure you can architect an app on top of a language without letting a programmer decide what activities go in what process, which then message one another (either directly or through some intermediary like a message box or a queue, or sockets, etc). To add async support, all you really need is a form of messaging that doesn't block on send, so a sender can keep going while a receiver does something in parallel (potentially, if it's in another process or thread). The complex part is getting a response later, by polling or by blocking, where blocking is going to be more performant under scaling. You can define a messaging system as queues without requiring threads or processes. The runtime could timeslice available cycles, and this is often done these days with event oriented systems. But if you don't want one main thread to block when using a blocking system call, you'd need some model that allows some threads or processes to keep going, to use resources productively in the meantime. I think my thread of thought just degenerated into totally generic OS speak, so I'm not helping unless I say something you see relates to async. Before I forget: try to draw as many diagrams as you can of what you're thinking about comms, because it helps show possible inconsistencies in your ideas. Use the classic style of diagrams in client/server protocol interactions where messages go laterally and time goes vertically. (Text mediums like this one aren't good for drawing pictures.) The main rule in async is messages don't arrive before sent. So receiving a message is evidence it was sent. You won't have a lot of other evidence besides received messages. So infer facts about messages sent only in terms of what you receive. You might get crazy sounding app models if you hope apps are able to know more than what can be inferred from received messages. Throw out use cases that try to keep globally synchronous knowledge of what's happening. Denis Bredelet-jido: An error is returned instead of a value At the runtime level, an app layer error is just another value in a message that can't be told from a non-error message. It's up the app layer to divide values into error and non-error types. How the runtime responds to runtime errors is separate from how errors get passed in app messages. If an error happens in the runtime, that's something else entirely. Logging and user notification probably get done in terms of app level messages, but some of the senders can be runtime level entities. (Notification that a runtime error was handled would appear to come from something not defined by an app.) Denis Bredelet-jido: What are the advantages and disadvantages of this approach? Do you have experience with it? Yes I have experience I can't be too specific about. Write ahead optimization can be used in protocols (seems like a lot of them) which wait unnecessarily on send, instead of writing more and getting an error later. Saying 'okay' immediately upon receipt and then actually writing concurrently with more client writes will decrease overall elapsed latency to get multiple writes done. But it uses more resources at one time, which might compete with lots of other sessions being serviced trying to use the same resources. It's an optimistic strategy assuming things normally go well, with low frequency of errors seen. You'd waste resources if errors were frequent and you committed more resources too early too eagerly. You can delay error reporting until a client needs a sign everything is fine Ñ say on close or commit. Good luck with your async support in a programming language. If you try to separate what a runtime must know and what apps must know, you might write yourself less confusing requirements. 18Jul07
¶
next mainstream PLs
profiling memory use ¶ Curtis W: ...but getting a compiler to utilize a fraction of our ability could yield a large boost in lowering memory usage for a GCed language. To lower high water memory usage for a language with gc, I think one focus should be standardized profiling in a language, showing both source of use and patterns of growth. This leverages a human mind's ability to process patterns by simply making them visible. Then a coder can reason about causes and experiment scientifically by making changes and observing results. Visibility is key to getting the mind into play. Highly expressive languages make it easy to request unintended effects like excessive memory use. Good profiling and debugging tools should make it easy to observe effects, so a coder can compare actual to expected results. 11Jul07
¶
template metaprog fwk
stack overflow AST nodes ¶ cdiggins: This is not a problem I have ever encountered in a recursive-descent parser, and I use them exclusively for all of my parsing work, and you know how much that is :-) t's a good idea to check for stack overflow when a parser is deployed where a malicious bit of source code might take down a server using a recursive descent parser on client requests. This is especially true in servers using threads to parse requests. (Single threaded Unix apps might assume the stack can get aribitrarily large and spiral into death by page thrashing before death by overflow.) For example, if you're using pthreads and the default stack size is 1MB and this seems too large for many threads you might want to have in pool, you might choose a smaller default size (like 256K I saw recently). Then suddenly it's not hard to construct a request that's not especially big that can overflow this stack in a parser. What I've done in the past is reify stack overflows as an explicit AST node. (The node says "you overflowed the stack here blah blah blah" so this propagates to the server response.) A lot of error conditions can be turned into parse tree nodes to put off what should be done about them, so the parser needn't do it. 28Apr07
¶
phil gosset
maybe an experiment ¶ I only watched the first five minutes of Phil's talk (during which I learned how one pronounces "Principia"; I'm one of those readers who knows many, many words I've never heard anyone say aloud). But your description of Phil's technique sounded like an experiment to me, intending to verify one's own independent reasoning, to get two readings on a value that should be the same, so each can verify the other. (So I thought your friend's question was similar to asking why double blind studies are better than open studies where expected results are known.) There's a very good reason to do this, which isn't obvious from the academic result of getting a match after minimizing bias during an experiment. But even this part is valuable as a test cycle: a type match is the same as passing a runtime test in terms of giving you a data point on quality. I've found that quality of code is often directly related to how much thought has gone into reasoning about whether it will work as intended, and the most effective reasoning is one that happens in your mind, and not in a tool. (A person considers many more ancilliary lines of reasoning than a minimal set necessary, and these end up informing other understandings about code, often giving insight into good and bad options like simplifications and risks.) 01May06
¶
believe about PLs
full lifecyle languages ¶ Dave Griffith: ...next great productivity gains will be made by languages with explicit support for activities normally associated with the analysis, design, testing, integration, deployment... That's basically what I've been thinking for a while. Now what to do about this is what occupies my design cycles during Copious Free Time. But I might add an item not noted in your list above: explicit support for learning. That one's tricky to explain, but it basically means bounded documentation: enough docs to explain what you find, and few enough docs (small enough language) that you can plan to get your head around it in a time you can plan. Typically languages don't have bounding their adequate docs as a goal. But to me this is as helpful as a line in a proof that says there are no more steps. Okay, now back to your list of things needing explicit support. Productivity gains would derive from saving time on tasks that take too long. Today just hacking out a first draft of code takes a small part of total time consumed by trying to ship a new product, like a complex server. Just to pick a single thing on the list, it takes at least as much time to analyze what a system is doing under debug. In other words, after you write the code, you spend at least as much time wondering what the system is actually doing, even if the code is nearly all correct. Starting about three years ago, I started telling folks you not only had to write code that runs correctly, you also must provide some kind of support to see whether code is running correctly. Even if there are no bugs, some emergent effect at runtime will puzzle you, and a long time can be consumed by tracing the cause and effect back to appropriate roots. When you work on a team with several other folks, after the first agonizing bug shows up, you can spend a lot of time proving your part of the system does exactly what expected, and didn't play a role in the observed chaos. Lately I seem to spend most of my time in forensic system analysis as a local coroner for other folks' code fatalities. I never had an ambition to be a code coroner. It would be enormously helpful if a language -- and not just one particular development environment -- had explicit support for generating evidence of what and how a system actually got from one point to another. At minimum, a count of every method called would be terribly useful for debugging and testing with a bit of scientific method. (Test A should fire method B between M and N times.) Basically, you'd like to reify a lot of things about the runtime enviroment, so you can minimally evaluate a running system. Better yet, it would be nice to able generate code that automates your evaluation in various ways. (Generating runtime breadcrumbs can be expensive, but in practice I see this expense being paid anyway, and sometimes in grotesquely ad hoc and inconclusive fashions. If a language supported such features at the conceptual model level, the cost ought to be less than otherwise. And real deployed systems typically want to have logging and auditing of some kind turned on anyway, if only to bill the folks using a system with some backup proof that the bill is not made up.) These days during my Copious Free Time, I think about doing this kind of thing using a hybrid of Smalltalk and Scheme and XML, where all syntaxes parse to variations on the same parse trees and compile to the same runtime. You'd also want to implement Python too with the same runtime. Using existing syntaxes -- especially ver simple ones -- would reduce size of docs. (You'd need to cheat a little to avoid making syntax for primitive data types vary too much between one code style and another.) The goal would be a Lisp/Scheme dialect (with full continuations) that also supported a Smalltalk dialect and an XML dialect, and also had namespaces for traditional Scheme and Smalltalk bindings, plus a lot of code for converting between language flavors (and sure, Python too, why not) and a expressly supported native C++ underpinningfor writing primitives. I basically like Patrick Logan's "shared nothing" model which resembles some parts of designs I did a few years ago, so I'd lean in that direction for the network i/o module and server support. I think a web server should be built into the langauge, so you can ask the runtime for a summary of current statistics. Everyone who writes a server wants a web interface, and having the language understand the idea would help. A hello world program ought to generate a web page or some XML someone wants to consume. I like the idea of being able to nest XML inside of Lisp inside of Smalltalk in whatever order I like. But that doesn't have anything to do with the semantics of explicit support for all the things in Dave Griffith's list. I don't want to make up a new language to get the desired support, though mixing existing languages might seem like that. Because rules for symbols in Lisp and Smalltalk are fairly simple, it would be easy to have a namespace system that allowed one to name any particular runtime value you wanted to sample, as a first class reified object. You kinda want syntax styles agnostic about symbols so the namespace architecture can be beefed up to refer to everything in the conceptual model providing all that extra support. Um, I am so having diarrhea of the mouth. Sorry -- gotta cut myself off. 16Jan07
¶
low level PLs
popper's objective knowledge ¶ Some folks might think philosophical subthreads are off topic. But they can touch on underpinnings of how programming languages are significant in human terms, especially in any context where computing systems strongly affect the way people think, or how they conduct affairs. With only one exception, I usually ignore what philosophers say as irrelevant to computing. Karl Popper is one of the few philosopher's credited with having much sensible to say about theory of science and the interaction of subjective and objective phenomena. Some folks in computing circles might find parts of Popper's views on "third world" interesting when evaluating the meaning of how computing systems embody systems of knowledge in the world outside a human mind. (I first became interested in the interaction between thought, systems, and "things" around 1980, when I thought it might be possible to invent a new language -- possibly mediated by computing systems -- that made people less crazy, or more intelligent, whichever was more practical. It didn't take me long to run across Karl Popper's Objective Knowledge [1972], which exposed me to more than I could digest at once. Since then I haven't seen much as applicable.) Wikipedia has an article on Popperian cosmology, which might be a good place to start following links on the topic of Popper's notions about "third world" structures and reified knowledge. However, it might not do much for you than provide a context for situating old arguments in a new setting that's more amenable to analysis. You might say Popper addressed the question, "Are ideas real?" Then he pointed at things we create in the environment that encode our ideas and affect the world. 07Jan07
¶
learning smalltalk
msg syntax to runtime method dispatch ¶ uchchwhash: perhaps you could point me to some place where subclassing in Smalltalk is contrasted with Java's? Your explanation sounded quite alien (for a Java-savvy), I'm afraid. (Please forgive my use of stream-of-conscious format instead of reverse pyramid structure as if I was writing something for publication.) I'm not Java savvy enough to do a decent job. And if I was, I'd hate to explain Smalltalk subclassing in terms of something more complex. It might be better if I just describe matters in terms of hash tables using symbols as keys. (Smalltalk symbols are like Lisp symbols: interned strings such that two symbols with the same name are in fact identically the same symbol instance. If string 'foo' is interned as a symbol, then #foo is this symbol under Smalltalk and 'foo is this symbol under Lisp.) The explanation will also sound pretty much like the way Python method dictionaries are done, so you can read about Python and get more or less the same description. My explanation below relates what you see lexically in the grammar to what you're likely to find in the runtime data structures of resulting Smalltalk classes. Each Smalltalk class has a method dictionary containing ordered pairs of (selector, method) entries. The method value has a type something like CompiledMethod, or whatever a particular runtime wants. The selector key has type Symbol, where a symbol is an interned string as noted above. Every time a method is defined, an entry is added to a class method dictionary with the method name as the key, and the compiled method as the value. The term "selector" in Smalltalk means a symbol used as the name of a method, and nothing more. So if you add a unary method named 'negate' to some class, then #negate is the symbol used as the selector for that method. Selectors only fall into three classes, depending on letters in the name, corresponding to the three grammatically different ways to express a message send in Smalltalk: keyword, binary, and unary messages. (This is in order of increasing precedence.) Keyword selectors end in colons. Binary selectors are one or two special symbols from an explicit list. Unary selectors are alphanumer identifiers that don't end in colon. The arity of a unary method is one. The arity of a binary method is two. And the arity of a keyword method is one plus the number of colons that appear in the selector name. (Plus one is for the message's receiver.) I just summarized what you can see yourself from the original Smalltalk grammar bubble diagrams, if you work out the consequences of the resulting grammar. Here I'll blockquote a sidebar on bubble diagrams:
To illustrate method dispatch, and resulting style of Smalltalk subclassing, let's consider an example. Earlier in this thread you chose the following example Smalltalk statement: y := x indexOf: $e ifAbsent: [0].
This assigns to variable y the value returned from sending #indexOf:ifAbsent: to x, with two arguments, the character constant $e and block [0]. (Note the purpose of using a block is delayed evaluation; the only way to avoid evaluating expressions passed to a method is to put them in a block, so they won't be evaluated until someone evaluates the block.) So your example here dispatches only one message: a keyword message named #indexOf:ifAbsent: taking two arguments plus the receiver x. (Smalltalk grammar intersperses the arguments with each section of a keyword message selector name, so a one to one corresponence results.) When you evluate the right-hand-side, what happens is something like this. The method to be called is found by the runtime by evaluating something like the following: (x class) methodForSelector: #indexOf:ifAbsent:
(Note I just made up the methodForSelector: name here; it's just pseudocode.) The method returned by that expression is then called with two arguments $e and [0]. A Smalltalk compiler will typically emit bytecode that actually references symbol #indexOf:ifAbsent: as a literal value in the method dispatch (unless the bytecode itself encodes the selector). So the method is effectively dispatched by name. This symbol is used as a lookup key to search method dictionaries -- presumably hash tables -- starting with the class of x and working back to superclasses of (x class) if #indexOf:ifAbsent: is not found in (x class). Each class has a pointer to it's own superclass, so searching the inheritance chain at runtime is explicit. In practice a runtime won't want to actually search the inheritance chain of classes every time a method is dispatched, but this is easily avoided by using a lookaside cache containing the result of earlier similar dispatches. A lookaside cache might just be a large hash table with keys of format (class, selector) and values equal to methods found earlier for such searches. (Lookaside cache invalidations need only occur when new methods are added to class method dictionaries.) This sort of dispatch and this kind of lookaside cache is often used in other similar languages like Objective C, which imitates both Smalltalk syntax and Smalltalk style method dispatch using selectors. Except Objective C probably maps selector names to integer constants at compile time, rather than using the symbols for selectors themselves. (I seem to recall having a conversation with John Anderson about this when we worked together briefly at OSAF. He was the guy who worked on optimizations in the Objective C runtime at NeXT, and he'd done something like the same lookaside cache that I'd coded into a Smalltalk I'd done in the 90's.) Anyway, you can infer the nature of Smalltalk subclassing from a few details:
Subclassing under Smalltalk just involves naming the superclass (or superclasses when multiple inheritanced is supported). Method overriding merely requires redefining a method with the same selector. The entry in the method dictionary of a subclass will be found first at dispatch time, so any other entries for the same selector in superclasses won't be found. Smalltalk-80 didn't have private scopes for member variables, so all subclasses could see and use the members of any superclass. Using C++ terminology, all Smalltalk methods were public and all member variables were protected. 20May06
¶
google web toolkit
one vs many ¶ I don't mean to agree or disagree -- just explode this into more detail for thought. Ehud Lamm: It seems like the tide is turning towards single language solutions, as opposed to the old wisdom of using different languages for the client, server, scripts etc. It's like a single language solution for an initial simulation of a later deployment context that uses multiple languages. It seems very pragmatic as a way to avoid/remove the fragility of development directly in JavaScript. And simulating a system you target is a clever way to do things, though burdened with some up front costs (which folks usually hate with a passion). But in a one vs many solution space, it seems more a move to many than a move to one, when you see the use of Java as a (temporary) substitute for JavaScript before deployment in JavaScript. If in the long run this was just a move to displace JavaScript with Java, yeah, that would be more a one language solution. The flavor of Google's tactic (using Java to for development time debugging) resembles my notion to develop in an easier, very dynamic language (Lisp, Smalltalk, and/or Python) before deploying in whatever the runtime context demands when I apply code to some application delivery context. I'm just not real excited about the use of Java for this, since Java reminds me of the verbosity of C++, which is a drag. But Java is an extremely practical choice given current tools, and given desire to see some adoption by folks out there trained now, as they are. I'm glad Google is doing this, since it makes using a sequence of languages in development sound less crazy to folks who otherwise assume this can't work (and then offer little but flak). I wish more folks would get with the program, and learn everything you target is a machine you can design another layer on top of, to wrangle some control over your attempts to develop for the machine. Ehud Lamm: To make this work smoothly, we'll need language innovation. This has to be a good rhing, right? Yes, I think so. If nothing else, something in the web toolkit would reify some of the model of the whole machine involving JavaScript, web pages, and servers, etc. Not only would these stakes in the ground be useful, but the higher order manipulations of this reified system would be a welcome change to random chaos in development. (Does "random chaos" sound negative? Probably. :-) I don't think anyone but Google would try this; I expect it has something to do with their reputation for hiring folks with higher educations, and thus with a tolerance for abstractions and plans, which are very much frowned upon in the industry today. Modeling anything at all is a big extravagance most places, where they don't even want you to write plain english notes on how your code works, because anything but code appears a frivolous waste of time. Language innovation requires something for the innovation to target; so now we might get some if some higher order way of expressing how a system works might get some adoption. There might be some innovation in the way the web toolkit generates JavaScript. (There's needn't be, though. The translation could be written in a non-retargetable way with very opaque manner that brooks no interference, forking, or understanding. Depends on the goals of the toolkit maker.) |