My Stuff
Email
Twitter
Front Page
Presentations
Projects
Music
Favorite Quotes

Respect
Vincent Foley-Bourgon
Sam Griffith
LeRoy Mattingly
Colin Putney
Matt Secoske
Sam Tesla
Andres Valloud

Admiration
Leo Brodie
Avi Bryant
Alan Cooper
Steve Dekorte
Stephane Ducasse
Doug Engelbart
Eric Evans
Brian Foote
Martin Fowler
Paul Graham
Dan Ingalls
Alan Kay
John McCarthy
Steve McConnell
Peter Norvig
Niall Ross
Randall Smith
Gerald Jay Sussman
David Ungar
Rebecca Wirfs-Brock
...And So Many More...

My Amps
Squeak
JavaScript
Scheme
Java
Corman Lisp
Ruby
Dolphin Smalltalk
Cincom Smalltalk
Self

Archives
05/01/2003 - 06/01/2003
06/01/2003 - 07/01/2003
07/01/2003 - 08/01/2003
08/01/2003 - 09/01/2003
09/01/2003 - 10/01/2003
10/01/2003 - 11/01/2003
11/01/2003 - 12/01/2003
12/01/2003 - 01/01/2004
01/01/2004 - 02/01/2004
02/01/2004 - 03/01/2004
03/01/2004 - 04/01/2004
04/01/2004 - 05/01/2004
05/01/2004 - 06/01/2004
06/01/2004 - 07/01/2004
07/01/2004 - 08/01/2004
08/01/2004 - 09/01/2004
09/01/2004 - 10/01/2004
10/01/2004 - 11/01/2004
11/01/2004 - 12/01/2004
12/01/2004 - 01/01/2005
01/01/2005 - 02/01/2005
02/01/2005 - 03/01/2005
03/01/2005 - 04/01/2005
04/01/2005 - 05/01/2005
05/01/2005 - 06/01/2005
06/01/2005 - 07/01/2005
07/01/2005 - 08/01/2005
08/01/2005 - 09/01/2005
09/01/2005 - 10/01/2005
10/01/2005 - 11/01/2005
11/01/2005 - 12/01/2005
12/01/2005 - 01/01/2006
01/01/2006 - 02/01/2006
02/01/2006 - 03/01/2006
03/01/2006 - 04/01/2006
04/01/2006 - 05/01/2006
05/01/2006 - 06/01/2006
06/01/2006 - 07/01/2006
07/01/2006 - 08/01/2006
08/01/2006 - 09/01/2006
09/01/2006 - 10/01/2006
10/01/2006 - 11/01/2006
11/01/2006 - 12/01/2006
12/01/2006 - 01/01/2007
01/01/2007 - 02/01/2007
02/01/2007 - 03/01/2007
03/01/2007 - 04/01/2007
04/01/2007 - 05/01/2007
05/01/2007 - 06/01/2007
06/01/2007 - 07/01/2007
07/01/2007 - 08/01/2007
08/01/2007 - 09/01/2007
09/01/2007 - 10/01/2007
10/01/2007 - 11/01/2007
11/01/2007 - 12/01/2007
12/01/2007 - 01/01/2008
01/01/2008 - 02/01/2008
02/01/2008 - 03/01/2008
03/01/2008 - 04/01/2008
04/01/2008 - 05/01/2008
05/01/2008 - 06/01/2008
06/01/2008 - 07/01/2008
07/01/2008 - 08/01/2008
08/01/2008 - 09/01/2008
10/01/2008 - 11/01/2008
01/01/2009 - 02/01/2009
09/01/2009 - 10/01/2009
10/01/2009 - 11/01/2009

Feed

Add this feed to a running copy of BottomFeeder

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:
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.

Labels: , ,


Comments
  • This post reminds me of the all-too-true quote by Brian Kernighan:

    Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.

    By Blogger kesmit, at 9:51 PM   

  • Hey! I wrote an entry about a clever coding too.

    http://squeak.preeminent.org/blog/?p=325

    - Steve

    By Blogger fastfingers, at 10:07 PM   

Saturday, October 10, 2009

