When I first started to work with web applications, it was just servlets and then came JSPs. At the time, I didn't like JSPs (and to this day I still don't). They seemed like a necessary evil that got the job done. The thing I found repulsive about them was the mix of server and client code together. The switching of contexts just made both look ugly and hard to maintain. But, it was easier than generating pages directly from servlets by hand. Of course, there were other solutions that I looked for, but nothing got away from the horrid markup. Then, I fell in love with Seaside which did away with markup and generated content through a custom DSL. It felt better to me and loved it until I realized that it was only for the developer, not the web page designer. It made their life harder. Now, we all know we should use minimal content and have CSS do the layout. But, in this modern world, you need folks who are experts at web page design. You can not and should not alienate them.
Fast forward to now. Currently, I've been using Javascript frameworks and working mainly from the front-end. The project I'm currently working on is pure HTML/Javascript/CSS for the front-end with RESTful calls to the backend. I must admit, I had my reservations about this arrangement, but after working with it; it is the right thing to do. No more ugly markup. The separation of concerns is well in place. And finally, the web designer is allowed to concentrate on making a rich user experience and the developer to concentrate on business logic. Perfect.
My feeling is that the days of template engines are over. It's a waste of time and all of them are a pain to debug. A pure Web 2.0 AJAX solution allows each to utilize their tools and do things separately. To me that is exciting and I will gladly leave ugly code behind.
Showing posts with label design. Show all posts
Showing posts with label design. Show all posts
Saturday, June 04, 2011
Friday, January 21, 2011
Domain Driven Design Immersion
I've been meaning to write about my experience at the Domain Driven Design Immersion training that I took back in November. But, I've been so busy that I haven't had the time to put down my thoughts on it properly until today. First off, I had a great time; met many wonderful and intelligent people; and learned quite a few new techniques. Just given that, it was well worth the time. The course was simply fantastic.
I read Domain-Driven Design
when it first came and fell in love with it. I've read it several times and consider it one of the greatest books on design. It's a tome of knowledge on being practical in design and getting closer to mapping user language to code. There are no silver bullets, just great advice and a road map of thought to get there. Despite the book being well-written, there were questions I've had when applying DDD throughout the years. I jumped at the chance to spend a week immersed in DDD and learn a few more tricks.
The training was excellent: well-paced, interactive, and loads of discussion. DDD is something that you must practice and the training provides a lot of it. From hands on coding to white board design sessions, you get your mind in the middle of the whole process. The trainers guided discussion and encouraged us to be creative in our solutions: messy whiteboards are good! I've been to a lot of training and seminars. Normally, little thought was given to pacing of the material. This was not the case with DDD Immersion. I felt there was an excellent mix of lecture, hands-on, and discussions. Each day felt like it went by too fast. Four days is not a lot of time to cram the amount of material that the DDD book covers, but the course materials provided abbreviated documents to bullet point the important ideas. I personally thought the materials made the course all worth it.
Even if you have read the DDD book, I still think the training is worth it's weight in gold. I work with Eric on his Time and Money library and I still got new ideas out of it. The training does add new material to the book and expands on Eric's new ideas concerning DDD.
The one thing that I took from the training is it's all about practicing to get better at design. Eric has laid out a path of thought on design. It still takes ingenuity and playing around with ideas to be successful. Design is a creative process and it takes hard thinking to match the language of users into code. The training brings that home. I whole heartily recommend the training and would go again given the chance. The real world keep giving me problems where questions arise to the best way to tackle them. You can never stop learning.
I read Domain-Driven Design
The training was excellent: well-paced, interactive, and loads of discussion. DDD is something that you must practice and the training provides a lot of it. From hands on coding to white board design sessions, you get your mind in the middle of the whole process. The trainers guided discussion and encouraged us to be creative in our solutions: messy whiteboards are good! I've been to a lot of training and seminars. Normally, little thought was given to pacing of the material. This was not the case with DDD Immersion. I felt there was an excellent mix of lecture, hands-on, and discussions. Each day felt like it went by too fast. Four days is not a lot of time to cram the amount of material that the DDD book covers, but the course materials provided abbreviated documents to bullet point the important ideas. I personally thought the materials made the course all worth it.
Even if you have read the DDD book, I still think the training is worth it's weight in gold. I work with Eric on his Time and Money library and I still got new ideas out of it. The training does add new material to the book and expands on Eric's new ideas concerning DDD.
The one thing that I took from the training is it's all about practicing to get better at design. Eric has laid out a path of thought on design. It still takes ingenuity and playing around with ideas to be successful. Design is a creative process and it takes hard thinking to match the language of users into code. The training brings that home. I whole heartily recommend the training and would go again given the chance. The real world keep giving me problems where questions arise to the best way to tackle them. You can never stop learning.
Sunday, August 29, 2010
Imitation and Repetition
I was recently reading Alice Cooper, Golf Monster: A Rock 'n' Roller's 12 Steps to Becoming a Golf Addict
and had an epiphany. In one of the chapters Alice talks about the way to be great at anything. He explains one should mimic the greats by watching and then repeat what they do until it's natural. I immediately started to think about programming, of course. We all have programmers that we admire. For me, most of my admired heros are great writers. It's their advice that I adore. I soak their words in and then practice what they talk about. But, it's not the same as reading code. What Alice was talking about was studying what the greats do and repeating it until it's as natural as the way you breathe. Where can we find great code? I always felt my programming took a huge leap forward with Smalltalk. The reason I think that is now crystal clear: Smalltalk encourages you to read other people's code. All of the tools embrace reading code. If you don't know how to do something, you can search via the tools to find something similar and then repeat what they did. There is a plethora of patterns and good programming in any Smalltalk system. All of the GoF patterns came from Smalltalk originally.
That's fine and dandy, but what about Java, Python, Ruby, or other developers? It's just as easy as finding an open source framework you love and diving head first into the code. Study it, practice writing in that style, and repeat until it feels natural. Open source gives us opportunities that only Smalltalk and Lisp did in the past: a limitless supply of software to study in any language we choose. But, this begs the question: How do I know I'm studying good code? Alice touches on this in his book by saying that as you pass milestones in competence, you know what the next level looks like. Start with code that looks good to you now and keep practicing. For me, I strive for readability. How can I remove all noise until the code I write is nothing but the language of the problem I'm trying to solve. I'm always reading code to find new techniques for removing noise. I study code written in different languages and see how I can apply any new techniques in the language I am currently using. I've found a lot of gems by studying from functional programming and applying them to object-oriented languages.
This brings up another interesting observation. If it's good to read code and practice writing it, when should you use a new technique? For me, I always practice a new technique a lot before I apply it to production code. Normally, once a technique feels natural and I know it like the back of my hand. Most of the open source libraries I've written have been to explore an idea that I saw somewhere else to play around with it. There's nothing worse than not having mastery and immediately applying it because it was new at the time. Be patient. Read, code, repeat. Always be reading and practicing. It's the only way to get better.
That's fine and dandy, but what about Java, Python, Ruby, or other developers? It's just as easy as finding an open source framework you love and diving head first into the code. Study it, practice writing in that style, and repeat until it feels natural. Open source gives us opportunities that only Smalltalk and Lisp did in the past: a limitless supply of software to study in any language we choose. But, this begs the question: How do I know I'm studying good code? Alice touches on this in his book by saying that as you pass milestones in competence, you know what the next level looks like. Start with code that looks good to you now and keep practicing. For me, I strive for readability. How can I remove all noise until the code I write is nothing but the language of the problem I'm trying to solve. I'm always reading code to find new techniques for removing noise. I study code written in different languages and see how I can apply any new techniques in the language I am currently using. I've found a lot of gems by studying from functional programming and applying them to object-oriented languages.
This brings up another interesting observation. If it's good to read code and practice writing it, when should you use a new technique? For me, I always practice a new technique a lot before I apply it to production code. Normally, once a technique feels natural and I know it like the back of my hand. Most of the open source libraries I've written have been to explore an idea that I saw somewhere else to play around with it. There's nothing worse than not having mastery and immediately applying it because it was new at the time. Be patient. Read, code, repeat. Always be reading and practicing. It's the only way to get better.
Saturday, October 17, 2009
Devilishly Clever
I was doing my usual research on Python when I ran across this recipe to make tail recursive calls not blow the stack in Python: Tail Call Optimization Decorator. I read through the code and thought, "Wow! This is devilishly clever!" As a thought exercise, I think it's awesome. But, it got me thinking about clever and production code. In my opinion, clever is never good or wanted in production code. It's great to learn and understand clever code though. It's a great mental workout to keep you sharp.
So, what's my point with all of this besides to say that clever is bad? The example in the above link is factorial (which as everyone knows is hated by me, but that's another story). But, the amazing thing about factorial examples are that they are dead simple, yet there's several ways to get the answer. Here's a few that I came up with:
Each of these compute factorial. Amazingly, there's even more ways than what I listed (the math wizards in the audience know what they are). Now, think about this: Computing factorial is dead simple. What happens when we get to harder problems? Being clever can actually get in the way of making the code easy to understand. It might even kill performance. Let's think back to the devilishly clever code. The performance of making Python tail recursive is awful. Sure, it's pure and tail recursive, but in production code that is deadly. What we want is simple and to the point. It's why I generally like solutions that need less code. There's less noise to get in the way of understanding and generally can mean better performance as well.
Real world problems are hardly ever as straightforward as factorial. The balancing act comes when you drop a solution because it's not working for whatever reason. Raising and catching exceptions so you can have a tail recursive factorial is overkill. It took more code than the non-recursive version. It begs for the programmer to know their tool set and to know how to solve problems in that tool set that are straightforward. Tail recursion is powerful in languages like Haskell and Erlang. But, there's always another way of doing things that can make more sense in the language you are using. In our case, the other ways were just as easy yet more scalable for our tool set. Food for thought the next time you go down the path of clever and end up writing more noise than solution.
So, what's my point with all of this besides to say that clever is bad? The example in the above link is factorial (which as everyone knows is hated by me, but that's another story). But, the amazing thing about factorial examples are that they are dead simple, yet there's several ways to get the answer. Here's a few that I came up with:
def reg_fact(x):
if x is 1:
return 1
return reg_fact(x - 1) * x
def tail_fact(x):
def func(acc, x):
if x is 1:
return acc
return func(acc * x, x - 1)
return func(1, x)
def not_rec_fact(x):
result = 1
for each in range(2, x + 1):
result *= each
return result
def not_rec_fact_fancy(x):
return reduce(lambda result, each: result * each, range(1, x + 1))
import operator
def not_rec_fact_super_fancy(x):
return reduce(operator.mul, range(1, x + 1))
Each of these compute factorial. Amazingly, there's even more ways than what I listed (the math wizards in the audience know what they are). Now, think about this: Computing factorial is dead simple. What happens when we get to harder problems? Being clever can actually get in the way of making the code easy to understand. It might even kill performance. Let's think back to the devilishly clever code. The performance of making Python tail recursive is awful. Sure, it's pure and tail recursive, but in production code that is deadly. What we want is simple and to the point. It's why I generally like solutions that need less code. There's less noise to get in the way of understanding and generally can mean better performance as well.
Real world problems are hardly ever as straightforward as factorial. The balancing act comes when you drop a solution because it's not working for whatever reason. Raising and catching exceptions so you can have a tail recursive factorial is overkill. It took more code than the non-recursive version. It begs for the programmer to know their tool set and to know how to solve problems in that tool set that are straightforward. Tail recursion is powerful in languages like Haskell and Erlang. But, there's always another way of doing things that can make more sense in the language you are using. In our case, the other ways were just as easy yet more scalable for our tool set. Food for thought the next time you go down the path of clever and end up writing more noise than solution.
Sunday, July 13, 2008
Functional Programming Has Warped Me
Look at this method that I recently wrote in a class for describing my database. The responsibility of this method is to commit the current session (which is encapsulated from the user) after x number of database events (adds, updates, deletes, etc). I need his functionality because I was bulk inserting objects that I had read from an XML feed. Here's what I first came up with:
The method takes a one argument block that will be called with a zero argument block that when called will increase the count and decide whether to commit and start a new transaction if it has reached the number given. At the end, I commit whatever active transaction there is. The thing I like about this is that I put the logic of counting and when to commit in one place. Here's how it would be used:
Notice how "afterAdd" is used. It is passed into the block that will handle all of the transaction adds. It is called after an item has been added. I like this code because it doesn't worry about anything, but adding things to the database. It also makes it obvious how often we will commit. But, I'm unhappy with commitEvery:during:. The reason is because it just seems like I could make it more generic and at the same time easier to test without resorting to mocks. I abstracted out the counting and put it as a method on BlockContext (this is Squeak):
This turns our first method into this:
It's a mini-DSL! Let's write a simple test. Note, this is not an SUnit test, it simply writes to the transcript. But, I wanted to show it without all of the assertions:
Gives this output:
Everything looks wonderful until we hit the last two lines. It seems pointless and unnecessary to do a begin and end with nothing in between. Our code will need to get a little bit more complex. Here's my next try:
It's more complicated. And now, I'm passing another block around to get its value. The reason was because to properly remove empty begin/end combinations, I needed to know when the before was and when the after was. This means passing a block to the afterAdd from before. So, now the test code looks like this:
I renamed afterAdd to onEachAdd which is a better name and indicates its role better. Now, the reason I wrote this post is that I sat back and went, "Wow. I have something easily testable, but might hurt some brains." My next thought was, "Why would this hurt someone's brain?" The answer is all of the indirection of the blocks. I will be quite honest at this point, I would normally start turning all of these blocks into objects and have better names for what they were doing. Blocks worked perfectly in this context and if you think about it. This code is not much different than what Collection>>streamContents: does.
This is one of the things that learning functional programming has done to me. The above code looks and feels natural. Passing a block into a method that will accept another block doesn't bother me at all. I love the DSL-like nature of the implementation too, but I can see where it might confuse (Hell, inject:into: still does). This is where objects come in because now we can create our small objects with more meaningful names than just value or value:, this will at least make the indirection less painful. Thought this was fun exercise to show off something else that you can do with higher order functions. Squeak on.
commitEvery: aNumber during: aBlock
| count |
count := 1.
[self session beginUnitOfWork.
aBlock value:
[count := count + 1.
count > aNumber
ifTrue:
[self session commitUnitOfWork.
self session beginUnitOfWork.
count := 1]]].
self session commitUnitOfWork
The method takes a one argument block that will be called with a zero argument block that when called will increase the count and decide whether to commit and start a new transaction if it has reached the number given. At the end, I commit whatever active transaction there is. The thing I like about this is that I put the logic of counting and when to commit in one place. Here's how it would be used:
populate
| db |
db := self new.
[db
commitEvery: 1000
during:
[:afterAdd |
ITunesSAXHandler readUsing:
(ITunesTracksHandler onEachTrackDo:
[:each |
db addTrack: each.
afterAdd value])]]
ensure: [db disconnect]
Notice how "afterAdd" is used. It is passed into the block that will handle all of the transaction adds. It is called after an item has been added. I like this code because it doesn't worry about anything, but adding things to the database. It also makes it obvious how often we will commit. But, I'm unhappy with commitEvery:during:. The reason is because it just seems like I could make it more generic and at the same time easier to test without resorting to mocks. I abstracted out the counting and put it as a method on BlockContext (this is Squeak):
duringDoEvery: aNumber onBegin: beginBlock onEnd: endBlock
| count result |
count := 1.
beginBlock value.
result := self value:
[ count := count + 1.
count > aNumber ifTrue:
[ endBlock value.
beginBlock value.
count := 1 ] ].
endBlock value.
^result
This turns our first method into this:
commitEvery: aNumber during: aBlock
^aBlock
duringDoEvery: 1000
onBegin: [ self session beginUnitOfWork ]
onEnd: [ self session commitUnitOfWork ]
It's a mini-DSL! Let's write a simple test. Note, this is not an SUnit test, it simply writes to the transcript. But, I wanted to show it without all of the assertions:
[:afterAdd |
| count |
count := 0.
10 timesRepeat:
[Transcript show: (count := count + 1) printString; cr.
afterAdd value]]
duringDoEvery: 5
onBegin: [Transcript show: 'begin'; cr]
onEnd: [Transcript show: 'end'; cr]
Gives this output:
begin
1
2
3
4
5
end
begin
6
7
8
9
10
end
begin
end
Everything looks wonderful until we hit the last two lines. It seems pointless and unnecessary to do a begin and end with nothing in between. Our code will need to get a little bit more complex. Here's my next try:
duringDoEvery: aNumber onBegin: beginBlock onEnd: endBlock
| count result hasBegun |
count := 1.
hasBegun := false.
result := self value:
[ :eachBlock |
count := count + 1.
hasBegun ifFalse:
[ beginBlock value.
hasBegun := true ].
eachBlock value.
count > aNumber ifTrue:
[ endBlock value.
hasBegun := false.
count := 1 ] ].
hasBegun ifTrue: [ endBlock value ].
^ result
It's more complicated. And now, I'm passing another block around to get its value. The reason was because to properly remove empty begin/end combinations, I needed to know when the before was and when the after was. This means passing a block to the afterAdd from before. So, now the test code looks like this:
[:onEachAdd |
| count |
count := 0.
10 timesRepeat:
[onEachAdd value:
[Transcript show: (count := count + 1) printString; cr]]]
duringDoEvery: 5
onBegin: [Transcript show: 'begin'; cr]
onEnd: [Transcript show: 'end'; cr]
I renamed afterAdd to onEachAdd which is a better name and indicates its role better. Now, the reason I wrote this post is that I sat back and went, "Wow. I have something easily testable, but might hurt some brains." My next thought was, "Why would this hurt someone's brain?" The answer is all of the indirection of the blocks. I will be quite honest at this point, I would normally start turning all of these blocks into objects and have better names for what they were doing. Blocks worked perfectly in this context and if you think about it. This code is not much different than what Collection>>streamContents: does.
This is one of the things that learning functional programming has done to me. The above code looks and feels natural. Passing a block into a method that will accept another block doesn't bother me at all. I love the DSL-like nature of the implementation too, but I can see where it might confuse (Hell, inject:into: still does). This is where objects come in because now we can create our small objects with more meaningful names than just value or value:, this will at least make the indirection less painful. Thought this was fun exercise to show off something else that you can do with higher order functions. Squeak on.
Monday, June 30, 2008
Too Complex?
There's a lot of features of Ruby that I like, but there are some that just drive me nuts like blocks not taking blocks and the ampersand operator. Raganwald did a great job of explaining blocks, procs, and the ampersand in this blog post: Explanation of Ruby's Unary operator. I came away with the feeling, "Wow! It took that much explanation just to tell how to send blocks around?" If blocks were first-class citizens, Ruby would be more elegant. Raganwald would not be writing huge blog posts on block vs. proc because it would be unneeded.
Ruby has all it needs right now. Ruby 2.0 should be a pruning of features and removing special cases. Get to the things that help you concentrate on your design and not on the language. Ampersand is nothing more than noise in your code and brain. Remember elegant is simple.
Ruby has all it needs right now. Ruby 2.0 should be a pruning of features and removing special cases. Get to the things that help you concentrate on your design and not on the language. Ampersand is nothing more than noise in your code and brain. Remember elegant is simple.
Sunday, June 29, 2008
Weakest Link
"The deliberate use of a weak element that will fail in order to protect other elements in the system from damage." - Universal Principles of Design
Unit tests could be seen as a form of weakest link in that we want the system to fail in our tests instead of in our production environment. Pre-condition and post-condition assertions (remember Bertrand Meyer don't you?) are the perfect example of weakest links in software design. The assertion fails, then the program stops or does something intelligent about the detection: logging the anomaly or dropping an incorrect record. We have more options available to us when our weak link fails in software than other domains. I would even put exception handling under the weak link design umbrella.
So, why don't we use it more? It's rare I see pre-condition assertions at all. I know the performance overlords declared them to be bad. But, when you have Rails running high traffic websites, does a few extra assertions really matter? I think not. I would rather have my program run slower with safety belts than doing something catastrophic with my data. Brian Foote once gave a talk where he equated pre and post conditions to the goalie in a game of hockey. And asked the question, "Would you want to play with or without the goalie?" It's a statement that's lived with me since.
With that being said, are they places in your current design that could use a few more fuses?
Unit tests could be seen as a form of weakest link in that we want the system to fail in our tests instead of in our production environment. Pre-condition and post-condition assertions (remember Bertrand Meyer don't you?) are the perfect example of weakest links in software design. The assertion fails, then the program stops or does something intelligent about the detection: logging the anomaly or dropping an incorrect record. We have more options available to us when our weak link fails in software than other domains. I would even put exception handling under the weak link design umbrella.
So, why don't we use it more? It's rare I see pre-condition assertions at all. I know the performance overlords declared them to be bad. But, when you have Rails running high traffic websites, does a few extra assertions really matter? I think not. I would rather have my program run slower with safety belts than doing something catastrophic with my data. Brian Foote once gave a talk where he equated pre and post conditions to the goalie in a game of hockey. And asked the question, "Would you want to play with or without the goalie?" It's a statement that's lived with me since.
With that being said, are they places in your current design that could use a few more fuses?
Sunday, May 18, 2008
And we wonder why we are one of the most hated careers
This is a shame: No Dashs or Spaces. We should be helping and not being a road block. I know I've cussed at a few of these sites listed. Sometimes I think we should put ourselves in the shoes of our customer and think, "What would make their life easier?" We should make it as easy as possible for a user to achieve their goals. The computer should disappear and only the problem at hand should be on the user's mind. We still have a long way to go. Let's start being more empathetic. The users are people too.
Friday, May 09, 2008
UML, Design, and Paper
Why did the UML design tools fail? By fail, I mean never gain popular acceptance. To this day, I still do my designs by hand and then do them in a drawing tool when they are solidified. The UML tool makers never understood that when designing, you need to be able to make mistakes and explore different options. Instead, they forced you to make decisions too early in the process so they could auto-generate your code. I always found it cumbersome and they got too much in the way. And that's why they failed. I love paper. All of my friends that design still use paper for their designs as well. The reason is that you can try out ideas and the design can always be thrown away. Design is an exploration journey. The tools only looked at the destination and not how to make the journey easier. The journey is what mattered.
Tuesday, May 06, 2008
I cried
I almost cried for joy when I read this post on one of my favorite topics: Small Classes, Short Methods. Someone has been reading my mind. I love the set of rules. It's something that I strive for in my code. I've run into problems with other developers though (they generally think I am nuts when I wrap things like phone numbers, ids, domain specific codes, etc into objects instead of strings). But, by wrapping the primitive into an object that states it's role, you start moving logic closer to where it belongs. The books "Genius Within" and "Prefactoring" were the books that really brought this home for me (especially "Genius Within"). Lots of great advice. Go read it now!
Monday, April 14, 2008
Missing Explanations
I was looking over my last post and forgot that I didn't discuss these two methods below:
It might seem a little overkill to have the everytimeButFirst function, but I think it reads better in the method and communicates the intent. Normally, you could implement putting in the comma in between items in the collection like so:
It's a little bit longer. I hate the boolean variable "shouldAddComma". I really do. It just doesn't read well. It's hard to see what's going on. The naming makes it better. But, it's just adding unnecessary noise.
But, all is not well in the first version that I used either. Not all programmers are versed in functional programming, but even a functional programmer would scoff at the first version. Why? It changes state. Plus, the implementation of everytimeButFirst while clever would not be obvious. Basically, what you're seeing is my experiment in using nothing but functions instead of a boolean. Fun for blogging and brain stretching, but not for production systems. Clever is the enemy of the maintenance programmer. But, what if we did this:
You might say I just displaced the boolean to somewhere lese and you would be right. But, I got it out of the method toString which is where I didn't like it. It's safely behind a wall where it makes sense to have. The internal implementation of everyTimeButFirst was too clever the first time around. It is still using higher-order functions, but one is easier to understand than nested ones that change themselves out.
Remember I said the original implementation would make a functional programmer gag because of the side-effects. We could make it side-effect free by making it recursive. But, Javascript is not optimized for tail-recursion. So, I did what I thought was best.
Function.prototype.everyTimeButFirst=function() {
var self=this;
var toCall=function() {
toCall=self;
return null;
};
return function() {
return toCall.apply(this, arguments);
};
}
Array.prototype.toString=function() {
var result="[";
var addComma=function() { result = result + ", " };
var addCommaOnlyAfterFirst=addComma.everyTimeButFirst();
Array.prototype.each.call(this, function(every) {
addCommaOnlyAfterFirst();
result = result + every;
});
return result + "]";
}It might seem a little overkill to have the everytimeButFirst function, but I think it reads better in the method and communicates the intent. Normally, you could implement putting in the comma in between items in the collection like so:
Array.prototype.toString=function() {
var result="[";
var shouldAddComma=false;
Array.prototype.each.call(this, function(every) {
if (shouldAddComma) {
result = result + ","
} else {
shouldAddComma=true;
}
result = result + every;
});
return result + "]";
}
It's a little bit longer. I hate the boolean variable "shouldAddComma". I really do. It just doesn't read well. It's hard to see what's going on. The naming makes it better. But, it's just adding unnecessary noise.
But, all is not well in the first version that I used either. Not all programmers are versed in functional programming, but even a functional programmer would scoff at the first version. Why? It changes state. Plus, the implementation of everytimeButFirst while clever would not be obvious. Basically, what you're seeing is my experiment in using nothing but functions instead of a boolean. Fun for blogging and brain stretching, but not for production systems. Clever is the enemy of the maintenance programmer. But, what if we did this:
Function.prototype.everyTimeButFirst=function() {
var shouldExecute=false;
return function() {
if (shouldExecute) {
return toCall.apply(this, arguments);
} else {
shouldExecute=true;
return null;
}
};
}You might say I just displaced the boolean to somewhere lese and you would be right. But, I got it out of the method toString which is where I didn't like it. It's safely behind a wall where it makes sense to have. The internal implementation of everyTimeButFirst was too clever the first time around. It is still using higher-order functions, but one is easier to understand than nested ones that change themselves out.
Remember I said the original implementation would make a functional programmer gag because of the side-effects. We could make it side-effect free by making it recursive. But, Javascript is not optimized for tail-recursion. So, I did what I thought was best.
Sunday, April 13, 2008
"Super" was wrong
My implementation of "super" was wrong. I did an implementation in this blog post. It was wrong for several reasons. The number one reason was because it was too complex. The first hint that it was too complex was when I was setting the "constructor" property manually. This is done for you and only needs to change if you set the constructor's "prototype" property with "{}" or "new Object()". I was guilty of the this sin. Turns out if I play by the rules, I don't need the "subclassFrom" function nor do I need to add the special "super" properties. It's actually less verbose and it's playing by the Javascript rules. When in Rome, do as the Romans.
My second mistake was that I thought each object had an immutable property "prototype". This is why I was getting "undefined" when I asked for it. I should have known that I was wrong and not the implementation. Anyway, my belief was WAY WRONG. I don't know where I got this piece of mythology from. I think I pulled it from the sky. The truth is that the "prototype" property is only on Function objects. It's set on the objects it constructs and it is not available. Mozilla and Adobe implementations have a property "__proto__" that stores it, but it is not in the standard and thus, not to be used. All objects by the standard should get their prototype from their "constructor" property. Learn something new everyday.
For the new implementation, I got rid of the "subclassFrom". It's not needed now and I'm only left with the "methods" function from before. It now simply moves properties from one object to another. It's only for syntactic shorthand now. I did add something new: function "proto". It's "super" basically. Here's the implementation:
So, how do you do super by playing by the rules? Here's how:
Output from the above:
Notice the "this.proto().constructor.call" in the Sub constructor function, it expands to "this.constructor.prototype.constructor.call". This is calling the "super" constructor. We could also do "arguments.callee.prototype.constructor.call" instead, but really it's a lot to type in isn't it? We also couldn't put that into a function as easily without calling "arguments.callee".
I got a better implementation and something that's more like Javascript. I like keeping my Javascript close to what a Javascript developer would expect. Prototype-based programming is powerful and it's worth exploring things without classes. But, that's a small digression. All I wanted was super and some shorthands for grouping my methods. I have that now.
My second mistake was that I thought each object had an immutable property "prototype". This is why I was getting "undefined" when I asked for it. I should have known that I was wrong and not the implementation. Anyway, my belief was WAY WRONG. I don't know where I got this piece of mythology from. I think I pulled it from the sky. The truth is that the "prototype" property is only on Function objects. It's set on the objects it constructs and it is not available. Mozilla and Adobe implementations have a property "__proto__" that stores it, but it is not in the standard and thus, not to be used. All objects by the standard should get their prototype from their "constructor" property. Learn something new everyday.
For the new implementation, I got rid of the "subclassFrom". It's not needed now and I'm only left with the "methods" function from before. It now simply moves properties from one object to another. It's only for syntactic shorthand now. I did add something new: function "proto". It's "super" basically. Here's the implementation:
Function.prototype.methods=function(funcs) {
for (each in funcs) if (funcs.hasOwnProperty(each)) {
this.prototype[each]=funcs[each];
}
}
Object.prototype.proto=function() {
return this.constructor.prototype;
}
So, how do you do super by playing by the rules? Here's how:
Function.prototype.methods=function(funcs) {
for (each in funcs) if (funcs.hasOwnProperty(each)) {
this.prototype[each]=funcs[each];
}
}
function Base() {
print("base constructor called");
}
Base.prototype.toString=function() { return "aBase"; }
function Sub() {
this.proto().constructor.call(this);
print("sub constructor called");
}
Sub.prototype=new Base();
Sub.prototype.toString=function() { return this.proto().toString.call(this) + " and aSub"; }
Output from the above:
base constructor called
********
base constructor called
aBase
********
base constructor called
sub constructor called
aBase and aSub
Notice the "this.proto().constructor.call" in the Sub constructor function, it expands to "this.constructor.prototype.constructor.call". This is calling the "super" constructor. We could also do "arguments.callee.prototype.constructor.call" instead, but really it's a lot to type in isn't it? We also couldn't put that into a function as easily without calling "arguments.callee".
I got a better implementation and something that's more like Javascript. I like keeping my Javascript close to what a Javascript developer would expect. Prototype-based programming is powerful and it's worth exploring things without classes. But, that's a small digression. All I wanted was super and some shorthands for grouping my methods. I have that now.
Annotations and Aspects in Javascript
Here's the moment, I've been building up to:
Here's the output:
Not much to explain at all. I think the code speaks for itself. The output shows the arguments passed in and what the results were from the call. I didn't return anything from either method, but it wouldn't be hard to verify that it does indeed work. I implemented my own "toString" for Arrays to print out the arguments and it's worth looking at. "arguments" is not an array, but we can call array functions on it. Most of the built-in Array functions can be called on any object. You just have to use the wordy "Array.prototype.functionName.call" to use it.
I wrote this for this blog post, but I'm planning on beefing it up with exception handling and un-weaving among other things. I also plan on adding more tests to my test suite that I did not show. The purpose was to show some cool tricks in Javascript and I hope it was fun.
And here's the entire implementation once more:
var logAround=function(func,name) {
this[name]=defineAround(function(proceedFunc) {
print("before: "+name+" "+Array.prototype.slice.call(arguments,1));
var result=proceedFunc();
print("after: "+name+" "+result);
return result;
}, func);
}
var doesItLog=function(each) {
return each.log == true;
}
Transaction.prototype.eachFunction(logAround.onlyWhen(doesItLog));
var test=new Transaction();
test.begin();
test.commit();Here's the output:
js> load("aspect.js")
before: begin []
begin
after: begin undefined
before: commit []
commit
after: commit undefinedNot much to explain at all. I think the code speaks for itself. The output shows the arguments passed in and what the results were from the call. I didn't return anything from either method, but it wouldn't be hard to verify that it does indeed work. I implemented my own "toString" for Arrays to print out the arguments and it's worth looking at. "arguments" is not an array, but we can call array functions on it. Most of the built-in Array functions can be called on any object. You just have to use the wordy "Array.prototype.functionName.call" to use it.
I wrote this for this blog post, but I'm planning on beefing it up with exception handling and un-weaving among other things. I also plan on adding more tests to my test suite that I did not show. The purpose was to show some cool tricks in Javascript and I hope it was fun.
And here's the entire implementation once more:
function defineBefore(beforeFunc, proceedFunc) {
return function() {
beforeFunc.apply(this,arguments);
return proceedFunc.apply(this, arguments);
};
}
function defineAfter(afterFunc, proceedFunc) {
return function() {
var result=proceedFunc.apply(this, arguments);
afterFunc.apply(this,arguments);
return result;
};
}
function defineAround(aroundFunc, proceedFunc) {
return function() {
var args=Array.prototype.slice.call(arguments,0);
args.unshift(proceedFunc.bindTo(this));
return aroundFunc.apply(this, args);
};
}
Function.prototype.bindTo=function(thisObject) {
var selfFunc=this;
return function() {
return selfFunc.apply(thisObject, arguments);
};
}
Function.prototype.everyTimeButFirst=function() {
var self=this;
var toCall=function() {
toCall=self;
return null;
};
return function() {
return toCall.apply(this, arguments);
};
}
Function.prototype.onlyWhen=function(ruleFunc) {
var self=this;
return function() {
if (ruleFunc.apply(this, arguments))
return self.apply(this, arguments);
}
}
Object.prototype.each=function(everyFunc) {
for (var name in this) if (this.hasOwnProperty(name)) {
everyFunc.call(this, this[name],name);
}
}
Object.prototype.eachFunction=function(everyFunc) {
this.each(everyFunc.onlyWhen(function(every) {
return every.constructor == Function;
}));
}
Object.prototype.run=function(func) {
func.call(this);
}
Object.prototype.define=function(name, annotations, func) {
this[name]=func;
annotations.each(function(each, name) {
func[name]=each;
});
}
Array.prototype.each=function(everyFunc) {
for (var index=0; index < this.length; index++)
everyFunc.call(this, this[index], index)
}
Array.prototype.toString=function() {
var result="[";
var addComma=function() { result = result + ", " };
addComma=addComma.everyTimeButFirst();
Array.prototype.each.call(this, function(every) {
addComma();
result = result + every;
});
return result + "]";
}
Annotations in Javascript
Last post, I talked about how to implement crude aspect advices. I say crude because the implementation would need to be beefed up a bit for wider use. The ideas were more important. I wanted to spend the article on the tricky bits of implementation. I'll take the same tactic with this article.
Let's start with what our code will look like. In fact, let's implement something everyone is familiar with: a transaction. Now, our transaction will have a blank implementation (we'll print out the important bits). It's the API that we will care about later. Here's the definition:
OK. There's some new functions named "run" and "define". I'll show their implementations soon. You can probably guess the implementation of "run". It's simply so that my hands don't get tired typing "Transaction.prototype". I could use "with {}", but I have found its implementation inconsistent among Javascript dialects. The next new function, "define", is how we're going to define our annotations. It's a shorthand for better readability and to reduce duplication. All it will do is add the function to our object and add the annotations as properties. Annotations are simple in Javascript because functions are just objects and we can add anything we want to them. How cool.
Here's the implementations:
Both are straightforward. But, I introduced another function: "each". Those familiar with Ruby will notice what it does right away. For the non-Rubyists, it iterates over all the properties of the object. My implementation also sends the name of the property as an extra parameter. Here's how I did "each":
It basically allows me not to have a "for" loop. Simple. You might have noticed my use of "call". The reason is because I wanted to pass along "this" to the function passed in.
That is it for implementing annotations. The query language is already built in (albeit a little verbose). We only have to iterate over the functions of an object. Here's some code I would like to use to test my annotations and query implementation:
Oh boy. Can you guess the output from the above? Here it is:
The annotations I have is to mark a function to be logged on entry and exit. I'll spend the rest of the article on the implementation and bring together the annotations, aspects, and logging in the next article.
Let's talk about the new functions: "onlyWhen" and "eachFunction". I think you can guess what each of them do. "onlyWhen" is defined on a function and takes a function to check the arguments for some condition. If the condition is true, it executes the receiver function. It returns a function that brings all of this together. This is more functional programming at work. "eachFunction" takes a function that will be called for each function that is a value of a property on the object.
Like always, here's how I implemented them:
In the "onlyWhen" implementation, I have to bind "this" (the function) to self so that we can use it later in the function that we use a result. "this" will be bound to a different object. "eachFunction" brings together the functions "each" and "onlyWhen".
That's it! You could of course add implementations to define annotations so that we make sure that only pre-defined ones are used in functions. I'll leave that as an exercise to the reader. It wouldn't take much to implement it. The next article will put everything together. All we have to do now is weave based on rules. We have the means to iterate over functions and we have the means to replace implementations through our advices. The next article will mix this batter.
Let's start with what our code will look like. In fact, let's implement something everyone is familiar with: a transaction. Now, our transaction will have a blank implementation (we'll print out the important bits). It's the API that we will care about later. Here's the definition:
function Transaction() {
}
Transaction.prototype.run(function() {
this.define(
"begin",
{log: true},
function() {
print("begin");
});
this.define(
"commit",
{log: true},
function() {
print("commit");
});
this.define(
"rollback",
{log: true},
function() {
print("rollback");
});
this.toString=function() {
return "i am transaction";
}
});OK. There's some new functions named "run" and "define". I'll show their implementations soon. You can probably guess the implementation of "run". It's simply so that my hands don't get tired typing "Transaction.prototype". I could use "with {}", but I have found its implementation inconsistent among Javascript dialects. The next new function, "define", is how we're going to define our annotations. It's a shorthand for better readability and to reduce duplication. All it will do is add the function to our object and add the annotations as properties. Annotations are simple in Javascript because functions are just objects and we can add anything we want to them. How cool.
Here's the implementations:
Object.prototype.run=function(func) {
func.call(this);
}
Object.prototype.define=function(name, annotations, func) {
this[name]=func;
annotations.each(function(each, name) {
func[name]=each;
});
}Both are straightforward. But, I introduced another function: "each". Those familiar with Ruby will notice what it does right away. For the non-Rubyists, it iterates over all the properties of the object. My implementation also sends the name of the property as an extra parameter. Here's how I did "each":
Object.prototype.each=function(everyFunc) {
for (var name in this) if (this.hasOwnProperty(name)) {
everyFunc.call(this, this[name],name);
}
}It basically allows me not to have a "for" loop. Simple. You might have noticed my use of "call". The reason is because I wanted to pass along "this" to the function passed in.
That is it for implementing annotations. The query language is already built in (albeit a little verbose). We only have to iterate over the functions of an object. Here's some code I would like to use to test my annotations and query implementation:
var every=function(each,name) {
print(name);
}
var doesItLog=function(each) {
return each.log == true;
}
Transaction.prototype.eachFunction(every.onlyWhen(doesItLog));
Oh boy. Can you guess the output from the above? Here it is:
begin
commit
rollback
The annotations I have is to mark a function to be logged on entry and exit. I'll spend the rest of the article on the implementation and bring together the annotations, aspects, and logging in the next article.
Let's talk about the new functions: "onlyWhen" and "eachFunction". I think you can guess what each of them do. "onlyWhen" is defined on a function and takes a function to check the arguments for some condition. If the condition is true, it executes the receiver function. It returns a function that brings all of this together. This is more functional programming at work. "eachFunction" takes a function that will be called for each function that is a value of a property on the object.
Like always, here's how I implemented them:
Function.prototype.onlyWhen=function(ruleFunc) {
var self=this;
return function() {
if (ruleFunc.apply(this, arguments))
return self.apply(this, arguments);
}
}
Object.prototype.eachFunction=function(everyFunc) {
this.each(everyFunc.onlyWhen(function(every) {
return every.constructor == Function;
}));
}In the "onlyWhen" implementation, I have to bind "this" (the function) to self so that we can use it later in the function that we use a result. "this" will be bound to a different object. "eachFunction" brings together the functions "each" and "onlyWhen".
That's it! You could of course add implementations to define annotations so that we make sure that only pre-defined ones are used in functions. I'll leave that as an exercise to the reader. It wouldn't take much to implement it. The next article will put everything together. All we have to do now is weave based on rules. We have the means to iterate over functions and we have the means to replace implementations through our advices. The next article will mix this batter.
Friday, April 11, 2008
Aspects in Javascript
Just like I promised! I'm going to describe how to do aspects in Javascript. Now, this example will be simplified. You will probably want to beef up the implementation given here. I'm really showing off an idea and how you can apply it to your Javascript libraries. I did this for fun and was shocked how easy it was. In these examples, I will be using Spidermonkey.
I'm first going to show off how to implement advices. Let's start with "before" advice and then have a simple test for our implementation. I would recommend using JSUnit for testing. I'm simply going to output it for demonstration purposes.
The easiest advice to implement is "before". All we need to do is define a function that creates a new function that calls our before function and then calls the function we originally wanted to call. "defineBefore" handles this quite nicely. The rest of the code below is defining our "before" advice and the function we want to call and then doing the wrapping. Pretty simple.
Here's the output:
You can see from the results that our "before" advice implementation can look at the arguments (but, not change them, we'll get to that). And be basically benign to the whole operation. Our weaving works!
"After" advice is equally as easy. We just need to remember the result and return that from our weaved function. Again, the "after" function will be benign and not be able to change the result. Here's the implementation:
Here's the results:
Now, our "before" and "after" advice weaving did not allow us to change the arguments or results. I wanted to keep it simple without a lot of complications. If we need to change the arguments or tamper with the result, let's do it with an "around" advice.
The implementation for "around" is a little bit more complicated. I want to make calling the proceed function as simple as possible in the after advice function. To do this, I need to wrap the proceed function with another function that will bind the "this" so that we don't have to worry with sending "call" on the proceed function. The implementations before didn't have to deal with the proceed function in the advice and that made life easier. Now, we'll have that control and with that power, comes the ability to change arguments and results. Here's the implementation:
And where would we be without the results?
One more detail, I didn't do an error handling or recovery. You will probably want to wrap any kind of exceptions that might happen in the proceed function and take appropriate action.
So far, our implementation is nice and clean. We're mainly using functional programming concepts for it. The next steps is writing the implementation for the pointcuts and combining that with what we did here. I'll also going to show how to define annotations. Trust me it's going to get more fun as we combine the batter and get ready to bake!
I'm first going to show off how to implement advices. Let's start with "before" advice and then have a simple test for our implementation. I would recommend using JSUnit for testing. I'm simply going to output it for demonstration purposes.
The easiest advice to implement is "before". All we need to do is define a function that creates a new function that calls our before function and then calls the function we originally wanted to call. "defineBefore" handles this quite nicely. The rest of the code below is defining our "before" advice and the function we want to call and then doing the wrapping. Pretty simple.
function defineBefore(beforeFunc, proceedFunc) {
return function() {
beforeFunc.apply(this,arguments);
return proceedFunc.apply(this, arguments);
};
}
function printArg(x) {
print("proceed: " + x);
return x + 1;
}
before=function(x) {
print("before: " + x);
}
printArg=defineBefore(before, printArg);
print("result: "+printArg(6));
Here's the output:
js> load("aspect.js")
before: 6
proceed: 6
result: 7
You can see from the results that our "before" advice implementation can look at the arguments (but, not change them, we'll get to that). And be basically benign to the whole operation. Our weaving works!
"After" advice is equally as easy. We just need to remember the result and return that from our weaved function. Again, the "after" function will be benign and not be able to change the result. Here's the implementation:
function defineAfter(afterFunc, proceedFunc) {
return function() {
var result=proceedFunc.apply(this, arguments);
afterFunc.apply(this,arguments);
return result;
};
}
after=function(x) {
print("after: " + x);
}
printArg=defineAfter(after, printArg);
print("result: "+printArg(8));
Here's the results:
js> load("aspect.js")
before: 8
proceed: 8
after: 8
result: 9
Now, our "before" and "after" advice weaving did not allow us to change the arguments or results. I wanted to keep it simple without a lot of complications. If we need to change the arguments or tamper with the result, let's do it with an "around" advice.
The implementation for "around" is a little bit more complicated. I want to make calling the proceed function as simple as possible in the after advice function. To do this, I need to wrap the proceed function with another function that will bind the "this" so that we don't have to worry with sending "call" on the proceed function. The implementations before didn't have to deal with the proceed function in the advice and that made life easier. Now, we'll have that control and with that power, comes the ability to change arguments and results. Here's the implementation:
Function.prototype.bindTo=function(thisObject) {
var selfFunc=this;
return function() {
return selfFunc.apply(thisObject, arguments);
};
}
function defineAround(aroundFunc, proceedFunc) {
return function() {
var args=Array.prototype.slice.call(arguments,0);
args.unshift(proceedFunc.bindTo(this));
return aroundFunc.apply(this, args);
};
}
function concatAndPrint(first,second) {
print("first: "+first);
print("second: "+second);
var result=first + second;
print("concat result: "+ result);
return result;
}
around=function(proceedFunc, first, second) {
print("around first: "+first);
print("around second: "+second);
var result=proceedFunc(first, second);
return result + "c";
}
concatAndPrint=defineAround(around, concatAndPrint);
print("final result: "+concatAndPrint("a","b"));
And where would we be without the results?
js> load("aspect.js")
around first: a
around second: b
first: a
second: b
concat result: ab
final result: abc
One more detail, I didn't do an error handling or recovery. You will probably want to wrap any kind of exceptions that might happen in the proceed function and take appropriate action.
So far, our implementation is nice and clean. We're mainly using functional programming concepts for it. The next steps is writing the implementation for the pointcuts and combining that with what we did here. I'll also going to show how to define annotations. Trust me it's going to get more fun as we combine the batter and get ready to bake!
Tuesday, April 08, 2008
Javascript Inheritance with super
One thing that has always bothered me about Javascript is there is no implementation of "super". Most texts suggest to simply call the super function explicitly via its prototype. It makes for refactoring your inheritance hierarchy no fun because of the direct calls. And let's not mention all the duplication.
Let's think for a minute. Everything in Javascript is an object. What if we added the super automatically to the overriden functions so we could get access to it? And while we are at it, why not make our object definitions a bit more explicit along the way?
So, I whipped up the following code:
It's two very short functions. But, it allows me to do this:
"arguments.callee.superFunction.call" is a little verbose, but it's better than duplicating the name of my "super" implementation prototype all over the place. Plus, how often do you call "super". I generally think too many calls to "super" is a design smell.
I like this approach mostly. The only thing I don't like is the names I chose. But, I'll keep working on it. I was just happy in two functions, I have "super" in my constructors and functions. If anything, it's a different way of thinking about the problem.
Coming next, how to do aspects and annotations in Javascript.
Let's think for a minute. Everything in Javascript is an object. What if we added the super automatically to the overriden functions so we could get access to it? And while we are at it, why not make our object definitions a bit more explicit along the way?
So, I whipped up the following code:
Function.prototype.subclassFrom=function(superClassFunc) {
if (superClassFunc == null) {
this.prototype={};
} else {
this.prototype=new superClassFunc();
this.prototype.constructor=this;
this.superConstructor=superClassFunc;
}
}
Function.prototype.methods=function(funcs) {
for (each in funcs) if (funcs.hasOwnProperty(each)) {
var original=this.prototype[each];
funcs[each].superFunction=original;
this.prototype[each]=funcs[each];
}
}
It's two very short functions. But, it allows me to do this:
function Parent(z) {
this.z=z;
}
Parent.subclassFrom(null);
Parent.methods({
methodOne: function(x) {...},
methodTwo: function() {...}
});
function Child(z,y) {
arguments.callee.superConstructor.call(this,z);
this.y=y;
}
Child.subclassFrom(Parent);
Child.methods({
methodOne: function(x) {
arguments.callee.superFunction.call(this,x);
...
},
methodThree: function() {...}
});
"arguments.callee.superFunction.call" is a little verbose, but it's better than duplicating the name of my "super" implementation prototype all over the place. Plus, how often do you call "super". I generally think too many calls to "super" is a design smell.
I like this approach mostly. The only thing I don't like is the names I chose. But, I'll keep working on it. I was just happy in two functions, I have "super" in my constructors and functions. If anything, it's a different way of thinking about the problem.
Coming next, how to do aspects and annotations in Javascript.
Friday, April 04, 2008
Monkey Patching
Monkey patching seems to be all the debate rage currently in the dynamic language blogosphere. The one that caught my attention was Gilad Bracha's in particular. I don't disagree with him at all. I know it's strange reading that from a Smalltalker. But, I view monkey patching like inheritance. It's a great device to have in your programmer back pocket, but can be dangerous. Use with caution and look at your options. Don't use it without thinking of the implications it can cause. The reason its dangerous is because it's a global change that widens the protocol of the affected object or worse changes the contract for an already existing method.
The main reason monkey patching is so dangerous is the global change it makes and the increased probability of having a collision if another project names a method the same. Or worse, another project overrides the default behavior and replaces it with their own. Both can cause subtle bugs that can be hard to catch and find. It's the global nature of it that can bite. It's convenient, but you should think twice before doing it. It's limits your project's options.
Groovy has a novel approach to monkey patching in that it allows them to be scoped for the duration of a block called categories. This reduces the risk of a global change and the addition is only in affect for the duration of the block. Now, it can cause issues if something gets called outside of the block by accident, which is why I wish it had different ways of scoping (class, package, etc). But, with AspectJ these problems can be reduced. Categories reduce a lot of the risk of monkey patching in that collisions are highly unlikely and the protocol is only widened for that invocation. It also ensures that unknown project dependencies will not creep in. I worry less about monkey patching in this instance because if you get burned, it will be your project not everyone else. I think categories are the way to go (if they added class scoped categories, I would be in heaven).
I don't hate monkey patching. Far from it. It is handy to override method implementations during debugging. I can change pre-existing methods if they have bugs in them even before the next release is out. I don't have to wait on the vendor. These are great pluses. I'm just advocating thought before you monkey patch in your own projects. Much like before you swing the inheritance hammer, you should do the same with monkey patches.
The main reason monkey patching is so dangerous is the global change it makes and the increased probability of having a collision if another project names a method the same. Or worse, another project overrides the default behavior and replaces it with their own. Both can cause subtle bugs that can be hard to catch and find. It's the global nature of it that can bite. It's convenient, but you should think twice before doing it. It's limits your project's options.
Groovy has a novel approach to monkey patching in that it allows them to be scoped for the duration of a block called categories. This reduces the risk of a global change and the addition is only in affect for the duration of the block. Now, it can cause issues if something gets called outside of the block by accident, which is why I wish it had different ways of scoping (class, package, etc). But, with AspectJ these problems can be reduced. Categories reduce a lot of the risk of monkey patching in that collisions are highly unlikely and the protocol is only widened for that invocation. It also ensures that unknown project dependencies will not creep in. I worry less about monkey patching in this instance because if you get burned, it will be your project not everyone else. I think categories are the way to go (if they added class scoped categories, I would be in heaven).
I don't hate monkey patching. Far from it. It is handy to override method implementations during debugging. I can change pre-existing methods if they have bugs in them even before the next release is out. I don't have to wait on the vendor. These are great pluses. I'm just advocating thought before you monkey patch in your own projects. Much like before you swing the inheritance hammer, you should do the same with monkey patches.
Wednesday, February 13, 2008
Yet More On Tools
I got so many good comments on Tools that I couldn't just let the replies go to waste. Here's another one from Sam Tesla:
My answer to this is simply why write a new language to begin with? The reason is simple: to solve a specific problem better than before. We shouldn't stop trying. Now, with that being said, let's move on. I know Erlang is a powerful language and have many times sang its praises. There's a lot of languages in the same boat (Io, Mozart, and many more). I can understand not wanting to do another editor in the beginning, but it's a good exercise. There are non-trivial problems to solve in doing your own editor. Plus, it allows new developers to see a non-trivial example of your new language's code. Again, let's move on.
My main rant was on tools (not necessarily editors), yes, I know I picked on Eclipse and Emacs. But, Eclipse is really much more than a text editor. It has several tools to reason about your code (Refactoring), give your different viewpoints (Call Hierarchy, Class Hierarchy), and ways to ignore what you don't care about. These are the kinds of tools that I am interested in. I think a code beautifier and debugger are the price of admission. You are not even playing the game without those. Now, Erlang is a different way of thinking, it should have tools to support that thinking. Much like the Refactoring Browser changed the way we view code in Smalltalk. I think Ruby is ripe with opportunities to change the way we think about code. Again, the tools should reflect that.
I'll use a simple example: AspectJ. Now, it's not a language per se. But, it's a different way of viewing code and a different way of developing. I liked the idea of aspects, but it didn't click until the Eclipse support came. Once it was easy to see the consequences of my actions, my mental model became stronger. It also made it easier to reason about what my systems were doing. Tools should aid and enhance your mental model. They should allow you to ignore details and quickly hone on the ones you do.
Lastly, I'll add that I wouldn't even think about doing Java code without Eclipse. It's the tools. But, when I'm programming in another language like Ruby for example. I want to stay thinking in that language. So, if I have an idea for a nice tool, it should be easy to whip it up in the language I'm working in. I shouldn't have to switch gears to another language. We should think of languages as playgrounds to change the way we program and change it for the better.
Sooner or later I was going to bite on one of your posts.
While I agree that tools for a language should be written in the language, I have to disagree on specifics.
Why should I have to write Yet Another Yet Another Yet Another Editor for my language in my language when a perfectly good one already exists?
The Erlang/OTP team asked that question and decided they shouldn't. Instead they made an elisp module that turns your Emacs into an Erlang node and uses Erlang's distribution primitives to hook into a live Erlang node.
They provide many of the tools you'd expect from a Smalltalk, including the ability to inspect and interrogate (inasmuch as it makes sense for a functional language) running processes.
It's in Emacs, sure, but that's because Emacs has had at least ten years more to mature than Erlang, let alone any editor written in Erlang.
Good programmers don't repeat themselves if they don't have to.
My answer to this is simply why write a new language to begin with? The reason is simple: to solve a specific problem better than before. We shouldn't stop trying. Now, with that being said, let's move on. I know Erlang is a powerful language and have many times sang its praises. There's a lot of languages in the same boat (Io, Mozart, and many more). I can understand not wanting to do another editor in the beginning, but it's a good exercise. There are non-trivial problems to solve in doing your own editor. Plus, it allows new developers to see a non-trivial example of your new language's code. Again, let's move on.
My main rant was on tools (not necessarily editors), yes, I know I picked on Eclipse and Emacs. But, Eclipse is really much more than a text editor. It has several tools to reason about your code (Refactoring), give your different viewpoints (Call Hierarchy, Class Hierarchy), and ways to ignore what you don't care about. These are the kinds of tools that I am interested in. I think a code beautifier and debugger are the price of admission. You are not even playing the game without those. Now, Erlang is a different way of thinking, it should have tools to support that thinking. Much like the Refactoring Browser changed the way we view code in Smalltalk. I think Ruby is ripe with opportunities to change the way we think about code. Again, the tools should reflect that.
I'll use a simple example: AspectJ. Now, it's not a language per se. But, it's a different way of viewing code and a different way of developing. I liked the idea of aspects, but it didn't click until the Eclipse support came. Once it was easy to see the consequences of my actions, my mental model became stronger. It also made it easier to reason about what my systems were doing. Tools should aid and enhance your mental model. They should allow you to ignore details and quickly hone on the ones you do.
Lastly, I'll add that I wouldn't even think about doing Java code without Eclipse. It's the tools. But, when I'm programming in another language like Ruby for example. I want to stay thinking in that language. So, if I have an idea for a nice tool, it should be easy to whip it up in the language I'm working in. I shouldn't have to switch gears to another language. We should think of languages as playgrounds to change the way we program and change it for the better.
More On Tools
Friedrich had this to say about my Tools post:
First off, I could give the quick flippant Smalltalker response and say, "If you need a text editor that can handle large chunks of text, then you're doing something wrong." But, I'm not. Sure, we don't need to handle large chunks in coding for Smalltalk, but the world is a different place now. The need is there. Today we need to handle XML documents, source from other languages, HTML, and that's just a start. But, an editor that can handle large amounts of code and makes it easier for new Smalltalkers to get them into the swing of things so to speak.
Secondly, the Smalltalk community might not know they need it. Write it! If anything you might deficiencies through the exercise and decide to fix those as well. I wrote a Java Serialization framework to learn its format, but to fix bad streams as well. Nobody wanted it, but me. You got to do things for yourself and for your own journey as a developer. If you need any help, feel free to contact me. But, don't expect any community to follow along. The best you can hope for is to make the music and hope other people like the melody to join in.
I wrote about this on the Squeak mailing list and I guess somewhere else also. I asked for decent editor written for Squeak, AFAIKT such a thing does not exist. But does that mean that Smalltalks are not good for programming an editor? And if no-one sees that this would be good thing, am I expected to step in?
First off, I could give the quick flippant Smalltalker response and say, "If you need a text editor that can handle large chunks of text, then you're doing something wrong." But, I'm not. Sure, we don't need to handle large chunks in coding for Smalltalk, but the world is a different place now. The need is there. Today we need to handle XML documents, source from other languages, HTML, and that's just a start. But, an editor that can handle large amounts of code and makes it easier for new Smalltalkers to get them into the swing of things so to speak.
Secondly, the Smalltalk community might not know they need it. Write it! If anything you might deficiencies through the exercise and decide to fix those as well. I wrote a Java Serialization framework to learn its format, but to fix bad streams as well. Nobody wanted it, but me. You got to do things for yourself and for your own journey as a developer. If you need any help, feel free to contact me. But, don't expect any community to follow along. The best you can hope for is to make the music and hope other people like the melody to join in.
Monday, February 11, 2008
Tools
I'm a firm believer in that tools should be written in the language that they are to be used for. I always treat a language with caution if the only tools for it are written in Emacs Lisp or Java and not itself.
But, why does it matter? It matters because writing tools for your language in another language tells me something. It tells me that:
Any of the above points are bad in my book. But, I see it constantly in new programming languages (even in ones 10+ years old). Now, I love languages and learning them. Why? Because I always walk away with a new way of thinking about things. It's also nice to see how other people solve certain problems. It shocks me though that so many languages depend on Eclipse or Emacs for their tools. At some point, a language moves from command line pet project to a true programmer amplifier. The language has to be elegant in syntax, yet have the tools to make debugging and reasoning about the system simple.
Now, before you get angry with me, I do realize that using Emacs or Eclipse gives a huge boost in the beginning with their frameworks. But, by never leaving them, it could also mean there are holes in your frameworks or in your language itself. Tools written in your language help to expose issues like limited reflection or that your language is hard to parse. I believe writing tools should be easy and for everyone to do. If writing tools in your language is hard, then you might need to redesign your language.
I think if we had more languages that had their tools written in them, we would all benefit. Plus, with tools written in your language, new developers have some example code that has real world applications. The way to prove to me if your language is elegant is not to show me how easy factorial is to write, but to show me how easy it is to write an inspector.
But, why does it matter? It matters because writing tools for your language in another language tells me something. It tells me that:
- It's possibly harder to write in than Java
- It's hard to parse
- Limited or no reflection support
- Not user friendly
- Can not perform well enough to support tools written in itself
Any of the above points are bad in my book. But, I see it constantly in new programming languages (even in ones 10+ years old). Now, I love languages and learning them. Why? Because I always walk away with a new way of thinking about things. It's also nice to see how other people solve certain problems. It shocks me though that so many languages depend on Eclipse or Emacs for their tools. At some point, a language moves from command line pet project to a true programmer amplifier. The language has to be elegant in syntax, yet have the tools to make debugging and reasoning about the system simple.
Now, before you get angry with me, I do realize that using Emacs or Eclipse gives a huge boost in the beginning with their frameworks. But, by never leaving them, it could also mean there are holes in your frameworks or in your language itself. Tools written in your language help to expose issues like limited reflection or that your language is hard to parse. I believe writing tools should be easy and for everyone to do. If writing tools in your language is hard, then you might need to redesign your language.
I think if we had more languages that had their tools written in them, we would all benefit. Plus, with tools written in your language, new developers have some example code that has real world applications. The way to prove to me if your language is elegant is not to show me how easy factorial is to write, but to show me how easy it is to write an inspector.
Subscribe to:
Posts (Atom)