The Artima Developer Community
Sponsored Link

Agile Buzz Forum
One more Unit of Understanding

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
James Robertson

Posts: 29924
Nickname: jarober61
Registered: Jun, 2003

David Buck, Smalltalker at large
One more Unit of Understanding Posted: Jun 17, 2004 4:47 PM
Reply to this message Reply

This post originated from an RSS feed registered with Agile Buzz by James Robertson.
Original Post: One more Unit of Understanding
Feed Title: Travis Griggs - Blog
Feed URL: http://www.cincomsmalltalk.com/rssBlog/travis-rss.xml
Feed Description: This TAG Line is Extra
Latest Agile Buzz Posts
Latest Agile Buzz Posts by James Robertson
Latest Posts From Travis Griggs - Blog

Advertisement

Measurements

There's this really cool package called Measurements in the Open Repository. I don't think it's cool because I authored; it's really cool because it employs a neat trick for unit reduction which I don't take credit for. Read the package comment for more...

Said package allows one to attach 'units' (meters, liters, candela, newtons, etc.) to ArithmeticValues. For example:

4 ~> 'kg'		"makes 4 kilograms"
5 as: 'cd.m/hr2'		"makes 5 meter-candelas per hour squared"
1 ~> 'm' ~> 'cm'		"makes 1 meter, and converts it into 100 centimeters"

The ~> is just a shortcut for the as: message. The problem is... ~> is no fun to type. And even if there was a good binary selector that was easy to type (aside: "I know! We could use >> or <<!"), it's still the same problem one often runs into with points:

(1 @ 2 + 4 @ 3) = (1@2 + (4@3))   --> false

You saw this coming, right?

Wouldn't it be nice if you could just use a unary message to create these things? One could write them for the common ones I guess. But there are 417 initialized in the Measurements system, and it has the ability to do arbitrarily composed ones, e.g. "inch x candela per radian x second x kilogram."

You've guessed already, haven't you? doesNotUnderstand: to the rescue. The basic idea is to take a look at the selector, and see if we can't make a unit name out of it. Of course the simple units are easy. But we'd like to be able to do composite units too. The only character that isn't a normal alphanumeric and isn't a binary message character is the $_ (sorry Squeak guys). We'll use that for the $/ character. And it just so turns out that out of all those units, none of them uses the $x character anywhere! And that looks like the multiplication product thing. Now that we can transform a selector pattern into a Unit name, we just have to override doesNotUnderstand:. Quite trivial:

ArithmeticValue>%gt;doesNotUnderstand: aMessage 
	^[self as: (self selectorAsUnitName: aMessage selector)] 
		on: Measurements.UnknownUnitError
		do: [:ex | super doesNotUnderstand: aMessage].

Now we can just evaluate 4 mL and out comes 4 milliliters. Or 5 kgxm_s2 and out comes 5 newtons. If we evaluate 5 winebego we get a message not understood.

You didn't see this coming though

That's not good enough for today's blog though. There are those that have complained that this kind of stuff makes a mess of the debugger. Evaluating 5 winebego leaves a bunch of interesting (more than 10) interesting frames in the stack. And no one wants to wade through that. Not even me. So let's try this again:

ArithmeticValue>%gt;doesNotUnderstand: aMessage 
	| mc |
	mc := thisContext.
	[^self as: (self selectorAsUnitName: aMessage selector)] 
		on: Measurements.UnknownUnitError
		do: [:ex | ].
	^[super doesNotUnderstand: aMessage] on: MessageNotUnderstood
		do: 
			[:ex | 
			ex searchFrom: mc sender.
			ex pass]

That's better. Now 5 winebego looks like a normal dnu: in the debugger. What's going on here? Well first of all, I have to give credit to Eliot Miranda for showing me how to do this once upon a time. The thing is, I'm not sure if that's exactly how he did it. I'd welcome better versions from those that are better than I with context crunching. Whether or not Eliot taught me this particular approach or not, he proved to me once upon a time that if you get your hands on the context, you can do just about anything.

The first part of the message, is an assignment of the current context. We capture the pseudo variable thisContext in a temp, so that we can use it later. By the time we get to where we're going to want information about this context, we'll be in a block context and buried in layers of exception handling/dnu: contexts. We try to create and return Measurement by turning the selector into unit string, if the unit doesn't exist, we trap that and continue on. Then we fire the super doesNotUnderstand:. But we immediately turn around and trap the signal. We do this so we can short circuit all of the work we just did. We set the MNU's original context, to the context that fired this dnu: override in the first place. And then pass it on to be caught be the debugger machinery.

This same technique could be used in yesterday's blog to hide the isotropic selector scenario. Or the multi-with:'s of the day before.

The results of today's code are in a package called !Measurements-ByMessage! in the Open Repository, which adds the dnu: behavior to the prereq'd !Measurements! package. If !Measurements! sounds interesting, but this dnu: stuff scares you, just load !Measurements!

Read: One more Unit of Understanding

Topic: In meetings Previous Topic   Next Topic Topic: Can we sandbox in VisualWorks?

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use