Freezing Objects in Python

 
Someone stop me. I like freezing simple objects or what Eric Evans calls "Value Objects" in his excellent "Domain Driven Design" book. Python doesn't have immutable objects (ala freeze in Ruby) explicitly, but we can easily create it. Python gives us the power to get under its covers. Here's my implementation:
class ValueObject(object):
def __setattr__(self, name, value):
if name == 'value' and hasattr(self, 'value'):
raise AttributeError("Can not change value attribute")
else:
self.__dict__[name] = value


Do you have to ask? Yes, I made a test. Here they are:
import unittest
class Test(unittest.TestCase):

def testSimple(self):
class Cents(ValueObject):
def __init__(self, value):
self.value = value
def __str__(self):
return str(self.value) + " cents"
subject = Cents(5)
self.assertEquals(5, subject.value)
def set_value():
subject.value = 6
self.failUnlessRaises(AttributeError, set_value)

Labels: ,


Comments

Monday, October 05, 2009

method_missing in Python

 
OK, I know there is a few implementations of this knocking around on the net already, but I'm in learning mode. So, here's my implementation of Ruby's method_missing:
class PossibleMissingAttribute(object):
def __init__(self, object, name):
self._object = object
self._name = name

def __call__(self, *args, **kwargs):
return self._object._method_missing(self._name, *args, **kwargs)

def __str__(self):
return self.__class__.__name__ + ": " + str(self._object) + "." + self._name

def __getattr__(self, name):
return None

def __nonzero__(self):
return False

class MethodMissingError(Exception):
def __init__(self, object, name, *args, **kwargs):
self.object = object
self.name = name
self.args = args
self.kwargs = kwargs

def __str__(self):
return repr(str(self.object) + "." + self.name)

class Missing(object):
def __getattr__(self, name):
return PossibleMissingAttribute(self, name)
def _method_missing(self, name, *args, **kwargs):
raise MethodMissingError(self, name, *args, **kwargs)


Three objects and that's it! One to be a placeholder when an argument is missing. One object to represent the MethodMissingError and the last to be an abstract class to inherit from. But, you could easily just put these methods anywhere. Here's my tests:
import unittest

class TestMissing(unittest.TestCase):
def test_missing(self):
class TestClass(Missing):
def __str__(self):
return self.__class__.__name__
def existing(self):
return "i am"
def _method_missing(self, name, *args, **kwargs):
return args[0]
self.assertEquals("am not", TestClass().missing("am not"))
self.assertEquals("i am", TestClass().existing())
self.assertEquals(None, TestClass().missing.something_else_missing)
self.assertFalse(TestClass().missing)
self.assertEquals("PossibleMissingAttribute: TestClass.missing", str(TestClass().missing))

def test_exception(self):
class TestClass(Missing):
def __str__(self):
return self.__class__.__name__
self.failUnlessRaises(MethodMissingError, lambda: TestClass().missing())



You've seen my implementation. Here's another one that I found on the net: method_missing in Python
class MethodMissing(object):
def method_missing(self, attr, *args, **kwargs):
“”" Stub: override this function “”"
raise AttributeError(”Missing method %s called.”%attr)

def __getattr__(self, attr):
def callable(*args, **kwargs):
return self.method_missing(attr, *args, **kwargs)
return callable

Um, this version is much shorter than mine. It gets the same job done and is more obvious of what it is doing. Simply returning a function on __getattr__ is the best way to go. It's what my PossibleArgumentMissing was doing. But, where I tried to get all fancy using __call__ to mimic a function which caused my version to be longer. I was also trying to get the argument to come back to return false in if conditions and be almost like None (again, I'm learning and was looking at what was possible). Simplest is best.

This was a fun thought exercise. I love all the hooks Python provides to allow you to get underneath the hood if need be. I will be soon doing a post on descriptors and even decorators. I find Python tends to sway heavier on the functional side of programming versus object-oriented. I'm loving the succinct, yet readable code. Too many languages get into the being so terse that they sacrifice readability or debugability (is that a word? Is now!).

Labels: ,


Comments

Tuesday, September 29, 2009

