On 4/10/06, Jack Unrue jdunrue@gmail.com wrote:
I feel the need to strike the right balance between UI/logic separation vs. taking full advantage of Cells.
Oh, if you are big on separation maybe you can confirm something I suspect, namely that Cells, by allowing different instances of the same class to have different rules for the same slot, makes OO design.
For example, one can have a game normally look like:
(make-instance 'game :speed (c? (* (^difficulty-slider) 500))...
But then for debugging a hard problem you might want to play back a log at full speed and so the replay code goes:
(make-instance 'game :speed 500...
In the first case the game /instance/ is being driven by the GUI slider widget, but the game /class/ does not know about the GUI.
Does that make sense in resolving the separation issue?
ken
On 4/10/06, Ken Tilton kentilton@gmail.com wrote:
Oh, if you are big on separation maybe you can confirm something I suspect, namely that Cells, by allowing different instances of the same class to have different rules for the same slot, makes OO design.
For example, one can have a game normally look like:
(make-instance 'game :speed (c? (* (^difficulty-slider) 500))...
But then for debugging a hard problem you might want to play back a log at full speed and so the replay code goes:
(make-instance 'game :speed 500...
In the first case the game /instance/ is being driven by the GUI slider widget, but the game /class/ does not know about the GUI.
Does that make sense in resolving the separation issue?
I'm going to assume a certain interpretation of your example that you might not have actually intended, so if you didn't mean that the value of (^difficulty-slider) is literally a Cells wrapper around a slider or some specific widget type, then please by all means correct me. And thus ignore the rest of this and just know that we'd be totally agreeing :-)
So at the risk of preaching to the choir...
I'm not too far out of disagreement with you, but consider a slightly different arrangement. In the above example, rename (^difficulty-slider) to something like (^difficulty-selector). What the renamed slot now indicates is that the "difficulty" value is still user input but we don't specify what UI is actually used to implement that -- it's just some mechanism for which the game logic doesn't need to know the specifics. And at the same time, change the formula associated with the difficulty-selector such that it's not directly tied anymore to any particular widget type. The game UI code is obviously responsible for translating widget events into values posted to this selector model. So, in some respects, the name change seems pedantic, but it's an important part of the abstraction.
By not pre-supposing what control is actually driving that slot, you get some freedom to make UI changes that (within reason) won't affect this game logic. For example, in version 1 the difficulty setting is a slider, but after usability testing you realize that a slider is too granular, and users are better served if it's just three radio buttons. The UI code would change such that (^difficulty-selector) values are now posted by code that manages the radio buttons, but the game logic remain unchanged.
This facilitates the same sort of debug-mode hardcoding of the difficulty value, again without requiring a change to the game logic. The debug value is just another form of user input, albeit a hard-coded value where the user is actually the programmer trying to debug a problem.
Really, I'd bet this isn't too far from the concept you have in mind, I'm just saying that we can abstract that a bit further. Again, that's if I'm interpreting you correctly.
-- Jack Unrue
Jack Unrue wrote:
On 4/10/06, Ken Tilton kentilton@gmail.com wrote:
Oh, if you are big on separation maybe you can confirm something I suspect, namely that Cells, by allowing different instances of the same class to have different rules for the same slot, makes OO design.
For example, one can have a game normally look like:
(make-instance 'game :speed (c? (* (^difficulty-slider) 500))...
But then for debugging a hard problem you might want to play back a log at full speed and so the replay code goes:
(make-instance 'game :speed 500...
In the first case the game /instance/ is being driven by the GUI slider widget, but the game /class/ does not know about the GUI.
Does that make sense in resolving the separation issue?
I'm going to assume a certain interpretation of your example that you might not have actually intended, so if you didn't mean that the value of (^difficulty-slider) is literally a Cells wrapper around a slider or some specific widget type, then please by all means correct me. And thus ignore the rest of this and just know that we'd be totally agreeing :-)
So at the risk of preaching to the choir...
I'm not too far out of disagreement with you, but consider a slightly different arrangement. In the above example, rename (^difficulty-slider) to something like (^difficulty-selector). What the renamed slot now indicates is that the "difficulty" value is still user input but we don't specify what UI is actually used to implement that -- it's just some mechanism for which the game logic doesn't need to know the specifics. And at the same time, change the formula associated with the difficulty-selector such that it's not directly tied anymore to any particular widget type. The game UI code is obviously responsible for translating widget events into values posted to this selector model. So, in some respects, the name change seems pedantic, but it's an important part of the abstraction.
By not pre-supposing what control is actually driving that slot, you get some freedom to make UI changes that (within reason) won't affect this game logic. For example, in version 1 the difficulty setting is a slider, but after usability testing you realize that a slider is too granular, and users are better served if it's just three radio buttons.
Exactly what I had in mind. I actually should have given:
(md-value (widget-named :game-difficulty))
Then the slider is named :game-difficulty and the md-values range from 0 to 1. The rule does not have to change if :game-difficulty becomes a radio-group container widget of three radio buttons easy, average, and hard as long as the radio-group rule for md-value translates the selection to 0.33, 0.66, and 1.0 or something like that.
But I am going further and saying there is not much harm in having the radio-group just take on the symbolic values 'easy, 'average, and 'hard because we can then change the code to:
(make-instance 'game :difficulty (c? (ecase (md-value (widget-named :game-diificulty)) (easy 0.333)...etc
ie, the ability to tailor instances with rules vs mere literal values such as 0.333, 0.555 makes decoupling less urgent. The developer just has to arrange at some point for the game difficulty to resolve to a value between zero and one.
ken