tag:blogger.com,1999:blog-981057064039389802024-03-13T03:49:18.259-07:0060,000 feetA blog (mostly) about high level programming languages.Jasonhttp://www.blogger.com/profile/17941750539814842623noreply@blogger.comBlogger8125tag:blogger.com,1999:blog-98105706403938980.post-19256643522744482112013-04-06T05:14:00.002-07:002013-04-06T05:14:30.610-07:00Lazy like a hummingbird<br />
In my travels through the world of high level languages, the main language that caught my attention was Common Lisp. I was drawn to it because it was the most powerful language I had ever encountered. With macros, the full language available at compile time, restartable exceptions, the most powerful object system in computer science, it was hard to imagine a more expressive language.<br />
<div>
<br /></div>
<div>
But then I started looking into Haskell. I've come to the conclusion that it is on equal footing with my beloved Common Lisp, if not beyond. And now I'm starting to do work in it. I had avoided that for a long time because while I realised Haskell was powerful, I was afraid the type system (good though it is) would be constantly in my way, reducing the value of the overall power of the language to me [1]. After using it for a few days now, I find that is not the case at all. In fact, after one gets past the initial frustration of code not immediately compiling the type system becomes quite liberating.</div>
<div>
<br /></div>
<div>
I'm coding in Eclipse with the Haskell extension so the IDE can compile the code as I'm writing it and I get near instant feedback on any type errors. Eclipse is pretty shoddy and not nearly as powerful as the Visual Studio+Resharper combination I work with in my day job but it saves time and appears to be the best option currently. With this setup I can just make what ever changes I want and wait for the IDE to tell me what all I have to update.</div>
<div>
<br /></div>
<span style="font-size: x-large; font-weight: normal;">Type Analogous</span><br />
<span style="font-size: x-large; font-weight: normal;"><br /></span>
<br />
<div>
After spending some time building in Haskell I've come to realise that the Type system is to programming as Object Orientation is to <a href="http://sixtyk.blogspot.ch/2013/03/dynamic-scope-pattern.html">global variables</a>. That is, OO didn't remove global variables it simply demarcated them. It's still possible that a variable ends up with an unexpected value caused by some member function with no quick way of determining how it happened [2], but we can isolate exactly which methods could be responsible because non-members can't access the member variables at all.</div>
<div>
<br /></div>
<div>
The type system is similar: you can still have what ever types you want. If you want a variable that can be an int, a float, a string or a character and automatically convert between the two you can do that. But what Haskell's type system does for you is demarcate <i>where</i> this can happen. You can still end up with a character where you expected a float, but only in those places were you've chosen those kinds of types. Just as OO limits your global variable surface area, Haskell limits your type based error surface area.<br />
<br /></div>
<div>
<br /></div>
<span style="font-size: x-large;">All dressed up...</span><br />
<span style="font-size: x-large; font-weight: normal;"><br /></span>
<br />
<div>
So what am I going to do exactly. I'm planning on making a content management system in Haskell. :) I know about the web systems out there but none of them work exactly as I want. I don't like most CMS because they require so many different technologies (i.e. moving parts) to get into production. They all have to be front ended by some web server, a caching server, etc., etc. if they hope to achieve any kind of scale at all.</div>
<div>
<br /></div>
<div>
Haskell, in contrast, has Warp. A fast web serving base that can break the dreaded 10k/s barrier. Hopefully I can successfully leverage it to create something nice and unique. If it doesn't work out, I'm sure I'll learn a lot at least.</div>
<div>
<br /></div>
<div>
My next series of posts will be documenting building the system so I suppose they can function as a kind of practical newbie guide [3]. You can also follow my progress (or lack thereof) via my <a href="https://github.com/jason-johnson/hyper-cms">repository</a>.</div>
<div>
<br /></div>
<div>
[1] This might seem like an odd sentiment given that my day job is C# coding, but the C# type system isn't as strict as Haskell's.</div>
<div>
<br /></div>
<div>
[2] Well, a watch statement is pretty quick. But seeing the offender in the stack trace is even easier.<br />
<br />
[3] or maybe anti-guide! If you want to use haskell use a pro like <a href="http://learnyouahaskell.com/">"Learn you a haskell"</a> or <a href="http://book.realworldhaskell.org/">"Real world haskel"</a>)</div>
Jasonhttp://www.blogger.com/profile/17941750539814842623noreply@blogger.com0tag:blogger.com,1999:blog-98105706403938980.post-45280091583218091912013-03-12T09:02:00.001-07:002013-03-12T09:02:09.055-07:00Dynamic scope pattern[Noticed this was unpublished so I'll just publish it now]<br />
<br />
Recently I noticed some confusion about dynamic scoping on Reddit. Many of the comments were even bashing dynamic scoping as something one would never need. The fact of the matter is you probably use dynamic scoping already, you just do it by hand.<br />
<br />
<span style="font-size: medium; font-weight: bold;">What are global variables?</span><br />
<br />
To understand what dynamic scoping is and why you are using it now, we need to understand global variables. So what is a global variable? A global variable is an implicit parameter to one or more functions [1]. Rather then pass some variable to 20 different functions, most of which simply pass it on to the functions they call, you make a global variable.<br />
<br />
From this definition we can see that OO programing also has "global" variables. They are just restricted to specific classes (class/static variables) or instances of a class (instance variables).<br />
<br />
Global variables, while convenient, a double edged sword. The problem is that any of these functions could change the variable without leaving a trace. That is, given the following:<br />
<code><br />int important_variable = 100;<br />void main()<br />{<br />first();<br />second();<br />third();<br />}</code><br />
If the code crashes somewhere in second() or third() due to important_variable being changed, it is difficult to tell what changed it. If some function called by first() changed the variable then by the time one of the later functions crashes, the offender wont be on the stack anymore.<br />
<br />
<span style="font-size: medium; font-weight: bold;">Dynamic scoping - global variables done right</span><br />
One thing that would help with the above problem is if we could say that important_variable has a fixed value and can only be rebound within a specific context. This way, if our code crashes because important_variable was changed we can look at the back trace. The offending function will have to be in it. In C++ we might do something like this:<br />
<code><br />template <value> class DynamicSet {<br /> Value* _var;<br /> Value _oldVal;<br /><br /> DynamicSet(Value* var, Value newValue) : _var(var), _oldVal(*var) {<br /> *_var = newValue;<br /> }<br /><br /> ~DynamicSet() { *_var = _oldVal; }<br />}<br /><br />void first()<br />{<br /> DynamicSet(&important_variable, 9);<br /> do_something();<br /> do_something_else();<br /> // ...<br />}</value></code><br />
Here we take advantage of C++'s destructors to make sure the variable is put back as it was no matter how the current stack frame is exited. This is how dynamic scoping works.<br />
<br />
<span style="font-size: medium;"><span style="font-weight: bold;">Relationship between exception handling and dynamic variables</span></span><br />
<br />
The reddit thread also mentioned exception handling. The interesting thing here is that exception handling can be implemented in terms of dynamic scoped variables (some languages do exactly that) and dynamic variables can be implemented with exceptions if you have restartable exceptions (I've seen a dynamic variable implementation in Squeak that worked like this).<br />
<br />
[1] Well, it could be zero, but that wouldn't make much sense. :)Jasonhttp://www.blogger.com/profile/17941750539814842623noreply@blogger.com4tag:blogger.com,1999:blog-98105706403938980.post-2101108215205851392007-12-05T21:00:00.000-08:002007-12-06T12:15:07.829-08:00Defining new language constructsI was recently involved in a conversation about the flexibility of different languages. Once a person has used a language like Smalltalk or Lisp, one measure they will probably use will be how hard it is to add new constructs to the language that behave as "built in" ones.<br /><br />In Smalltalk this is easy because all constructs are written within the language using blocks (which serve the purpose of "closures"), so any methods you define can easily behave as standard constructs that come with the language. It also helps that you can add your methods to any class, including existing ones like True where certain operations make more sense.<br /><br />In other languages this takes more thought because the constructs don't use explicitly delayed execution the way Smalltalk does. For example the lisp code:<br /><br /><pre><br />> (if (some-function) (true-case) (false-case))<br /></pre><br /><br />may look like a regular function call but the last two forms don't get evaluated until the first one completes, at which point only one of them will be.<br /><br />Now lets imagine in one of our projects we are seeing a great deal of code that looks like:<br /><br /><pre><br />> (if (and (case-one) (case-two))<br />> (true-case)<br />> (false-case))<br /></pre><br /><br />but perhaps it would be clearer to make a new construct called 'ifboth. E.g.<br /><br /><pre><br />> (ifboth ((case-one) (case-two)) (true-case) (false-case))<br /></pre><br /><br />But how can we do this? We can't use a function because that would evaluate all these forms before calling our new construct. Luckily Lisp comes to the rescue with macros. A macro receives the forms unevaluated as lists, and is free to do what it needs with them. It simply needs to return a list that will be the expanded code. In our case the macro is pretty simple:<br /><br /><pre><br />> (defmacro ifboth ((case-one case-two) true-case false-case)<br />> `(if (and ,case-one ,case-two)<br />> ,true-case<br />> ,false-case))<br /></pre><br /><br />I wont go into the details of how this works as there are plenty of resources on the subject. The above simple takes code like:<br /><br /><pre><br />> (ifboth ((case-one) (case-two)) (true-case) (false-case))<br /></pre><br /><br />And turns it into code like:<br /><br /><pre><br />> (if (and (case-one) (case-two))<br />> (true-case)<br />> (false-case))<br /></pre><br /><br />at compile time.<br /><br />But this got me thinking about Haskell. Can Haskell do this? They don't have macros after all. <span style="font-style: italic;font-size:78%;" >1</span><br /><br />It turns out Haskell can. Lisp has to use macros because it needs a way of controlling the evaluation of the forms.<span style="font-size:78%;"> <span style="font-style: italic;">2</span></span> In Haskell forms are always delayed until actually used. So how can we make ifboth for Haskell?<br /><br /><pre><br />> ifboth :: Bool -> Bool -> a -> a -> a<br />> ifboth True True trueCase = trueCase<br />> ifboth _ _ _ falseCase = falseCase<br /></pre><br /><br />And test it in ghci with:<br /><br /><code><br />> ifboth True True (print "hi") (print "bye") {- prints "hi" -}<br />> ifboth True False (print "hi") (print "bye") {- prints "bye" -}<br /></code><br /><br />If one spent a while programming in Haskell with it's lazy evaluated, "only evaluate if the value is actually used" nature, one might forget that defining new language constructs actually requires extra thought in some languages. And isn't even possible in others.<br /><br />1. Actually they do have several different implementations of "macro"-ish systems, but they are not needed for things like delayed evaluation as the language simply works this way.<br /><br />2. Of course Lisp macros have many uses beyond this, but the delayed evaluation is handy in this case.Jasonhttp://www.blogger.com/profile/17941750539814842623noreply@blogger.com1tag:blogger.com,1999:blog-98105706403938980.post-30593781767725934822007-05-14T13:45:00.000-07:002007-05-14T13:52:47.740-07:00Keeping focusOne of things I love about the Smalltalk system is how it lets me keep focus on what I'm doing. Working on my friend's website, there are lots of pieces of data that I make pages to display, as any programmer can relate. And also as any programmer can relate, I have the issue of how to populate this "model data" to test if my display pages are working.<br /><br />So I have two choices: (1) I can create the whole GUI that lets one enter data or (2) I can put some extra code in my program to populate the data, for testing.<br /><br />The problem with 2 is not big, just that I'm wasting time writing stuff I will have to take out later, just so I can test. Choice 1 doesn't have that problem, but it does break my focus. I have to stop working on what I am really interested in: the display pages, to work on a data input page(s).<br /><br />But Smalltalk has another option (my favorite in fact): I can simply navigate through the live web site objects (via 'find instance of') until I find pages and insert the model data directly.<br /><br />This way I can keep on working on what I am focussed on right now. In traditional languages you must choose one of the above 2 options, but in Smalltalk option 2 has been done for you generally in the tools.Jasonhttp://www.blogger.com/profile/17941750539814842623noreply@blogger.com0tag:blogger.com,1999:blog-98105706403938980.post-43143134266297910682007-05-12T01:57:00.000-07:002007-05-12T01:59:55.917-07:00ThreadingHere is a post I made recently at http://lists.squeakfoundation.org/pipermail/squeak-dev/2007-February/114181.html.<br /><br />Basically my summary of the state of concurrent processing today (reproduced below).<br /><br /><pre>Afaik there are 3 ways of handling true concurrent execution (i.e. not green<br />threads):<br /><br />1) Fine-grained locking/shared thread state:<br /><br />The old way of running heavy weight threads, sharing memory across threads<br />and using some kind of locking to protect against race conditions.<br /><br />Positive: Hrm, well I guess there is the most support for this, since it is<br />probably the most common. If you don't use any locking but only read the<br />data shared this is very fast approach.<br /><br />Negative: It simply doesn't scale well. It also doesn't compose well. You<br />can't simply put two independently created pieces of code together that use<br />locking and expect it to work. Stated another way, fine-grained locking is<br />the manual memory management of concurrency methodologies [1]. If any part<br />of your code is doing fine-grain locking, you can never "just use it"<br />somewhere else. You have to dig deep down in every method to make sure you<br />aren't going to cause a deadlock.<br /><br />This one would probably be very hard to add to Squeak based on what John<br />said.<br /><br />2) Shared state, transactional memory:<br /><br />Think relational database. You stick a series of code in an "atomic" block<br />and the system does what it has to to make it appear as the memory changes<br />occurred atomically.<br /><br />Positive: This approach affords some composability. You still should know<br />if the methods your calling are going to operate on memory, but in the case<br />that you put two pieces of code together that you know will, you can just<br />slap an atomic block around them and it works. The system can also ensure<br />that nested atomic blocks work as expected to further aid composability. <br />This approach can often require very few changes to existing code to make it<br />thread safe. And finally, you can still have all (most?) of the benefits of<br />thread-shared memory without having to give up so much abstraction (i.e.<br />work at such a low level).<br /><br />Negative: To continue the above analogy, I consider this one the "reference<br />counted memory management" of options. That is, it works as expected, but<br />can end up taking more resources and time in the end. My concern with this<br />approach is that it still does need some knowledge of what the code you are<br />calling does at a lower level. And most people aren't going to want to<br />worry about it so they will just stick "atomic" everywhere. That probably<br />wont hurt anything, but it forces the system to keep track of a lot more<br />things then it should, and this bookkeeping is not free.<br /><br />This one would also require some VM (probably very big) changes to support<br />and could be tough to get right.<br /><br />3) Share nothing message passing:<br /><br />Basically, no threads, only independent processes that send messages between<br />each other to get work done.<br /><br />Positive: This approach also allows a high level of composability. If you<br />get new requirements, you typically add new processes to deal with them. <br />And at last, you don't have to think about what the other "guy" is going to<br />do. A system designed in this manner is very scalable; in Erlang for<br />example, a message send doesn't have to worry if it is sending to a local<br />process or a totally different computer. A message send is a message send. <br />There is no locking at all in this system, so no process is sleeping waiting<br />for some other process to get finished with a resource it wants (low level<br />concerns). Instead a process will block waiting for another process to give<br />him work (high level concerns).<br /><br />Negative: This requires a new way of architecting in the places that use<br />it. What we are used to is; call a function and wait for an answer. An<br />approach like this works best if your message senders never care about<br />answers. The "main loop" sends out work, the consumers consume it and<br />generate output that they send to other consumers (i.e. not the main loop). <br />In some cases, what we would normally do in a method is done in a whole<br />other process. Code that uses this in smalltalk will also have to take<br />care, as we *do* have state that could leak to local processes. We would<br />either have to make a big change how #fork and co. work today to ensure no<br />information can be shared, or we would have to take care in our coding that<br />we don't make changes to data that might be shared.<br /><br />I think this one would be, by far, the easiest to add to Squeak (unless we<br />have to change #fork and co, of course). I think the same code that writes<br />out objects to a file could be used to serialize them over the network. The<br />system/package/whatever can check the recipient of a message send to decide<br />if it is a local call that doesn't need to be serialized or not.<br /><br />[1] The big win usually cited for GC's is something to the effect of "well,<br />people forget to clean up after themselves and this frees up their time by<br />not making them". But really, the big win was composability. In any<br />GC-less system, it is always a nightmare of who has responsibility for<br />deleting what, when. You can't just use a new vendor API, you have to know<br />if it cleans up after itself, do I have to do it, is there some API I call? <br />With a GC you just forget about it, use the API and everything works.</pre>Jasonhttp://www.blogger.com/profile/17941750539814842623noreply@blogger.com2tag:blogger.com,1999:blog-98105706403938980.post-54219135017913384692007-02-02T09:10:00.000-08:002008-12-22T09:32:53.801-08:00A case of caseIn all the documentation I have seen on Smalltalk, everyone always points out how to use #ifTrue:ifFalse and why it works this way. Ok, got it. But at some point, one needs more branching control then just if/else.<br /><br />Now I know; anytime you branch you could accomplish the same thing using method overriding [1], but it isn't always practicle to break out a new class for a one time thing. This is the reason there is a #ifTrue:ifFalse in the first place, no doubt.<br /><br />But what we can learn from #ifTrue:ifFalse is, if there is something the language doesn't provide that you need, just put it in. :) I was writting a complicated function some time back where I ran into just this case. I was having to nest if statements, but the problem was, each condition was complex. So several of the conditions had to be repeated in different if statements. Obvious code smell. "If only I Smalltalk had case!" I thought. Wait a minute, why don't I just write one? I didn't handle the general case, I just made a method that handled my specific case.<br /><br />onFirstComplexCase: aFirstBlock orSecond: aSecondBlock else: aLastBlock<br /><br />Of course using this method instantly made a 20 line [2], complicated and thus, unclear method into a short, very readable one. And then today, while tracking something else in the Object class I found that someone else thought of this first and did the more general solution. :)<br /><br />caseOf: anAssociationCollection otherwise: aFailBlock<br /><br /> anAssociationCollection associationsDo: [:assoc|<br /> assoc key value = self ifTrue: [ ^ assoc value ]<br /> ].<br /> aFailBlock value.<br /><br />Very nice. This lets us do something like:<br /><br /> event<br /> caseOf: {<br /> [ #clicked ] -> [ self handleClick ].<br /> [ #resized ] -> [ self handleResize ].<br /> " ... "<br /> }<br /> otherwise: [ self error: 'unknown event: ', event asString ]<br /><br />Very cool. It looks like an ML style caseOf statement. But this still doesn't handle my case does it? My cases are much more complicated then just comparing a class to something. You have, no doubt, figured this out, but just in case; how does the case method work again? It evaluates the key and compares that result to the recieving object. So if we have a more complicated case we could just go:<br /><br /> true<br /> caseOf: {<br /> [ self hasThisTrait and: [ Date today month = 12 ] ] -> [ self goCrazy ]<br /> " ... "<br /> }<br /> otherwise: [ self error: 'logic error' ]<br /><br />[1] An if statement basically creates a branch in execution. This can also be done with a class hierarchy and method overloading (and in fact this <span style="font-weight: bold;">is</span> how the boolean hierarchy works in smalltalk).<br /><br />[2] In Smalltalk, if your method gets over 5-10 lines it is time to start looking at some refactoring. I didn't completely believe them initially when they told me, but so far it has been the case nearly every time.Jasonhttp://www.blogger.com/profile/17941750539814842623noreply@blogger.com1tag:blogger.com,1999:blog-98105706403938980.post-47896501924461356842007-02-02T09:08:00.000-08:002007-02-11T13:00:38.574-08:00Watching the watchmenTalk of monitoring tools today got me to thinking.<br /><br />Back in my C/C++ days, efficiency was my main concern. I wanted things to go as fast as possible. So I couldn't be spending instructions to do things like logging, monitoring my own performance, etc. And my code isn't going to crash anyway, so what's the point, right? Wrong. Of course my system was perfect :), but it talked to other systems that weren't. This meant my programs just didn't seem to do anything. We were completely flying blind since there was no indication of any sort of what it was doing. That was always the main push of my manager at the team (to all of us, not just me): make the systems better at monitoring themselves.<br /><br />Fast forward a few years and I am in a meeting hearing about monitoring tools that will be able to tell us things we need to know this time. This got me thinking about the nature of this beast. When you write a tool, you are putting in extra code to expose a part of your system. A different interface, or view :) then the normal. You have to, first, guess what might be a problem area so that you can write the interfaces to show it. But, as was implicitly mentioned in this meeting, you are often wrong in your guess about this. Or at least you don't expose enough.<br /><br />Our systems use the popular *4log libraries to handle logging via configuration. What most programmers do with this is put a log message at the start and end of every (!!!) function. So if you turn on debugging you will literally see every single call the system makes. Is there no better way to do this?<br /><br />I think there is. In smalltalk, a live system, I can view any part of the code any time I want. This is a good thing, but it was the first thing I saw as a negative when I started using smalltalk. So inefficient with so much of the system exposed! But is it? It has reflection, a way for us to use the system to ask the system things about itself. It's not some hack and it's not something you can "turn off" for production. It is part of it from top to bottom. It also has viewers that are designed to use this reflection to show us parts of the system. While it's running.<br /><br />So if you think about it, they did the same thing my manager ask me to do years ago, and the same thing the developers at my company are trying to do now: they are giving ways to monitor the system. But the difference is, instead of guessing which parts of the system might have problems that we need to look at, they just gave us the whole thing.<br /><br />As far as efficiency, sure, raw C++ (and even Java) may be more performant then raw smalltalk. But if end the end you wind up doing things like calling two logging functions per normal function, every time, then how much more efficient is your code? After all, Smalltalk isn't calling into the viewers for every function. Only if you use them. And you never have to go back and add new monitoring tools because you guessed wrong.Jasonhttp://www.blogger.com/profile/17941750539814842623noreply@blogger.com0tag:blogger.com,1999:blog-98105706403938980.post-11968681741374341722007-02-01T12:16:00.000-08:002007-02-01T12:53:35.764-08:00The world from 60,000 feetWell, I haven't gotten involved in this whole blog thing up until now but... well... why not.<br /><br />This blog is probably going to mostly be about high level programming languages. I think the tide is slowly starting to shift toward higher level, more productive programming languages.<br /><br />I know it has for me. I was a Perl programmer for more then half a decade, but I always hated that language. My favorite language at the time was C++. It was syntax I knew from C, but it was the most powerful language I had seen yet. You could change and add to the language! This is what drew me to it. Power.<br /><br />About 6 months ago I got back into programming a bit and started looking around. A new friend had ask me to take over his web site. He is pretty talented at graphical stuff, but running a web site always winds up involving technical details that he didn't feel comfortable doing. And since I "do computers" he figured I wouldn't mind taking it.<br /><br />But the fact is I always hated web programming. Stateless programming, templates, etc. I just didn't like it at all. But looking around something caught my eye: a little web framework called "Seaside".<br /><br />Now the last I heard, PHP was the king of web programming and PHP was similar to Perl. Not to mention a heavy user of templates. So I wasn't interested at that at all. But this Seaside thing was talked about like it was quite the big deal, so I took a look.<br /><br />They had this little counter, just a 0 with a ++ and -- under it. When you click on one of those you affect the number. At first I was pretty underwhelmed, but I clicked the button for "show source code". Now I didn't know anything at all about smalltalk, but I saw something that really grabbed my attention.<br /><br />All the code did was update the object. No stuffing variables into some hidden field, writing files, none of that. Just updates and object and nothing more. I read everything on the site, just to see how they did this, so I could steal, er, use it for whatever system I made. But the more I looked, the more crazy it got.<br /><br />So I downloaded Smalltalk, it wasn't that intimidating. Just download a file, click it and it runs. No installer menu filling my registry with God knows what. Just a nice little executable like 'putty' (an SSH tool).<br /><br />Gradually, my love for C++ slipped away. Replaced with something new. A language that was even <span style="font-weight: bold;">more</span> powerful, but at the same time <span style="font-weight: bold;">much</span> simpler. C++ gained some of it's power allowing you to override what a given operator means for certain classes. Since smalltalk is written in itself, it goes so much further. All the <span style="font-weight: bold;">control statements</span> are written in the language, and thus accessible to the coder. Or you can write your own.<br /><br />The other big thing was this concept of a "live" system. A Perl system I wrote way back when had to do some operations on a very large number of network devices. Unfortunately, at close to device 59,000 something bad happened. A bug. Rather then try to guess what was happening and do some logging, I just ran the whole system in the debugger. The system normally took 6 hours to run. In the debugger, running single threaded, it was much more then that. This meant I got 1 chance per day to try and figure out what the problem was. But with logging it would have been the same.<br /><br />Finally, after about the 3rd or 4th day I found it. I don't remember the details, but I do remember that I had a fix in about 30 seconds after seeing what was going on. But the problem is, the devices <span style="font-weight: bold;">behind</span> this one had not been backed up for some time now and some people were getting more then a little excited about this. But I only had a couple of thousand devices to go, why not just start from here?<br /><br />Well, because I couldn't. There was nothing I would be able to do from inside the debugger to convince the program to go on from this point, and starting again meant waiting another day. Obviously it wasn't a huge deal, or I could have written a little "one off" to touch the untouched devices (and maybe I even did, I don't remember). But it was something I wished I could have done, but something like that is just impossible. Or is it?<br /><br />Not with Smalltalk (or Lisp as it turns out). In Smalltalk, I sometimes write portions of the code from the debugger, since this is the best place to get instant feedback of what the code is doing.<br /><br />I will talk more about this later, perhaps. But let me just finish with, going to smalltalk has also peaked my interest in other high level languages as well. I am looking at Haskell (incredible language as well, and possibly the most terse there is) as well, and keep turning my head toward Erlang, but I haven't bit quite yet.Jasonhttp://www.blogger.com/profile/17941750539814842623noreply@blogger.com0