Prototypes in Python

 
I'm in love with prototype-based languages, but there only a few to play with ECMAScript (Javascript) and Self. Since I'm learning Python, I thought it would be a good experiment to implement prototypes in Python. For one thing, I would get to learn some of the meta capabilities and other advanced features. A pure thought experiment just for learning and seeing how far I could push another paradigm onto Python. Here is what I came up with:
class Prototype(object):
def __init__(self, *args):
self._parents = []
for each in args:
self._parents.append(each)

def clone(self, *args):
return self.__class__(self, *args)

def __getattr__(self, name):
for each in self._parents:
try:
return getattr(each, name)
except AttributeError:
pass
raise AttributeError(name)

The implementation is suprisingly simple: three methods!

The first is the initialize method for new instances. It takes a list of objects that we will use as the parents of our new object. Of course, by simply calling Prototype's constructor with no arguments, we get a clean object. I made the ability to have multiple inheritance simple because of Self.

The second method is the clone method and it simply calls the constructor for another object makes itself the first parent with the rest of the arguments becoming the other parents. Nothing to it!

The last method is the real meat. It only get called by the Python engine when an attribute fails to be looked up. All I do is call each parent and if one succeeds, it returns the answer. This is a depth first search of the parent hierarchy. Simple.

And that's all we need to implement prototypes in Python. Now, I just need to explain some practical uses of this in some later blog entries.

How do I know all of this works? Here's my tests:
import unittest
class Test(unittest.TestCase):

def testSimple(self):
parent = Prototype()
child = parent.clone()

parent.value = 0
self.assertEquals(0, child.value)
child.value = 1

self.assertEquals(1, child.value)
self.assertEquals(0, parent.value)

self.failUnlessRaises(AttributeError, lambda:parent.unknown)

def testMultipleParents(self):
parent1 = Prototype()
parent2 = Prototype()
child = parent1.clone(parent2)

parent2.value = 2
self.failUnlessRaises(AttributeError, lambda:parent1.value)
self.assertEquals(2, child.value)

If anyone sees anything wrong in my implementation or un-Pythonic let me know. I'm still learning, but I thought it would be cool to show my progress.

Labels: ,


