Re: [Readable-discuss] RESTART *..*
On 1/17/13, Alan Manuel Gloria almkg...@gmail.com wrote: On 1/17/13, David A. Wheeler dwhee...@dwheeler.com wrote: Personally I'm not (yet?) a big fan of $. Not a problem. It's grown on me. Once you have it, you find that patterns where it applies are remarkably common. (^^)v In support of that, consider the following: (cond (( x -1) (* x x)) (( x 1) (* x x)) (( x 0) (sqrt (abs x))) (else (sqrt x))) ..without $, we'd have to use: cond ( x -1) * x x ( x 1) * x x ( x 0) sqrt (abs x) else sqrt x ...with $: cond ( x -1) $ * x x ( x 1) $ * x x ( x 0) $ sqrt $ abs x else $ sqrt x ...which is a lot nearer to the original s-expression. The only caveat is that in this form, $ is followed by a *single* expression, but if you're using multiple expressions, you should really be using indentation anyway. (admittedly, you can also just retain the parentheses, but every parenthesis cuts the power of t-expressions, because parentheses disables stuff.) Here's a better example of the intended use of $: import $ srfi srfi-45 import $ amkg object ; use for debugging define probe(x) print(x) x define lazy-construct(a d) lazy $ probe $ let ((rv #f)) set! rv $ object-construct 'cons-cell object-method-register rv 'car $ lambda () a 'set-car! $ lambda (na) $ set! a na 'cdr $ lambda () d 'set-cdr! $ lambda (nd) $ set! d nd rv -- I feel $ is a really good balance of something-weird (con) but extremely useful (pro). Sincerely, AmkG -- Master HTML5, CSS3, ASP.NET, MVC, AJAX, Knockout.js, Web API and much more. Get web development skills now with LearnDevNow - 350+ hours of step-by-step video tutorials by Microsoft MVPs and experts. SALE $99.99 this month only -- learn more at: http://p.sf.net/sfu/learnmore_122812 ___ Readable-discuss mailing list Readable-discuss@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/readable-discuss
Re: [Readable-discuss] RESTART *..*
On 1/16/13, David A. Wheeler dwhee...@dwheeler.com wrote: Alan Manuel Gloria: Okay, the *semantics* is confusing to me. How are the following suppose to parse? The answer is, whatever we decide it should be :-). So now is a perfect time to discuss it. And if some combination seems to be nonsense, we can declare it an error. BTW, Don't pay serious attention to the sweet.g file's action statements for restart_list. I'm doing a lot of experiments with them and they're almost certainly wrong right now. So with that caveat... library foo * begin define cat 'meow define dog 'woof * I assume: (library foo (begin (define cat 'meow) (define dog 'woof) ) Yes, that's what I think too. But that's if we allow same-line and later-lines simultaneously at *all*. My current draft BNF *does* allow this (by intent), but if the semantics are too complicated, we could just say either (1) it must be all on one line, or (2) the start has to be followed by hspace* comment_eol. But let's continue the thought... But what happens with this? library foo * begin print(x) define cat 'meow define dog 'woof * To be honest, I don't know of a use case that suggests any particular semantic. I can image that this *might* mean: (library foo (begin (print x) (define cat 'meow) (define dog 'woof))) Though perhaps other semantics would make sense. How about this? let * x 5 * { x + 42 } I think t that should mean: (let ((x 5)) (+ x 42) Basically, *...* wraps an extra () around an expression. Since x 5 means (x 5), * x 5 * == ((x 5)). And since what follows *...* is just another parameter, that parameter should be treated normally. Or worse, this? let *x y w z * 42 I'm guessing that should perhaps be: (let (x y (w z)) 42) Perhaps we'd better off to not allow stuff to be on both the same line and on later lines, at least at first. The library definition use case and the let use case are easily distinguished that way. That might mean you'd have a multi-line statement with a begin on its own line, but if it's a long sequence of definitions, that may not be a big deal. Okay here's the concrete semantics I propose: 1. The head production is the part that looks for the *. Upon finding a *, it consumes any number of horizontal spaces, line comments, and multiline/datum comments. It then enters superlist production 2. superlist just calls the top-level i_expr repeatedly, creating a list of items, and terminating upon finding a *, returning the yielded list. 3. When superlist returns, head just takes its returned value and takes it as a single item, and continues on its merry way looking for n-expressions and superlists after the *. The third part allows: let * x 5 \\ y 6 * {x + y} You know, maybe we should call it SUPERLIST in contrast with SUBLIST. ^^;;; You're smiling, but that's not an entirely insane name. It's hard to think of a good name for this. MEGALIST UBERLICHST OVERLYPRETENTIOUSADJECTIVELIST Sincerely, AmkG -- Master Java SE, Java EE, Eclipse, Spring, Hibernate, JavaScript, jQuery and much more. Keep your Java skills current with LearnJavaNow - 200+ hours of step-by-step video tutorials by Java experts. SALE $49.99 this month only -- learn more at: http://p.sf.net/sfu/learnmore_122612 ___ Readable-discuss mailing list Readable-discuss@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/readable-discuss
Re: [Readable-discuss] RESTART *..*
On Jan 16, 2013 3:29 PM, David A. Wheeler dwhee...@dwheeler.com wrote: If we separate the 2 issues, I feel the no-blank-lines is the more problematic of them. In Python, I'm fine with most of my defs being indented because they're class methods, but couldn't live without new lines. Do you agree? I don't *exactly* agree, though you may be missing an element of sweet-expressions that makes end-on-blank-lines work. But first, my priorities. My current view is that the more important requirement is that the REPL and file format need to be exactly the *same* in a Lisp-based system. In Python you often cannot cut-and-paste from files into the command line (REPL), because Python files and the REPL have different blank-line semantics. (Technically, the Python spec only covers files, but that's a useless nuance.) That's actually a problem in Python today, and annoys me sometimes. In a Lisp such a difference would be crazy, because Lisps are programmable programming languages where experimentation is common. I'll defer to you on this though I don't feel this is such a big problem in python; GUI shells solve it by distinguishing pressing Enter from pasting Enter, and/or by requiring ctrl+Enter (or backspacing the indent then Enter) to terminate multiline command (IIRC dreampie does the latter). Unfortunately, lisp doesn't have python's : hint that the expression will be a multiline one. It's indeed annoying in the terminal. IIRC ipython has some magic %paste syntax to paste an arbitrary block. IOW, the file/repl tension does justify a construct like this, but it's a REPL only construct... Now for blank lines. Blank lines ending an expression actually isn't bad, even in a larger program, because of the rule that comment-only lines (possibly indented) are completely ignored, and do NOT end an expression. I agree that without that rule it'd be hideous to use, but using comment-only lines to vertically separate material actually works very cleanly. Ah, indeed I missed that! It changes a lot of priorities in my arguments, because if I'd keep the indentation and only use this for empty lines, that feels a petty - perhaps I should just live with empty comments and not need a new construct at all; and let people who also want to reset indentation have their way... It's not insane to use some marker to mean end of expression, several languages do that. Indeed, sweet-expressions could be modified to that easily enough. But then users have to remember to do that after every expression. If REPL use is rare, that'd be fine. But I expect lots of people to use the REPL, often, and I want the REPL to be very pleasant to use. It's hard to beat enter a blank line to evaluate. Agreed. I absolutely hate SQL prompts that don't execute until I add a ; Well, the current notation is obviously not a disaster, since we've written programs without it. But when the variables involve modest amounts of calculation, it gets harder to see what you're doing than I'd prefer, because it's easy to suddenly require more paren-nesting than is typical in other languages. Contrast: let ((x cos(f(c ! dostuff x with: let * x cos(f(c)) * ! dostuff x In one-variable lets, a common mistake is to forget to embed the variable in double-parens. Since the parens are also usually used for the expression calculation, it can be easy to miss. Making it possible to visually distinguish the outer parens that create the variable list, from the internal parens used for the expression, makes it clearer which parens are more structural vs. the ones involved in the variable calculation. That, in turn, makes it easier to notice the omission of doubled parens for one variable. * is 2 chars, (( is 2 chars. Some of your win here is just from using spaces to set apart the delimiters. Isn't this better addressed by schemes that allow [..] in place of (..): let [ (x cos(f(c))) (y sin(f(c))) ] ! dostuff x y which is more homoiconic by preserving the nesting level. I feel that let's structure is truly annoying, but it's not the notation's job to hide that if it becomes less homoiconic; I'd rather fix the construct and use a let1 macro. or: let * x $ cos $ f c * ! dostuff x Personally I'm not (yet?) a big fan of $. I have even more reservations about \\ usage: let * x cos(f(c)) \\ y sin(f(c)) * where the list depth hinges on the fact that there are 2 elements between the \\. I see how such constructs are appealing in-line once you're used to them, but this makes me ask: could you lift $ and/or \\ to a separate layer from indent processing, so they remain available inside regular (..) lists? As it turns out, the new ruleset for *...* that I just posted is quite simple, and it basically only requires one extra line in the spec to handle the on-the-same-line case. I must admit I haven't even tried to keep up with your grammar work. I'll try to take a look - the whole thing is much shorter than the
Re: [Readable-discuss] RESTART *..*
Beni Cherniavsky-Paskin: * is 2 chars, (( is 2 chars. Some of your win here is just from using spaces to set apart the delimiters. Isn't this better addressed by schemes that allow [..] in place of (..): let [ (x cos(f(c))) (y sin(f(c))) ] ! dostuff x y which is more homoiconic by preserving the nesting level. I feel that let's structure is truly annoying, but it's not the notation's job to hide that if it becomes less homoiconic; I'd rather fix the construct and use a let1 macro. I agree that let's structure in the 1-variable case is annoying (I also sometimes use a let1 macro), and I agree that it's not a notation's job to *hide* annoying structures. But if a structure is common and annoying to *use*, it's reasonable to look for notations that make it *easier* to use. Hiding bad, ease-of-use good :-). Personally I'm not (yet?) a big fan of $. Not a problem. It's grown on me. Once you have it, you find that patterns where it applies are remarkably common. I have even more reservations about \\ usage: let * x cos(f(c)) \\ y sin(f(c)) * where the list depth hinges on the fact that there are 2 elements between the \\. I see how such constructs are appealing in-line once you're used to them, I think that's the key point. It needs to be easy for people to mentally map a construct to the underlying list structure, of course, but after a while, you learn that certain visual patterns are the easy way to use a construct. The same thing happens in other languages; I read the following C construct: for (i=0; i max; i++) ... as a single trivial idea, because it's such a common pattern. but this makes me ask: could you lift $ and/or \\ to a separate layer from indent processing, so they remain available inside regular (..) lists? Sure, that's possible. But do we *want* to? 1. That would mean that you'd have to escape them even inside (...). 2. It mildly interferes with backwards compatibility. I'd like traditional code to mostly work as is - in a backward-compatible way - if it's cleanly formatted. Programs that use symbols $ or \\ would then have problems. 3. I'd like to be able to call down to the underlying reader as much as possible to implement constructs; doing this *requires* that we override processing the contents of a list, even if it already has a curly-infix reader. I must admit I haven't even tried to keep up with your grammar work. I'll try to take a look - the whole thing is much shorter than the one-at-a-time mails made me think :-) Thanks! I'm not against having a way to restart indentation; I just wanted to keep the *option* to not restart it to 0, as a matter of taste. Though in light of empty comments always being available, I'm ok with a construct that always forces indent to 0, if it's simpler. It's much simpler. There are some nasty subtleties in *starting* with a non-zero indent and trying to make it mean the obvious in a Lisp-based language; see the draft SRFI for more details if you're curious. It could work in other languages, but in Scheme, it's hard because (1) the read interface of Scheme is already fixed, and (2) standard Scheme doesn't support unlimited unread-char. Sure, it must be multi-char. I was think more in the directions of e.g. #( ... )# but those might well be taken for things like vectors. Yes, #( introduces a vector comment. And # is busy enough; there are a lot of nonstandard Scheme extensions that start with #, so anything starting with # is frankly not safe for use (it'd probably conflict with SOMETHING). Another very unbaked idea: perhaps we can take a hint from typography and formats such as reStructuredText, markdown and emacs outline-mode (which all took that hint): Represent most structure using indentation, but some structure above that using several levels of headings. Not a crazy idea, but that would only deal with the case for multi-line that begins on the left edge. That wouldn't deal with the small let case, or anything that has some simple prefix. I know you don't care about short let, but *I* do :-). I think like this direction on aesthetic grounds, but I suspect the practical convenience of having some reader macro entering the t-expr parser in the middle of s-expr parsing will prevail. Yes, that's my thinking too. --- David A. Wheeler -- Master Visual Studio, SharePoint, SQL, ASP.NET, C# 2012, HTML5, CSS, MVC, Windows 8 Apps, JavaScript and much more. Keep your skills current with LearnDevNow - 3,200 step-by-step video tutorials by Microsoft MVPs and experts. ON SALE this month only -- learn more at: http://p.sf.net/sfu/learnmore_122712 ___ Readable-discuss mailing list Readable-discuss@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/readable-discuss
Re: [Readable-discuss] RESTART *..*
On 1/17/13, David A. Wheeler dwhee...@dwheeler.com wrote: (library (amkg animals (1 0)) (export cat (rename (rover dog))) (import (only (amkg pets (1 0)) rover)) (define cat (quote meow))) Is that what you expected? Yes ^^ On 1/17/13, David A. Wheeler dwhee...@dwheeler.com wrote: Personally I'm not (yet?) a big fan of $. Not a problem. It's grown on me. Once you have it, you find that patterns where it applies are remarkably common. (^^)v I'm not against having a way to restart indentation; I just wanted to keep the *option* to not restart it to 0, as a matter of taste. Though in light of empty comments always being available, I'm ok with a construct that always forces indent to 0, if it's simpler. It's much simpler. There are some nasty subtleties in *starting* with a non-zero indent and trying to make it mean the obvious in a Lisp-based language; see the draft SRFI for more details if you're curious. It could work in other languages, but in Scheme, it's hard because (1) the read interface of Scheme is already fixed, and (2) standard Scheme doesn't support unlimited unread-char. *Technically* R5RS and R6RS don't support unread-char, at all. However, most implementations of Scheme support some kind of unread-char, and some of them only support a 1-character unread-char. So we have adopted the limitation of requiring only 1 character lookahead, which can be done by using (let ((c (read-char port))) (unread-char c port) c). Another very unbaked idea: perhaps we can take a hint from typography and formats such as reStructuredText, markdown and emacs outline-mode (which all took that hint): Represent most structure using indentation, but some structure above that using several levels of headings. Not a crazy idea, but that would only deal with the case for multi-line that begins on the left edge. That wouldn't deal with the small let case, or anything that has some simple prefix. I know you don't care about short let, but *I* do :-). An interesting idea IMO. Hmm. I think like this direction on aesthetic grounds, but I suspect the practical convenience of having some reader macro entering the t-expr parser in the middle of s-expr parsing will prevail. Yes, that's my thinking too. --- David A. Wheeler -- Master Visual Studio, SharePoint, SQL, ASP.NET, C# 2012, HTML5, CSS, MVC, Windows 8 Apps, JavaScript and much more. Keep your skills current with LearnDevNow - 3,200 step-by-step video tutorials by Microsoft MVPs and experts. ON SALE this month only -- learn more at: http://p.sf.net/sfu/learnmore_122712 ___ Readable-discuss mailing list Readable-discuss@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/readable-discuss -- Master Visual Studio, SharePoint, SQL, ASP.NET, C# 2012, HTML5, CSS, MVC, Windows 8 Apps, JavaScript and much more. Keep your skills current with LearnDevNow - 3,200 step-by-step video tutorials by Microsoft MVPs and experts. ON SALE this month only -- learn more at: http://p.sf.net/sfu/learnmore_122712 ___ Readable-discuss mailing list Readable-discuss@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/readable-discuss
Re: [Readable-discuss] RESTART *..*
Okay, I've tried to write down my thoughts on WHY I think restart lists are a good idea. Comments welcome. A better name is welcome for sure :-). BTW, I have a draft BNF and action rules, but don't take them seriously for restart lists; I used them more for experimentation. --- David A. Wheeler h2 id=reservedRestart lists (lt;* ... *gt;)/h2 p Sweet-expressions without restart lists (lt;* ... *gt;) work well in a vast number of circumstances. However, they can be somewhat awkward for two typical use cases: /p ol liA long sequence of definitions contained within an initial statement. This situation occurs in many library definition structures such as Scheme R7RS ttdefine-library/tt and in some larger data structures./li liA let-style statement with one or two variables with short initial values./li /ol p Let's begin with the first use case. When there is a long sequence of definitions contained within an initial statement, and no special notation like restart lists, all the definitions in the long sequence must be indented and none can be separated by a blank line (since that would end the entire sequence, not just a definition). Indenting almost an entire file is annoying, and needing no blank lines for that long invites mistakes. /p p For example, here's an example from the R7RS Scheme specification for define-library: /p pre (define-library (example grid) (export make rows cols ref each (rename put! set!)) (import (scheme base)) (begin (define (make n m) (let ((grid (make-vector n))) (do ((i 0 (+ i 1))) ((= i n) grid) (let ((v (make-vector m #f alse))) (vector-set! grid i v) (define (rows grid) (vector-length grid)) (define (cols grid) (vector-length (vector-ref grid 0))) (define (ref grid n m) (and ( -1 n (rows grid)) ( -1 m (cols grid)) (vector-ref (vector-ref grid n) m))) (define (put! grid n m v) (vector-set! (vector-ref grid n) m v /pre p This is easily reformatted into this sweet-expression: /p pre define-library example grid export make rows cols ref each rename(put! set!) import scheme(base) begin define make(n m) let (grid(make-vector(n))) do (i(0 {i + 1})) ! {i = n} grid ! let (v(make-vector(m #f alse))) vector-set!(grid i v) define rows(grid) vector-length(grid) define cols(grid) vector-length(vector-ref(grid 0)) define ref(grid n m) and {-1 n rows(grid)} {-1 m cols(grid)} vector-ref vector-ref(grid n) m define put!(grid n m v) vector-set!(vector-ref(grid n) m v) /pre p But there are reasons that sweet-expressions are defined the way they are. It is fundamental that a child line is indented from its parent, since that is the point of indentation. Opening a parentheses intentionally disables indentation processing; this is what developers typically expect (note that both Python and SRFI-49 do this), and it also makes sweet-expressions very backwards-compatible with traditional s-expressions. Ending a definition at a blank line is very convenient for interactive use, and interactive and file notation should be identical (since people often switch between them). Changing all of sweet-expressions, just to handle this particular case, does not seem warranted. /p p Now let's look at the second use case. The sweet-expression notation cleanly handles cases where let-expression variables have complex values (e.g., using \\), but for simple cases (1-2 variables having short initial values) it can take up more vertical space than traditional formatting. Using a leading $ takes up somewhat less vertical space, but it still takes up an additional line for a trivial case, it does not work the same way for let expressions with 2 variables, and David A. Wheeler thinks it is a rather unclear construction. You can also use parenthetical notation directly, but this is relatively ugly and it is annoying to need to do this for a common case. A similar argument applies to do-expressions, and these are not at all unusual in Scheme code: /p pre let ; Using \\ takes up a lot of vertical space in simple cases \\ x 5 {x + x} let \\ x 5 y 7 {x + x} let ; Less vertical space, but 1 variable only $ x 5 {x + 5} ; Note $ x 5 $ y 7 isn't right; that maps to ((x 5 (y 7))). ; The two-variable format can be surprising and does not let the ; programmer emphasize the special nature of the variable assignments ; (compared to the later expressions in a let statement). let x(5) y(7) {x + 5} let (x(5)) ; Use parentheses {x + x} let (x(5) y(7)) {x + x} /pre p A irestart list/i is surrounded by the markers lt;* and *gt;. The lt;* and *gt; represent opening and closing parentheses, but restart indentation processing at the left edge instead of disabling indentation processing. The purpose of restart lists is to make it easy to clearly express these and similar use cases. /p
Re: [Readable-discuss] RESTART *..*
The bit: ; Note $ x 5 $ y 7 isn't right; that maps to ((x 5 (y 7))). ..should be better placed in a paragraph - I for one tend to skim over source code in the SRFI. -- The bit: Changing all of sweet-expressions, just to handle this particular case, does not seem warranted. ...this doesn't seem to scan well. It seems to introduce an argument to support a position (don't change all of sweet-expressions) but the paragraph it's in doesn't look like it introduces the opposing position. -- You might want to juxtapose: let \\ x $ foo bar use x versus: let * x $ foo bar * use x I suggest you use something similar to what you did in readable.sourceforge.net: use a table so that both examples are side-by-side. I also suggest using the given example above rather than (let ((x 5)) (+ x x)), since it gives a better justification for using RESTARTBEGIN / RESTARTEND : We want to use foo bar not foo(bar) for stylistic reasons (i.e. foo is a command whose return status we want to know, not a function that just computes something). -- This bit: begin lt;* Looks wrong. Shouldn't that be: lt;* begin ?? Of course, it depends on what, exactly, is meant by * *. So maybe: begin . * ?? Sincerely, AmkG -- Master Java SE, Java EE, Eclipse, Spring, Hibernate, JavaScript, jQuery and much more. Keep your Java skills current with LearnJavaNow - 200+ hours of step-by-step video tutorials by Java experts. SALE $49.99 this month only -- learn more at: http://p.sf.net/sfu/learnmore_122612 ___ Readable-discuss mailing list Readable-discuss@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/readable-discuss
Re: [Readable-discuss] RESTART *..*
Okay, the *semantics* is confusing to me. How are the following suppose to parse? library foo * begin define cat 'meow define dog 'woof * I assume: (library foo (begin (define cat 'meow) (define dog 'woof) ) But what happens with this? library foo * begin print(x) define cat 'meow define dog 'woof * How about this? let * x 5 * { x + 42 } Or worse, this? let *x y w z * 42 Sincerely, AmkG P.S. You know, maybe we should call it SUPERLIST in contrast with SUBLIST. ^^;;; -- Master Java SE, Java EE, Eclipse, Spring, Hibernate, JavaScript, jQuery and much more. Keep your Java skills current with LearnJavaNow - 200+ hours of step-by-step video tutorials by Java experts. SALE $49.99 this month only -- learn more at: http://p.sf.net/sfu/learnmore_122612 ___ Readable-discuss mailing list Readable-discuss@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/readable-discuss
Re: [Readable-discuss] RESTART *..*
Alan Manuel Gloria: Okay, the *semantics* is confusing to me. How are the following suppose to parse? The answer is, whatever we decide it should be :-). So now is a perfect time to discuss it. And if some combination seems to be nonsense, we can declare it an error. BTW, Don't pay serious attention to the sweet.g file's action statements for restart_list. I'm doing a lot of experiments with them and they're almost certainly wrong right now. So with that caveat... library foo * begin define cat 'meow define dog 'woof * I assume: (library foo (begin (define cat 'meow) (define dog 'woof) ) Yes, that's what I think too. But that's if we allow same-line and later-lines simultaneously at *all*. My current draft BNF *does* allow this (by intent), but if the semantics are too complicated, we could just say either (1) it must be all on one line, or (2) the start has to be followed by hspace* comment_eol. But let's continue the thought... But what happens with this? library foo * begin print(x) define cat 'meow define dog 'woof * To be honest, I don't know of a use case that suggests any particular semantic. I can image that this *might* mean: (library foo (begin (print x) (define cat 'meow) (define dog 'woof))) Though perhaps other semantics would make sense. How about this? let * x 5 * { x + 42 } I think t that should mean: (let ((x 5)) (+ x 42) Basically, *...* wraps an extra () around an expression. Since x 5 means (x 5), * x 5 * == ((x 5)). And since what follows *...* is just another parameter, that parameter should be treated normally. Or worse, this? let *x y w z * 42 I'm guessing that should perhaps be: (let (x y (w z)) 42) Perhaps we'd better off to not allow stuff to be on both the same line and on later lines, at least at first. The library definition use case and the let use case are easily distinguished that way. That might mean you'd have a multi-line statement with a begin on its own line, but if it's a long sequence of definitions, that may not be a big deal. You know, maybe we should call it SUPERLIST in contrast with SUBLIST. ^^;;; You're smiling, but that's not an entirely insane name. It's hard to think of a good name for this. --- David A. Wheeler -- Master Java SE, Java EE, Eclipse, Spring, Hibernate, JavaScript, jQuery and much more. Keep your Java skills current with LearnJavaNow - 200+ hours of step-by-step video tutorials by Java experts. SALE $49.99 this month only -- learn more at: http://p.sf.net/sfu/learnmore_122612 ___ Readable-discuss mailing list Readable-discuss@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/readable-discuss