Comments
  • You can actually shorten your constructor down to self._parents = list(args).

    By Blogger kesmit, at 9:28 AM   

  • Welcome to the world of Python. You will like it here.

    By Blogger MikeHoss, at 9:48 PM   

  • You shouldn't copy objects so deeply. You have to make a class-like thing. You create a new object with a proto attribute. Then when an attribute is requested you find it in the local attributes. If it's not found you search in the proto, and so on until the proto is None. Your way you always copy the whole object, and it becomes heavy in memory terms.

    By Blogger Luca Bruno aka Lethalman, at 3:43 AM   

  • Thanks Kesmit and MikeHoss!

    To Luca, Where am I copying deeply? The clone method creates a brand new object and it is not a copy. The parents attribute is the proto attribute you were talking of. The __getattr__ is only called when an attribute is not found and then it searches for it in the parents (and if they are Prototypes, then they will do the same.

    How am I copying the entire object? I want to make sure I'm not doing something wrong.

    By Blogger Blaine, at 6:14 AM   

Sunday, September 13, 2009

Constraints in Python

 
With every language that I learn, I pay close attention to my work flow. I take note of anything that gets in the way of getting my task accomplished. In a perfect language, I should never have to worry about the language ever. A free flow of ideas should come out. Whenever I find myself totally immersed in a problem, I keep track of what made it so. Recently, the one feature that surprised me the most was Python's forced indention. At first I thought, how nice not to use curly brackets to denote scope, but what about that white space? But, I put my best foot forward and worked on some code. I must admit that I love it now! It makes code more readable by keeping the rules consistent (you have to be or your code will have bugs). Formatting my code is now one less worry I have. I am forced to indent as I go along. This constraint is liberating in the fact that it allows me concentrate on the problem at hand and is one less thing to worry about. No more worrying about if the curly braces go on the same line or not depending on local coding standards. It makes code easier to read across Python programmers. In fact, I have yet to meet a Python program that I could not understand easily from first glance which is a rare accomplishment in other languages.

I also love the constraint about one line lambdas. I thought it was going to drive me crazy, but I have embraced completely. It keeps your lambda functions short and sweet like they really should be. But, the best side effect is that if that lambda needs to grow, you simply move it out to a named function automatically making your code more readable. Excellent.

Both of the features that I just mentioned are merely constraints that take away thinking about the language and force you to write readable code. I always like and embrace the design decisions to constrain the coder to make more maintainable code. The less I have to think about the language, the more time I spend on the problem I am trying to solve and that's the way it should be. It just amazed me that sometimes I find them in the strangest of ways. I would have never thought that forced indention or one lined lambdas would bring me so much coding pleasure.

Labels:


Comments
  • I had kind of the same feeling you mention here. As Smalltalker, at first I didn't like the indentation, but when I have had to start coding python I discovered that it's useful.

    But, I must say that anyway I believe Smalltalk is always a step ahead. Why? Because you have prettyprint. You can write your code in a faster way and get it automatically formated in a standard way.

    But, also, I think it could be taken forward in this way: Suppose you don't like the prettyprint why of formatting. You can modify the IDE to show you the code prettyprintted in the way you most like, anytime, and save it in the standard formatting for your office standards, for example.

    By Blogger tenuki, at 3:23 PM   

  • I always use automatic formatting in any language I use. I thought it was cool that it is unneeded in Python. It's also nice that the formatting is the same from developer to developer, no more taste wars.

    The more the language takes away mundane issues (formatting), the more time you have to think about your problem at hand which is the whole point of languages.

    By Blogger Blaine, at 7:06 PM   

  • Yes. Your point is clear!

    By Blogger tenuki, at 8:21 PM   

  • Glad to hear from another convert.

    By Blogger Ron Smith, at 10:27 PM   

Saturday, September 12, 2009

The Dead Year

 
This blog has been dead for a year and it's not been a lack of passion. I had some rather large life changing events to deal with. I have emerged stronger than ever before and I plan to start posting regularly here again. To let you know what to expect, I hope to do some new articles on my exploration of Python and Django. I've been enjoying my time with Python quite a bit. I also hope to spend some time digging into Google's V8 ECMAScript. It's time for me to kick this blog back into gear. Hold on!

Labels:


Comments
  • Good luck to you, Blaine. Have you stopped doing Smalltalk development altogether?

    By Blogger jim, at 7:41 AM   

  • Glad to see you're back.

    By Blogger Ben, at 8:18 AM   

  • awesome! Welcome back man!

    By Blogger Matt Secoske, at 8:44 AM   

  • Glad to hear you'll be writing again, and that you've come through the challenges you've faced this year.

    By Blogger Eugene Wallingford, at 2:19 PM   

  • Glad to hear you'll be writing again. I enjoy you stuff. I'm even more glad that you've survived your trials this year.

    By Blogger Eugene Wallingford, at 2:19 PM   

  • Glad to have you back. That which does not kill you makes you stronger!

    By Blogger Sam Griffith Jr., at 9:58 PM   

Sunday, January 04, 2009

Domain Change

 
Update your bookmarks from blainebuxton.com to blainebuxton.net. The new blog address is: blog.blainebuxton.net. I plan on to start blogging again soon in the next year. Things are going on that I'd rather not discuss in a public forum. But, I will be back soon!

Labels:


Comments
  • Hello,
    Hope all is well? Havent seen you post in quite some time
    Regards
    -M

    By Blogger Manish, at 7:09 AM   

Saturday, October 11, 2008

Forced Pairing

 
Let's be honest. I'm not a big fan of "forced" pairing. I believe in active collaboration between cooperating team members. In other words, two people should pair if they want to. The energy of pairing is incredible when both partners are engaged and having fun. The code is better and so is the resultant design. It's the other side of the coin that I don't like and think the results are worse, even if the two programmers were working separately. A good pairing starts with the pair liking to work with one another. They don't have to see eye to eye, but they must enjoy the journey of working on code together. They will push and encourage each other to do better. A fun competition that will result in another problem being pushed past and the winner being a well-maintained system.

"Forced" pairing is what XP advocates and I see a huge problem with it. The reason is if the pair doesn't like or respect one another, the code quality will be on par with that of the worst of the pair. When pairs don't get along, one side will disengage. When this happens, you get none of the benefits. The team still thinks its safe from doing code reviews because they had four eyes on all code. Not so. To ignore the effects of "forced" pairing is to ignore that the team is human.

Labels: ,


Comments
  • Pairing assumes that two people working on the problem is the best way to proceed. Often even if the two like their pair partner it's not the best use of their skills nor time.

    The blind assumption that agile pairing will result in success, or more success, that the individuals working alone is a very big assumption and often false.

    Many people do their best thinking alone. Maybe they are inspired or get an idea within a team framework but the execution for them is best done alone. Working with someone to refine their initial work is often the best time to introduce another pair of eyes or even a whole group of many eyes.

    Often the extra eyes are needed for assistence with aspects of design, or with debugging or refinement. Often the extra eyes get in the way and prevent progress.

    Maximizing the genius of the people on your team can be a valuable asset. Applying the blind hammer of blunt instruments like the agile methods can be highly counter productive.

    As a professional programmer, systems analyst and designer, and producer of products it's important to know when to seek out others for assistence. To force it upon the process and team members is highly unwise.

    Working together with other professionals on many projects I always attempt to maximize the best of working alone and working together with the rest of the team. The key is knowing what is best for your productivity and for the team's productivity in producing the results that are aimed for.

    There is an "I" in "team" after all and it's known as the intelligence of the individual.

    By Blogger peter, at 9:09 PM   

  • Couldn't have said it better myself.

    By Blogger Blaine, at 9:14 PM   

  • Hi, Blaine! I know you've had reservations about this. Pairing with difficult people is, well, difficult. It is tempting to opt out.

    But what better place to work out the give and take of teamwork than in the smallest possible team: the pair? Anybody can work with a friend. But to work with a person who I don't naturally get along with is a chance to grow in virtue. I would not lightly pass up that opportunity.

    If two people cannot or will not work together, it seems to me one or both of them shouldn't be on the team who's standard is all production code written by pairs. Pairing is not forced, anymore than showing up for work on time is forced, because joining a team is voluntary.

    By Blogger Alan Wostenberg, at 2:26 PM   

  • I think both points are good. Human nature is certainly something to be tuned into. "Virtue" as Alan put it is also a worthwhile investment.

    I see so much value that comes from pairing that I tend to move in that direction, but I also prefer many times to work alone, allowing ideas to form, etc...

    Team make-up is probably key. At the end of the day, I suppose the wisest of us will employ that which delivers the most value to the company. That would require that each of us be open to either approach, and probably some combination of the two.

    If a pairing assignment offers little to no value, and nature forbid, negative value, then some action to correct the situation must occur.

    I think we can trust each other more than we do, so I will agree mostly with Blaine and Peter, but not entirely.

    Not pairing at all discourages engagement in the pairing activity a great deal. I find that a bit unsafe.

    Long story short, no absolutes, they just don't work.

    By Blogger Doug Stewart, at 11:35 AM   

Sunday, August 24, 2008

Mentoring

 
One of the round table discussion at Des Moines Bar Camp was how to get students excited about our field. The discussion went from how to get students interested in non-coding aspects of software development like QA testing to finding the passionate students. One thing that I suggested was that we needed to mentor more. I think we need not only to mentor students, but people within in our industry.

Let's face it. There's no way to know everything about software development. I would even say it's impossible to know everything about Java (the number of frameworks and new ideas is overwhelming) which is a small part of the entire computing realm.

Getting back to mentoring students though. In the user groups that I attend, it's been rare to come across a student. We are they not coming? Could we be scaring them away? Are user groups too advanced? What?! I think it is none of those things. I just don't think we are marketing to them. How do we do that? I think simply putting up posters and targeting students is a start. Reaching out to teachers is another avenue. Embracing students who come to these meetings is also needed. We need to keep those passionate about what we excited.

Which leads me to that we need to mentor everyone. I've been blessed in my career to have several mentors and I try to be a mentor when asked as well. I want to give back to those that have given me so much. There's only so far that books can go. Experience is best handed down through mentorship. I think it also allows us to feel freedom to try out new things. As we move to move functional programming languages, wouldn't be nice to be mentored by the people with the most experience?

I would love to see our industry to move to an apprentice model. I think the caliber of developers, testers, and leaders would increase dramatically. We're an industry that is just starting to tap into networking (Bar Camps are excellent for this) and embrace sharing (open source). It's exciting times right now and I can see where everyone needs to be mentored. I'm always learning and love talking with people who have more experience in a technology. We need to keep the learning going.

I think the new social networking tools could be used to great effect for setting up these relationships. So, let's get together and start mentoring. Create the developers we would like to work with and be.

Labels:


Comments
  • Blaine,

    Great post. One fact that immediately came to mind was that Minneapolis' Minnebar '08 was held at the U of M Union: http://is.gd/1TEk

    By Blogger Share Tactics, at 9:54 AM   

  • Blaine,

    I think this is a great idea. I wonder though how many CS students are excited about learning, considering the amount of education they just completed or are currently undertaking. Part of it may be that they need a break or feel it is just too much burden on their schedule.

    Some have lots of extra energy in them (Samuel comes to mind). I'm sure after a couple sessions of fun, they'll be turned on.

    By Blogger Doug Stewart, at 4:33 PM   

  • Wow on Minnebar. I need to make that one year. Awesome.

    On CS Students, I think a lot are excited and just need some guidance. I know I seeked out mentors (I had a couple of professors that gave me advice). But, to have someone in the real world, would have been glorious. I would like to return the favor.

    By Blogger Blaine, at 8:08 PM   

  • Agreed, dude. Agreed. A mentoring model is a cool idea. It's too bad that most companies don't get this idea. It's more "and into the fire you go with your 39k" sort of deal. Very tough to learn - properly - in those environments.

    By Blogger Jeff, at 2:16 PM   

  • Interesting point about the U of M Union. Perhaps we could hold user group meetings at the UNO union if we got a faculty member involved?

    By Blogger Matt, at 10:33 AM   

Bar Camps

 
In the past week, I've attended both the Omaha and Des Moines Bar Camps. At each, I gave my Seaside and advanced Javascript talks. The responses and turn out was incredible. Interest is growing in Seaside and Javascript is being viewed more as real language than a toy. It makes me a happy person. Everyone I met was passionate, articulate, and excited. I can honestly say that all of the Bar Camps I have attended thus far have been incredible. I've met many new friends and I can't wait to go to another. They are addicting! Hope to see everyone again! Check out my presentations page.

Labels:


Comments
  • Had an interesting presentation. I wish I could have stuck around to hear more about seaside, I've never messed around with Smalltalk.

    By Blogger Paris Holley, at 8:53 AM   

  • Well, next time I'm in Des Moines pull me to the side and I'll show all you want to know about Smalltalk. I'll be back in October for the Ruby User's Group.

    By Blogger Blaine, at 8:09 PM   

Wednesday, August 06, 2008

Update on Javascript

 
I've been meaning to write an update to my post on Javascript prototypes. Let me just show the updated code and then I'll explain what's changed.

Base = new Object();
Base.clone = function() {
var creator = function() {
this.constructor = arguments.callee
};
creator.prototype = this;
var result = new creator();
if (result.initialize)
result.initialize.apply(result, arguments);
return result;
}

Base.mixIn = function(properties) {
for ( var each in properties)
if (properties.hasOwnProperty(each))
this[each] = properties[each];
return this;
}

ZipCode = Base.clone().mixIn( {
initialize : function(standard, extension) {
this.standard = standard;
this.extension = extension;
},
toString : function() {
return this.standard + "-" + this.extension;
}
});

print(ZipCode.clone('56777', '4567'));

First thing you will notice is that I got rid of Object.prototype. I'll admit all of my recent Javascript code has been scripting on the server side. I do sometimes forget about all of the legacy code running on the browser(Adding properties to the Object.prototype can cause bugs in naive for loops). Not to fear, I created a Base object and put the clone and mixIn functions on it.

The next change is to the clone function where I introduced code that will setup the new clone if an initalize function is present. It calls the initialize function with the arguments passed into the clone function. I love doing things like this with Javascript.

Lastly, I had mixIn return itself. The reason is so that I could put the clone and mixIn on one line and only name the prototype once. This means I'm not duplicating the names of my objects and can more easily refactor the names. It also makes it easy to see the name and what prototype it is inheriting from.

I've always been infatuated with prototype-based programming languages. Javascript makes it possible to play with a real life prototype-based language. It saddens me to know that the next release of the standard introduces classes and that so many frameworks force classes into it. Once you get into using prototypes, you don't want to go back. You can use prototypes for a primitive versioning system, change tracking, and more that I haven't thought of yet. If you find more, let me know. Have fun using Javascript in new excting ways.

Labels: ,


Comments
  • I'm with you on hoping classes never become a 'standard' part of Javascript. In fact, Javascript probably should have standardized the __proto__ property and left off 'new', 'instanceof', and the whole constructor charade. These things can be built on top of the prototype model.

    Thanks for commenting.

    By Blogger TECHHEAD, at 3:52 AM   

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:

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.

Labels: , ,


Comments
  • The post reminded me the message #do:separatedBy:. The problem looks analog to its behavior, but I my smalltalk is a bit rusty right now..

    By Blogger tenuki, at 11:04 PM   

  • It is similar. But, do:separatedBy: iterates through the collection and then calls the separatedBy after each one except the last. Here I wanted to call the separatedBy: after x members in the collection and make sure it called in the end as well.

    By Blogger Blaine, at 7:36 AM   

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.

Labels: ,


Comments
  • I like the phrasing in the Scheme Standard:

    Programming languages should be designed not by piling feature on top of feature, but by removing the weaknesses and restrictions that make additional features appear necessary.

    Sadly, R6RS seems to have lost sight of this to a degree.

    By Blogger Jeremy, at 10:04 PM   

  • In all fairness, although there’s a lot of explanation for how to do unusual things, the simple things really are simple to accomplish, like using blocks with methods.

    So although I like the Scheme quote, someone defending Ruby might quote Alan Kay:

    Simple things should be simple, complex things should be possible.

    :-)

    By Blogger Reginald Braithwaite, at 8:16 PM   

  • I agree that most things in Ruby are simple. It's just frustrating for me when I'm trying to solve a problem and my head is completely into it and then WHAM! I'm caught off guard with a compiler issue or prevented by something else. It's the cognitive friction that I would like to remove from Ruby. I think the core ingredients are there, but I think Ruby should go through a feature removal. The first things for me to go would the ampersand followed closely by all of the built-in global variables.

    I would like to see Ruby go the path less traveled and not the one Java took.

    By Blogger Blaine, at 8:51 PM   

  • By Blogger Reginald Braithwaite, at 8:55 PM   

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?

Labels:


Comments

Sunday, June 22, 2008

My Hopes For Ruby 2.0

 
I must admit I haven't been following Ruby 2.0 too closely lately. But, I since I've been using 1.8 quite a bit lately. I thought I would list the things that are in 1.8, but I hope are gone in 2.0. This is a prayer of good vibes.

  1. Make blocks first class citizens. So, no more & in your methods. Everything should be an object. Blocks are no exception in fact, they have more right than anyone. This will make blocks syntactically cheaper too. No more "lambda". Happiness is cheap syntactic blocks.

  2. Reflection on everything. Yes, this means the stack. It also means keep the source in memory as well as part of classes and blocks. This might could used for a lot of cool things (like interesting ways to traverse code). Don't worry. It doesn't add much memory.

  3. Make blocks accept the same kind of parameters as methods. Right now, you can not have methods take an implicit block (you know with the & in front). Wait a minute. If number one is satisfied, this will be too. Oh goody. Cheap blocks take away complexity. Isn't this reason enough to have them?


OK, that's it for now. The above things would make life so much easier. I could lobby for taking away case statements and most keywords, but I think the above would be great to take the language forward without hurting too much.

Labels:


Comments



Web hosting by ICDSoft

Metalheads Against Racism
This page is powered by Blogger. Isn't yours?


My Weekly Top 20 Artists