Arc Forumnew | comments | leaders | submit | thaddeus's commentslogin
2 points by thaddeus 5242 days ago | link | parent | on: Noob question.

In addition to hasenj's suggestion of some:

  arc> (find 1 (list 1 2 3))
  1
The benefit of using 'find' is that it returns the value if found and nil otherwise. Depending upon how you're implementing your code you may discover 'find' works better. ie what if your going to pass the result into another function?

example:

   (tag-user (find user admins*))
might be better than:

   (if (some user admins*)
       (tag-user user))

-----

2 points by rocketnia 5242 days ago | link

As long as we're going down this road...

  arc> (some 'c '(a b c d c b a))
  t
  arc> (find 'c '(a b c d c b a))
  c
  arc> (mem 'c '(a b c d c b a))
  (c d c b a)
  arc> (pos 'c '(a b c d c b a))
  2
  arc> (keep 'c '(a b c d c b a))  ; significantly less efficient
  (c c)
All of these ('some, 'find, 'mem, 'pos, and 'keep) use 'testify, so if the thing you're searching for is a function, it will actually use that function as a predicate instead of searching for the function value itself.

  arc> (pos no '(a b c nil e))
  3
  arc> (pos no (list idfn no 72))
  nil
  arc> (pos [is no _] (list idfn no 72))
  1
By the way there's a gotcha specifically with 'find, which makes it an odd one out: You can't find nil with it. The function will be called with nil (if it's a function), but a success will just result in an intermediate return value of nil (the found value), which is then considered to be a failure, and the loop keeps going.

  arc> (find [do (prn "checking " _) no._] '(1 2 nil 3))
  checking 1
  checking 2
  checking nil
  checking 3
  nil
Even if 'find could find nil, a successful value of nil wouldn't be very helpful. ^_^

-----

1 point by markkat 5242 days ago | link

Thanks, rocketnia. That really does clear some things up. Here, 'some works great for me, since I am passing the result to 'case with two instances.

I can't say I have got a feel for Arc yet, but I think I am starting to. The more I work with it, the more it seems to open up. Not so much that feeling with the HTML definitions, however. But I am not going down that road until I have a much better handle on the language.

BTW, are there any good editors other than vi and emacs? I used vi years ago for FORTRAN, didn't like it, and have never used emacs. Currently I am using Wordpad. :p

-----

1 point by evanrmurphy 5242 days ago | link

> BTW, are there any good editors other than vi and emacs? I used vi years ago for FORTRAN, didn't like it, and have never used emacs. Currently I am using Wordpad. :p

Not sure how anyone could would want something besides vi/vim or emacs, but I use emacs with vim key bindings so maybe I'm biased. ;)

vi and emacs are great editors, but each has a mammoth learning curve. I think this is why they often make bad first impressions. I'd be interested to know more about your experience with vi and what you disliked about it.

Other editors I see people using include Notepad++, Textmate, jEdit, nano, NetBeans and DrRacket (which you might like for Arc and Racket code). Unfortunately, I don't know much about these firsthand.

-----

2 points by markkat 5241 days ago | link

I guess I expected this answer. :) Although I hadn't heard of nano. I should probably bite the bullet and start to familiarize myself with emacs or vi since they are so universal. Probably emacs.

My experience with vi involved some physics modeling years ago in undergrad. I recall 'zz'? I never stop forgetting to toggle the input mode. I don't know why. Maybe I am a bit right-brained, but I have a sloppy way of working. Something about vi felt so 'tight', and I prefer a canvas feel. Not really the best mindset for programming, no doubt, but I have learned it's ok to be a bit sloppy and get things done rather than be very neat and unproductive.

Sorry, starting waxing philosophical there... thanks.

-----

1 point by rocketnia 5241 days ago | link

For what it's worth, you're not the only one. I haven't gotten the hang of anything but the Windows text box conventions. :)

Pretty much all I want is an editor that loads quickly, shows whitespace, highlights matching parens, and does regex search-and-replace and find-in-files. Usually my choice has been EditPlus, but Notepad++ is a great open-source alternative, and gedit's another good one (although I think I've had to use grep separately with gedit).

I turn to jEdit for editing Unicode documents, NetBeans for editing C/C++, and DrRacket for editing big Racket projects, but those are extremely rare situations for me. Most of the time I avoid all three of them because of how long they take to load; I can almost always get in and out faster with plain Notepad.

I haven't looked at TextMate or nano.

-----

1 point by evanrmurphy 5241 days ago | link

> Something about vi felt so 'tight', and I prefer a canvas feel. Not really the best mindset for programming, no doubt, but I have learned it's ok to be a bit sloppy and get things done rather than be very neat and unproductive.

Upvoted for waxing philosophical.

-----

1 point by thaddeus 5241 days ago | link

Textmate (http://macromates.com/) at least has syntax colouring using aran's bundle:

https://github.com/aran/arc.tmbundle

If you're on windows, there's a version of textmate called 'e:

http://www.e-texteditor.com/

It can use aran's syntax colouring bundle as well.

-----


Very Nice.

> Attributes are specified by symbols, so there's no ambiguity.

I agree. This is one thing I really dislike in arc. There's ambiguity everywhere, not just in the html libraries.

For example the obj macro:

  (obj first "Farley" last "Mowat")
The case macro:

  (let x 'y 
    (case x y (pr "YES")))
It's just everywhere. And it particularly annoys me because it means you can not pass in functions as arguments to be evaluated (for the symbol elements).

Unfortunately your library, even though an improvement, would be one more layer of inconsistency. And by that I mean it becomes yet another mind switch I need to make when using arc + library-x. It's like the entire language needs an overhaul to remove the ambiguity, making it easier to adopt libraries such as yours.

Also, as a question:

Shouldn't this:

  (e "div" 'class "myclass" "content" "content")
 
Actually be:

  (e 'div 'class "myclass" "content" "content")
? After all "div" would generate a non-string output.

-----

1 point by rocketnia 5246 days ago | link

I can certainly understand if you'd prefer for certain Arc utilities to not be automatically quoted for you, since it saves a single character and costs a lot of flexibility, but I wouldn't say it's ambiguous.... I think I'd use "ambiguous" to refer to code that takes advantage of unspecified behavior.

I'm sure you've considered this, but we can technically accomplish the kind of code you prefer:

  (copy (table) 'first "Farley" 'last "Mowat")
  (tablist:pair:list 'first "Farley" 'last "Mowat")
  
  ; using 'switch from Anarki's lib/util.arc (but it uses (is a b)
  ; rather than testify.a.b, so functions still don't work)
  (let x 'y
    (switch x 'y (pr "YES")))
But I agree, it'd be nicer if the standard utilities had these behaviors, so that good behaviors could have good names. ^_^ (Incidentally, this is a place where namespaces could make the issue a matter of library development rather than language overhauling.)

As for your question about (e "div" ...) versus (e 'div ...), I think it would work either way. The tag name (not to be confused with an attribute name) can be anything that has the desired [pr _] behavior.

-----

1 point by thaddeus 5246 days ago | link

It's ambiguous.

When you read a piece of code like:

  (case x y (dosomething)))
Is y a symbol? or a variable name from somewhere else in the code? Unless you've memorized every macro's inner workings it's clearly open to more than one interpretation.

Where as with:

  (case x 'y (dosomething)))
you clearly know you are passing in a symbol.

-----

3 points by rocketnia 5246 days ago | link

Unless you've memorized every macro's inner workings it's clearly open to more than one interpretation.

That's the thing. To interpret (case ...) at all, you have to know what 'case is supposed to do as a macro. At least one suggestion on this forum (http://arclanguage.org/item?id=10858) would intentionally(?) have your example work in a different way:

  (case x 'y (dosomething))
    == reads as ==>
  (case x (quote y) (dosomething))
    == expands to ==>
  (let gs123 x (if (in gs123 'quote 'y) (dosomething)))
When I started out with lisps, I had trouble with this. I kept reading (quote x) as though it might evaluate x, or (let ((foo bar)) ...) as though it might involve a function call to foo. It wasn't just a matter of vocabulary; even if I figured out what (quote ...) did, some macro form around that code might give it a completely different meaning! What if one of these unintelligible commands had the side effect of rebinding 'quote? It took me a while to realize lisp programmers weren't quite as sadistic as that, but that revelation in itself doesn't make the whole issue go away. ^_^ You sorta do have to memorize every macro.

Of course, we could just go for read macros everywhere, rather than putting cheap abbreviations into the normal macros:

  ; assuming 'x abbreviates (quote x), which it does
  (obj 'key "value")
  
  ; assuming #'x abbreviates (fn () x)
  (if condition
    #'then
    #'else)
  
  ; assuming #,x abbreviates (fn (gs123) (zap gs123 x))
  (push elem #,place)
  
  ; assuming #. var x y z abbreviates (fn (var) x y z), with the read
  ; macro terminating once a mismatched closing parenthesis is reached
  (w/outstring #. var
    body1
    body2
    body3)
  
  ; assuming all of the above
  (mac #,case (var . body)
    (w/uniq #. g-var
      `(let var #. ,g-var (case-part-2 ,g-var ,@body))))
  (case x #''y #'(dosomething))
A good portion of the macros I write do nothing but prevent the need for these abbreviations (except they do put the #. variable in a more idiomatic spot), so it's a significant move. I kinda intended to show how bad it was, but what do you think? If we found hieroglyphics we could get used to, maybe they wouldn't be bad at all....

-----

3 points by akkartik 5246 days ago | link

you convinced me on case. I've fixed it in my repo, but only have internet on my kindle, won't push for a week.

-----

1 point by rocketnia 5245 days ago | link

I hope you fixed it so it uses 'testify, and I hope 'testify uses 'iso. :)

-----

2 points by akkartik 5245 days ago | link

Great idea! Done. i had case using just iso.

-----

2 points by akkartik 5240 days ago | link

Pushed. https://github.com/akkartik/arc/commit/294eef69b50ed4efe4873...

-----

1 point by hasenj 5246 days ago | link

Actually the ambiguity I was referring to is about what's an attribute and what's content.

  (some-function a b c d e f g)
Vanilla arc assumes all of "a b c d e f g" are regular arguments. If you want to supply named arguments, you'd need an extra pair of parens

  (some-function a b c (d x) (g y))
I wanted to get rid of the extra parenthesis without introducing ambiguity, and do it in a manner that's consistent with the way html tags work.

Now of course, one could still write confusing code:

    (= word 'class)
    ; some pages later:
    (div word "class-name" "actual-content")
btw, (e 'div ....) also works.

-----

1 point by evanrmurphy 5246 days ago | link

> This is one thing I really dislike in arc. There's ambiguity everywhere, not just in the html libraries.

So you'd rather have the following over arc's versions?

  (= 'x 1 'y 2 'z 3)

  (let 'x 1
    (pr x))

  (with ('x 1 'y 2)
    (pr x y))

  (obj 'a 1 'b 2)
I actually wrote recently about how I love this particular idiom in arc [1]. Unless I'm misunderstanding your point.

---

[1] http://arclanguage.org/item?id=13032

-----

3 points by rocketnia 5246 days ago | link

I don't know if I speak for thaddeus here, but I do think (obj 'a 1 'b 2) would a better API than (obj a 1 b 2), for a couple of reasons. For one thing, since it's something you may want to use a lot, it helps to be able to continue to use it even in the odd cases where you have keys you want to evaluate.

The other reason's a bit more in-depth: I think it's oftentimes good practice to use gensym keys, and especially gensym types. It helps prevent libraries from conflicting with each other. When using this technique, an 'obj that doesn't evaluate its keys is pretty useless, but an 'obj that does evaluate its keys is as convenient as (obj a 1 b 2) again, where 'a and 'b are variables holding the gensyms.

What I really think is best is to just have both. Looking at my site-generator code, I see that I defined (objal a 1 b 2) in terms of (tabal 'a 1 'b 2). :-p I think that's generally why I make so many macros that are merely cheap abbreviations of functions, actually; I like calling the function versions directly every once in a while, but I like saving a bit of line noise in the common cases.

As for your (= 'foo 42) example, I think that's one place where the quote really doesn't fit. No matter how many times you say (= 'foo 42), you should never witness (is 'foo 42), so you're not really assigning the value of that expression. However, if (= #,foo 42) were short for (= [zap _ foo] 42), which behaved as ([zap _ foo] [do 42]), that would totally make its own kind of sense. Of course, I'd want to have it both ways again, so I'd name that version 'fn-= or something so that '= could stay the same.

-----

2 points by waterhouse 5246 days ago | link

> What I really think is best is to just have both. ... I think that's generally why I make so many macros that are merely cheap abbreviations of functions, actually; I like calling the function versions directly every once in a while, but I like saving a bit of line noise in the common cases.

I agree with this. Compare it with having naming and anaphoric macros: iflet and aif, rfn and afn, even fn and []; I'd add awhile and whilet to the list even though awhile isn't in official Arc, and fromfile, tofile, and ontofile[1].

In fact, it is a very general practice in (functional?) programming to first write a function that handles something basic, then write another function on top of it that's more pleasant to work with. It really is ubiquitous. It's good because it separates the primitives from the high-level interfaces; if you want to change the primitives, you don't need to concern yourself with the high-level interface, and if you want to change the high-level interface, you don't need to concern yourself with the primitives. And macros give you flexibility, the ability to construct pleasant interfaces in ways you can't do with pure functions; for example, functions must evaluate all their arguments once, from left to right, while macros don't have to.

In this case, I don't think we have a function that directly does what you're asking for (i.e. it operates like obj except it evaluates the keys). I consider this a problem. Note that in Scheme and CL, we can go (vector x y z) instead of crap like (let u (make-vector 3) (= u.0 x u.1 y u.2 z) u). We should likewise have a good table-constructing function even if it isn't strictly necessary as a primitive. At the moment, this is the closest we have:

  (apply listtab (pair:list 'a 1 'b 2))
  =
  (obj a 1 b 2)
So I think we should have a function like this:

  (def ???? args
    (apply listtab pair.args)) 
What should we call it? I think it's rare enough to want evaluated keys that we don't need to give it a short name like "obj". Maybe "make-table", "make-hash", "make-obj"... Hmm, there's a function named "table". Given no arguments, (table) returns an empty hash-table. We could make this be our table-constructing function--in fact, I think that makes a lot of sense, as the function defined above should return an empty table when given no arguments. I again draw your attention to the analogy of the vector function in Scheme and CL. Only problem is, the table function currently defined in Arc actually is overloaded: if you give it an argument, it will apply this argument (which should be a function) to the table before returning the table.

  (xdef table (lambda args
                (let ((h (make-hash-table 'equal)))
                  (if (pair? args) ((car args) h))
                  h)))
  --ac.scm
I grepped Arc for '(table ' and found three instances (all in news.arc) where the function table was actually called with an argument [the other matches were, like, (tag (table width "100%" cellspacing ...))]. They look like this:

  (table 
     [each ip ips
       (= (_ ip) (dedup (map !by (+ (bads ip) (goods ip)))))]))

  (table [each-loaded-item i
           (awhen (and i!dead (check (sitename i!url) banned-sites*))
             (push i (_ it)))]))
The raw-Arc way to do this would be

  (let u (table)
     (each ip ips
        (= (u ip) ...))
     u)
and I believe Anarki has a w/table macro that would make it look like this:

  (w/table u
     (each ip ips
        (= (u ip) ...)))
Which, I think, is close enough to the original that PG/RTM (whoever wrote the above code) wouldn't be too displeased if we kicked out the old usage of table. Actually, you know, this alternating-pairs proposed usage only applies when the number of arguments is even; it wouldn't even conflict if we wanted to implement both behaviors: 1 arg -> empty table, even args -> alternating pairs. In fact, it would simplify implementation to do alternating pairs until there are less than 2 args left, and then if there's 1 arg, treat it as a function and apply it to the table, while if there are 0 args, just return the table.

So. Options: (1) Have table do alternating key-value stuff; if there's one argument remaining at the end, apply it to the table; then return the table. (2) Have table do alternating key-value stuff; signal an error if there's an odd number of arguments [and Paul Graham can get used to w/table], or (2a) if there's one argument, apply that function to the table, while if there are an odd number ≥3 of arguments, signal an error. (3) Leave table as is, and find a new name for our function.

I think (1) is the best thing to do at the moment, and (2) is probably a better future goal but I would be fine being overruled on that (its single advantage is error-reporting when you accidentally give table an odd number of arguments). I think (2a) is a stupid hack and (3) is bad. As such, here is an implementation of (1). Replace the old '(xdef table ...' definition in ac.scm with the following:

  (xdef table (lambda args
                (let ((h (make-hash-table 'equal)))
                  (define (slave xs)
                    (cond ((null? xs) 'nonce)
                          ((null? (cdr xs))
                           ((car xs) h))
                          (else (hash-table-put! h (car xs) (cadr xs))
                                (slave (cddr xs)))))
                  (slave args)
                  h)))
  ;Usage
  arc> (table)
  #hash()
  arc> (table [= _!a 1 _!b 2])
  #hash((a . 1) (b . 2))
  arc> (table 'a 1 'b 2)
  #hash((a . 1) (b . 2))
  arc> (table 'a 1 'b 2 [= _!b 3 _!c 4])
  #hash((a . 1) (b . 3) (c . 4))
[1] I mentioned these here: http://arclanguage.org/item?id=12877

-----

2 points by rocketnia 5246 days ago | link

I like all your reasoning, but I think you may have missed this post of mine: http://arclanguage.org/item?id=13236

I gave two ways we can already accomplish the behavior somewhat conveniently even if we don't have something sensible like your version of 'table:

  (copy (table) 'a 1 'b 2)
  (listtab:pair:list 'a 1 'b 2)
(Actually, I used tablist in that post, which was a bug.)

I could have sworn the Anarki version of 'table allowed for (table 'a 1 'b 2) at one point (which inspired me when I made 'objal and 'tabal for my static site generator), but I looked at the Git blame and didn't find any obvious evidence of that....

-----

1 point by waterhouse 5246 days ago | link

Oh, nice. I am not familiar with the copy function, but I missed that "listtab:pair:list" idea. Clever. I'm sure you agree, though, that we should have a nice table function, in the same way that we shouldn't have + defined for us but have to define - ourselves:

  (def - args
     (if (no cdr.args)  ;or no:cdr.args with my recent hack[1]
         (* -1 car.args)
         (+ car.args (* -1 (apply + cdr.args)))))
[1] http://arclanguage.org/item?id=13172

-----

1 point by rocketnia 5246 days ago | link

I honestly wouldn't mind very much if '- were omitted. >.>; Actually, wait. Arc puts every global in the same namespace, so we'd have a few conflicting versions of '- in our respective personal utilities (assuming we don't all just grab the Anarki version), and that would be somewhat horrible.

But yeah, 'table is much better as a function that can actually construct complete tables all at once. ^_^ In this case, it's not a matter of name conflicts between libraries; it's a matter of the most obvious names being taken for the wrong behaviors. Namespaces are still a potential solution, though.

-----

1 point by waterhouse 5246 days ago | link

Namespaces solve the problem flawlessly only if you plan for no one to work on or read anyone else's code, ever.

Taking '- as the example: there are a few non-obvious things about it (how it works with more than 2 arguments, or how it works with 1 argument), and people could reasonably end up with different versions of it (I do think the current version is best, but I wouldn't blame anyone for not thinking about the 3-or-more-variable case or for implementing it differently).

This is not a problem if you use '- as a utility in the definition of your library functions and someone else just imports the exported functions of your library and uses them. But if you intend to, say, paste code on the Arc Forum and expect anyone to understand it, or if you want people to be able to download the source code of your library and make improvements to it, then there's a cost to every non-standard definition you use: the reader has to learn it. If the language doesn't provide "map" and "let" and "with" (which are certainly not primitive; in fact, they're defined in arc.arc), then either you don't use those operators (and your code will suffer for it), or you do use them and that's one more thing the reader needs to learn to understand your code. It's not the end of the world, it's not a game-breaker for all projects, but it's one more obstacle to people understanding your code, and if you can get rid of it, then that is all to the good.

This is why getting people to agree on a good common core language is a good thing even if the language supports namespaces.

-----

1 point by rocketnia 5245 days ago | link

...getting people to agree on a good common core language is a good thing even if the language supports namespaces.

That's a good point. ^_^ I don't use Anarki- or Lathe-specific stuff in my examples if I can help it. And I recently mused on[1] the trend for a language to incorporate a lot of features in the standard so as to improve the common ground, so I should have seen that point to begin with.

[1] http://arclanguage.org/item?id=13139

Namespaces solve the problem flawlessly only if you plan for no one to work on or read anyone else's code, ever.

I've sorta been banking on the idea that everyone programs to their own languages of utilities anyway, and that each of those languages can be talked about, accepted as a common topic, promoted by its proponents, and standardized just like language cores can. Certainly CMSes and game engines have those kinds of communities. :)

Multiple independently developed libraries won't always work together well enough to all be clearly canon components of the language's identity, but I think they are more likely to be interoperable than are things designed from the outset to be entirely new languages.[2] So I don't think it's important for the core libraries to be cutting-edge in practice. The core can fade into the background to a degree.

I haven't thought about this stuff very deeply yet, and it's not rooted in a lot of fact, so please feel free to disillusion me. ^^

[2] I don't mean to suggest there's a well-ordered expected value of interoperability. Whether a library is designed or used in an interoperable or impregnable way is sorta relative and idiosyncratic. One more avenue for interoperability could even be annoying if people come to depend on it in poorly prepared-for ways. ^_^

-----

2 points by waterhouse 5246 days ago | link

Regarding (= 'foo 42): I do think it should be possible to perform assignment on symbols that are chosen at runtime. In Common Lisp, this would be (setf (symbol-function 'foo) 42); in Racket, this is (namespace-set-variable-value! 'foo 42). It's possible to do (eval `(= ,x ',desired-value)) [the ' is necessary because the Arc evaluator doesn't like objects in code], but this feels like a hack and there should be a built-in way to do it (I've said this before, by the way).

What should it be named, and if we wish to use the setf pattern, what should we name the corresponding analogue of "symbol-value"? At the moment, (eval x) does the same thing as (symbol-value x) when x is a symbol [bad things might happen if you do this to 'nil, but otherwise, yeah]. Would (= (eval x) ...) be fine? Or should there be a specialized operator named something like "symbol-value"? For purposes of conservatism, giving a meaning to (= (eval x) ...) seems easiest...

-----

1 point by rocketnia 5246 days ago | link

I immediately thought you were suggesting a way to set local variables at runtime, but this way I have a better answer for you. ^_^

Lathe already defines (= global.x desired-value) so that it behaves just like (eval `(= ,x (',(fn () desired-value)))). As an everyday expression, global.x behaves like (bound&eval x), which makes (zap _ global.x) a bit more intuitive. This utility works on Arc 3.1, Anarki, Rainbow, and Jarc.

I don't mind if something like this is added to the language core too, but isn't that less axiomatic?

-----

1 point by waterhouse 5246 days ago | link

It is certainly less axiomatic--you don't need this to have a working Lisp. However, it's useful (I needed it to do my little module system), it's likely to be very simple to implement as a primitive (given access to Scheme, we can just use 'namespace-set-variable-value!), and its name clearly maps to its function and its setf behavior. (I'd be inclined to use the name "global-value" or "symbol-value", actually.)

Finally, it's easier to see "symbol-value" written in the language description and use it than to figure out how to use the 'eval method properly. Just look at how complex your definition is; even I, who do see why it's necessary to go ',desired-value instead of ,desired-value in my version [if objs in code worked properly, you could use the former version for a while without noticing it was wrong], I don't see why it's necessary to go (',(fn () desired-value)), and I certainly wouldn't think of it myself. (I still don't believe it's actually necessary, but I won't press the point.) Like the case of (++ (car (pop xs))), this is one of the things a core language is good for: the implementor has considered and avoided all the subtle pitfalls for you. (Actually, implementing it as a primitive with the "namespace-..." functions, there aren't any pitfalls to avoid.)

By the way, I don't know if the core of the language is supposed to be all that axiomatic. It should be clean, certainly, but by its nature it should provide more than minimal axioms. In an essay[1], Paul Graham talks about "the core language—those operators that are neither primitives like car and cdr, nor special-purpose library functions. I mean operators like CL's mapcar, let, remove-if-not, and so on."

At any rate, here's what I think: (a) There will be people who need the equivalents of 'symbol-value and (setf symbol-value)--people who write module libraries or debugging facilities or for whatever reason[2] want to determine names at runtime. (b) This might be enough by itself to warrant placement in the core language, but (c) it is extremely easy, not to mention efficient, to implement these as primitives, while it is subtle, error-prone, and inefficient to implement it with 'eval. Therefore, it should be built into the language. (Technically, if it's a primitive, then it's not part of the core language by the implicit definition from the essay, but whatever you call it, it should be built in.)

I feel better about rambling/ranting when I also produce something tangible, like working code. So, here you go:

  (def symbol-value (x)
    ($.namespace-variable-value $.ac-global-name.x))
  
  (defset symbol-value (x)
    (w/uniq g
      (list `(,g ,x)
            `(symbol-value ,x)
            `(fn (val) (($ namespace-set-variable-value!)
                        ($.ac-global-name ,x)
                        val)
               val))))
[1] http://paulgraham.com/core.html

[2] I once did the following in a chess program:

  (each p '(r k n)
    (each a '(a b c d e f g h)
      (each n '(1 2 3 4 5 6 7 8)
        (setf (symbol-function (symb p a n))
              (fn () ...)))))

-----

1 point by rocketnia 5245 days ago | link

I needed it to do my little module system

That's why it's in Lathe, too. ^_^ Since it's there to implement the module system, it's one of only a couple dozen Lathe utilities that isn't in a module, but I find it useful enough that I like it that way.

I don't see why it's necessary to go (',(fn () desired-value))

If you embed a literal vector in an expression (for instance, a tagged value), Racket will evaluate that expression as a copy of the vector. So (let foo (annotate 'a 'b) (= global!x foo) (is global!x foo)) would return nil, which I find frustrating. ^^ (There might be significant mutations-aren't-reflected-in-both-copies gotchas too, but I'm not sure right now.)

If you embed a procedure instead, I don't know if it copies that too, but it doesn't matter 'cause a copy of the procedure should still close over the same bindings. In practice, (let foo (annotate 'a 'b) (= global!x foo) (is global!x foo)) works, so I'm satisfied.

By the way, I don't know if the core of the language is supposed to be all that axiomatic. It should be clean, certainly, but by its nature it should provide more than minimal axioms.

This is a point I just acknowledged but challenged at http://arclanguage.org/item?id=13266. I'm not sure entirely whether and how I (dis)believe it, so a response is welcome. ^_^

it is extremely easy, not to mention efficient, to implement these as primitives, while it is subtle, error-prone, and inefficient to implement it with 'eval.

Sure. Implementing 'symbol-value in terms of 'eval is a pretty big abstraction inversion, and it causes lots of unnecessary consing and compilation at runtime. I'm kinda conviced. ^_^

working code

Incidentally, official Arc allows you to say (assign a.b t) and observe (bound 'a.b). Your 'symbol-value makes it possible to get the value of 'a.b again, whereas there's no non-exploit way to do that with 'eval. ^_^ (You can use the (cdr `(0 . ,_a.b)) bug, lol.)

Speaking of 'bound... why is 'bound a primitive when 'symbol-value isn't? XD Actually, I've wondered why 'bound was a primitive for a while, but that kinda-convinces me further. ^_^

-----

1 point by thaddeus 5245 days ago | link

For the record I prefer:

  (obj 'a 1 'b 2)
keys are not variable names, so I'll ask (in general) why should obj be treated so special or differently?

Again, as you already pointed out, it boils down to flexibility and consistency. I don't see why I would trade saving one character spot over both 1. being clear & consistent and 2. allowing functions to be passed in:

  (obj (look-up-something) 1 (look-up-something-else) 2)

-----

2 points by rocketnia 5245 days ago | link

Right, but once again, adding a few characters may make things more consistent, but does it aid clarity? I really don't know either way.

Using the same heiroglyphics as I posted last time, we can implement 'case entirely as a function:

  ; Hypothetically, this uses the macros 'def, 'whenlet, 'iflet, and
  ; 'do; the functions 'if, 'testify, 'get, and 'apply; the special
  ; forms 'fn, 'zap, and 'quote; and the read macros
  ; #,x for (fn (gs123) (zap gs123 x)), #'x for (fn () x), and
  ; 'x for (quote x).
  
  (def #,case (subject . args)
    (whenlet (first . rest) args
      (iflet (then . rest) rest
        
        ; Check the testified result of (first) against the subject.
        (if (.subject:testify:do.first)
          
          ; Return the result of (then).
          then
          
          #'(apply case-part-2 subject rest))
        
        ; Return the result of (first). It's the last else.
        first)))
  
  ; was (case x y (dosomething))
  (case x #''y dosomething)
This is even an overall brevity boost, I think. What do you think of its clarity? I seriously might try this out in Penknife....

-----

1 point by thaddeus 5245 days ago | link

Not sure. I find the #''y hard on the eyes :)

Though it reminds me a little on how clojure handles inline anonymous functions, which I really like:

    #{+ %1 %2} ;% being the args, %1 being the first, %2 the second, and so on
wondering if something similar is possible with case:

(case x #(myfun)(dosomething))

yet still able to:

(case x 'y (dosomething))

note: Sorry if I missed something and am suggesting non-sense stuff, I haven't yet looked into the code details you provided - so I could be out to lunch.

-----

1 point by rocketnia 5245 days ago | link

In case you missed the post I referred to, here it is: http://arclanguage.org/item?id=13240

All the necessary assumptions are listed in the top comment though (assuming you can intuit my intentions for the 'iflet macro, etc.). It's by no means implemented. ^^

Also, I welcome any switch in the heiroglyphics that makes them more readable. In Penknife, where there are infix operators but no read macros, I'd probably represent #''y as "thunk:sym.y" or "sym.y^". In Arc, ^'y looks like a good choice. Maybe :y or %y could be the zapper (or some better-suited abstraction over setforms).

-----

1 point by evanrmurphy 5244 days ago | link

> Maybe :y or %y could be the zapper (or some better-suited abstraction over setforms).

Maybe something with `=` in it?

-----

1 point by evanrmurphy 5245 days ago | link

> keys are not variable names, so I'll ask (in general) why should obj be treated so special or differently?

There is at least some precedent of special treatment. In JavaScript's object literals, for example, you can do

  {a : 1, b : 2}
instead of

  {'a' : 1, 'b' : 2}
Then again, in other languages like Ruby, the keys must be explicit symbols:

  {:a => 1, :b => 2}
I'm not sure what the prevailing trend is on this (or whether knowing so would help to inform us about which is the better design choice).

-----

4 points by thaddeus 5246 days ago | link

> So you'd rather have to write the following?

no, I prefer

  arc> (= x 1 y 2 z 3)
I'm not suggesting variable names need to be quoted.

What I don't want to do is to this:

  [1]arc> (= a x b y c z)
I prefer:

  [2]arc> (= a 'x b 'y c 'z)
(assuming x,y,z are in fact just symbols, I expect to use [2] not [1])

And I currently can do this in arc.

However there's

  (case a x (dosomething))
where x is in fact a symbol.

-----


Considering:

http://arclanguage.org/item?id=12619

You may want to email him instead.

-----

1 point by evanrmurphy 5254 days ago | link

It's ironic because if only Arc Forum supported notifo, pg might be aware of our plea! :P (Although not really, because he would probably just be notified of responses to his own comments.)

I know for a fact that SteveMorin was already aware of the post you linked to (because he emailed me about it). I think he's casting another vote.

-----

3 points by thaddeus 5254 days ago | link

A long time ago I made some asinine comment about PG not checking up on his own forum to fix a bug (lol - there were so few people on this forum, that his voting algorithm determined we were all playing three way voting rings then and stopped counting ~ more like a three person voting circus to me - lol).

Anyway, to which, aw - instead of complaining about it, just sent him an email. pg responded fairly quickly with a bug fix and all. Doh!

So maybe just email him and you'll get what you want.

-----

1 point by evanrmurphy 5254 days ago | link

lol thanks for the anecdote. I'll plan to email him, then. :)

-----

1 point by SteveMorin 5253 days ago | link

I was hoping he might notice the Ask PG if he went back to search through the Archives later. I don't usually like to Ask him stuff directly

-----


Personally I like how it currently works:

1. String operations are costly, printing to stdout is not. ie. if I need to run a function to generate some numbers, dumping them at the right time to stdout has very little overhead, having to weave them within a string operation costs so much more.

2. There's a benefit having a web server that pushes changes out to the browser incrementally via stdout. The user doesn't have to wait for the entire operation to complete to see the results. ie, what if the last half of your server operation, provides no output for half of your users?

3. Adding on to #2, for troubleshooting and iterative development purposes, it's nice to see a portion of the output within your browser to see how far a long your operation got, results wise, before it hit an error.

-----

2 points by hasenj 5254 days ago | link

Sounds like a case of sacrificing expressiveness for performance. Somehow I think this goes against the design principles of arc.

For #2 and #3, the output has to be so huge before you reap this benefit. Most apps don't have this property, and if they did, I'd think there's a deeper design problem. Such problems can be better solved using asynchronous javascript requests (aka ajax).

-----

3 points by thaddeus 5254 days ago | link

> For #2 and #3, the output has to be so huge before you reap this benefit.

I think it's, more so, a case of how complex your code is rather than how big your output is. I can have 200,000 lines of code that outputs 20 small numbers. Knowing it hit the 8th number and what that number is can be huge for both a user and for development.

Also - maybe it's just me, but having partial output has helped me with 20 lines of code and very little output.

> Sounds like a case of sacrificing expressiveness for performance.

Also - maybe it's just me, but I've been spending most of my time with Clojure, where the Ring web server requires a string for an output. I found my code became less expressive than arc.

So, for Clojure, I actually wrote my own html framework to mimic arc's functions that write to stdout, then just put a big wrapper on it at the end: (with-out-str (println "stuff")). Next I plan to see if I can hack Ring@Jetty to pipe the output too.

Kinda funny, I went to Clojure and did the opposite of you. :)

-----

1 point by hasenj 5253 days ago | link

Well then, back to my original question: how do you compose elements together?

-----

3 points by thaddeus 5251 days ago | link

So I gave it another whirl and here's my attempt to capture the essence of you're problem:

For example you would like to do this:

  [1] arc> (spanclass "links" 
             (string "use this link:" 
               (tag (a href "http://mydomain.com/the-place-to-go") "here"))
And have it return this:

  <span class="links">use this link:<a href="http://mydomain.com/the-place-to-go">here</a></span>
and your first attempt might be something like this:

  [2] arc> (spanclass "links" 
             (pr:string "use this link:" 
               (tag (a href "http://mydomain.com/the-place-to-go")(pr "here")))
only you find it returns the wrong results:

  <span class="links"><a href="http://mydomain.com/the-place-to-go">here</a>use this link:</span>
so now you're probably thinking by having functions return strings like this:

  [3] arc> (tag (a href "http://mydomain.com/the-place-to-go") "here")
  "<a href=\"http://mydomain.com/the-place-to-go\">here</a>"
then the original function [1] would have worked.

Instead, with arc, you need to approach your code differently. You need to think about the timing of when things are happening rather than having available 'string-things' that you can compose by nesting your functions.

So with arc [1] needs to become [4]:

  [4] arc> (spanclass "links" 
            (pr "use this link:")
            (tag (a href "http://mydomain.com/the-place-to-go")(pr "here")))

  <span class="links">use this link:<a href="http://mydomain.com/the-place-to-go">here</a></span>
Am I capturing it correctly? Does this answer your question on how I compose my functions?

-----

1 point by thaddeus 5253 days ago | link

I've re-looked into your original questions, in an attempt to provide a meaningful response, but I find the scenario's are not concrete enough.

For example I find the re-arrange function a little vague.

i.e. could you not:

  (def something (a b c)
      (output b c a))
Could you provide an real-case like example where you feel you can show a clear difference? For, I found, even your row example can easily work with stdout inside a function rather than using pg's macro. ie. Not liking how some of the existing functions/macros work doesn't mean string weaving is the answer.

And row is a pretty crappy example, even when I built my Clojure library, I ditched pg's implementation and went with a more useful implementation, yet it still uses stdout. You have to remember that pg only built those macro's to support his specific cases in his HN app.

Also, for > Too many tricks. Too clever.

Well it's a library, it's not expected you're crafting macros for your regular coding. I mean there's only so many HMTL cases you need to handle right? So if the library is complete, providing (macro's or not) succinct code and faster results, then it's probably good to have - tricks inside or not.

-----

1 point by thaddeus 5258 days ago | link | parent | on: News.arc for newbies

A few thoughts:

Trying to incorporate the news.arc code into another language is probably not a good path to follow, since all the code in news.arc is heavily dependent upon the core language and it's constructs or syntax. It's not like news.arc is a module or something with a ubiquitous interface.

Furthermore, If I were to create a news/forum in Ruby I would choose to use Ruby's constructs as opposed to trying to implement arc constructs that are not suited to the language. For example, in Ruby I would be using classes, where in Arc one would use templates. Each has a different approach to implementing the code and each would present a different set of challenges/options.

If you're just learning Ruby, I highly recommend learning how Ruby developers think. What's considered idiomatic for the language. Then just build it.

Honestly, even though much longer and harder, I would suggest as an option, you consider putting the other languages on hold, take some time to learn Arc and the ins/outs of news.arc, then go try to implement in Ruby, but in a more Ruby like approach.

I think you'll find the minute you try to figure out how to implement, for example, a template in Ruby that Ruby developers will guide you to a more idiomatic approach.

-----

1 point by gimondarake 5258 days ago | link

Hello thaddeus,

First of all, thank you very much for your reply :)!

I have never touched Arc and news.arc, so maybe I shouldn't even try to incorporate the news.arc code into another language.

I have never received an advice like "Learning how Ruby developers think.", but I believe it is one of the best advice I have received (especially for a newbie like myself.)

Maybe I am trying do to everything when I should be focusing on one thing and learning slowly but surely.

-----


A more advanced option: http://tmux.sourceforge.net/

-----


I do the same as akkartik.

There are many other options though... for example:

http://arclanguage.org/item?id=10604

-----


-hmm ... Could Arc take advantage of this?

-----


Being related to the topic at hand I thought this may be of interest:

http://arclanguage.org/item?id=4247

-----

1 point by akkartik 5268 days ago | link

Ooh, interesting. Thanks. &o and &k, hmm.. arc is basically like a lisp-1 lisp, so this would embrace that further.

clarification: ..like a lisp-1 common lisp.

-----

2 points by rocketnia 5268 days ago | link

What do &o and &k have to do with lisp-1-ness?

-----

1 point by akkartik 5268 days ago | link

Arc seems mostly like common lisp (nil vs (), nil vs #f, unhygienic macros, ..); the only two things it shares with scheme seem to be being a lisp-1 and dot syntax for rest args. Now &o makes the optional syntax similar to common lisp as well.

-----

1 point by evanrmurphy 5268 days ago | link

So when you said "lisp-1 lisp", that second "lisp" was referring to Common Lisp, not the general lisp family of programming languages?

I was just confused by that phrase. In any case, now I understand your point.

-----

1 point by akkartik 5268 days ago | link

Yeah I wasn't clear. I've been thinking about lisp-1 vs lisp-2 ever since http://arclanguage.org/item?id=12814

-----

1 point by evanrmurphy 5268 days ago | link

For what it's worth, I think your `?` looks much nicer than `&o`, and it elegantly fits into the language so long as we continue to have no ssyntactic use for the question mark.

-----

2 points by akkartik 5268 days ago | link

Yeah, the & is just a superficial point. But I've been thinking more about &key in common lisp. I thought it was pretty redundant if you get pervasive keyword args, but there's one case it doesn't cover: if you want your rest args to take priority over the optionals.

Consider the final versioning of updating that I came up with at http://arclanguage.org/item?id=12974:

  (mac updating(place ? expr t iff 'is . body)
    (w/uniq rhs
      `(let ,rhs ,expr
         (unless (,iff ,place ,rhs)
           (= ,place ,rhs)
           ,@body))))
It's nice to have the optional args when I need them, but I need to either specify both or say :body to begin the rest args:

  (mac firsttime(place . body)
    `(updating ,place
        :body
          ,@body))
If I had 'greedy rest args' or 'lazy optionals' I could make the rest args take precedence instead. Hmm.

Update: But it looks like I can't allow both these formulations at once:

  ; A
  (updating (uvar u last-login) (date)
    (++ karma.u))

  ; B
  (ret max 0
    (each elem '(1 3 2 6 5)
      (updating max :iff > elem
        (prn "max now: " max))))
Perhaps I can use ?? for lazy optionals:

  (mac updating(place ? expr t ?? iff 'is . body)
    (w/uniq rhs
      `(let ,rhs ,expr
         (unless (,iff ,place ,rhs)
           (= ,place ,rhs)
           ,@body))))
Now I can say both A and B above, but must have :body in:

  ; C
  (mac firsttime(place . body)
    `(updating ,place
        :body
          ,@body))
What do you think? Am I over-optimizing? :)

Update 2: Hmm, perhaps I should get rid of required args (your suggestion) and have eager optionals (default), lazy optionals after '?', and rest.

-----

2 points by rocketnia 5267 days ago | link

IMO, as long as we don't have ? ssyntax, ? should be usable as a local variable name, which a special parameter list meaning defeats. Conversely, &o is already impractical as a variable name thanks to & ssyntax, so it can have a special meaning in parameter lists without introducing any warts. (I consider it a wart that in official Arc 3.1, 'o can only be used as a variable name when not destructuring.)

-----

2 points by akkartik 5267 days ago | link

I think I disagree. o is certainly a wart, but ? seems unlikely to be a variable name anyway. Have you actually used ? as a variable name before?

There's nothing special about ssyntax. If we would consider using ? as ssyntax, it's fair game to use for other syntactic purposes as well.

Update: A second rationale. As waterhouse found out (http://arclanguage.org/item?id=12612) arc is extremely permissive about letting us override things, so there's no point focusing on what is legal in this arbitrary implementation.

A third rationale. Right now you want to disallow ? because it's not ssyntax. When it becomes ssyntax won't it still be unusable in arglists for the same reason & is unusable now? :) At that rate we'd never get syntax inside arglists. Which may be a plausible position, but then we should be arguing about whether arglists can benefit from syntax.

-----

1 point by rocketnia 5267 days ago | link

Have you actually used ? as a variable name before?

I agree with you that it's unlikely. I'm mainly just a consistency nut. :) I'd like for every symbol to be seen as either special everywhere, maximizing its potential power, or else special nowhere, maximizing the number of macros that'll know what to do with it.

At least, I like this kind of consistency as long as the language/utility is going for ease of customization ('cause of fewer cases to cover) and minimalism. When it comes to making code readable and writable, consistency can be less important.

---

A second rationale. As waterhouse found out (http://arclanguage.org/item?id=12612) arc is extremely permissive about letting us override things, so there's no point focusing on what is legal in this arbitrary implementation.

For a language people are eager to make breaking changes to for their own purposes, that's true. But several programmers trying to make interoperable code won't each make breaking changes independently, right? Arc may promote a philosophy of fluidity, but any particular version of Arc is still made immovable by the community that uses it.

---

A third rationale. Right now you want to disallow ? because it's not ssyntax. When it becomes ssyntax won't it still be unusable in arglists for the same reason & is unusable now? :) At that rate we'd never get syntax inside arglists. Which may be a plausible position, but then we should be arguing about whether arglists can benefit from syntax.

Hmm? You've got what I'm saying completely backwards. I think we should commit to having ? be completely special (a kind of ssyntax, for instance) as long as we want it to be special in parameter lists. Meanwhile, & is totally usable for special behavior in parameter lists, since it's already useless for normal behavior (variable names).

-----

1 point by akkartik 5267 days ago | link

I think I'm ok with us as a community saying "this version of arc treats ? as ssyntax" without necessarily having any code to do so :) expand-ssyntax for ? is the identity until we come up with a use for it. "Use ? as a variable at your own risk."

(Probably a strawman.) What I don't want is to start reserving tokens or characters 'for later use' and enforcing that they can't be variables and so on. Common Lisp does this and it's stupid. Did you know you can't declare a function called type even though it doesn't do anything, hasn't done anything for the past 15 years? What a colossal waste of a great token! https://github.com/akkartik/wart/blob/master/009core.lisp#L1

---

You've got what I'm saying completely backwards.

Ah yes, I did. You said &o is ok.

It does raise the question of the right way to setup ssyntax. Either we're making changes to expand-ssyntax everytime we want a special case to turn off ssyntax expansion or we're saying "leave & as is when it begins or ends a symbol."

But then you could argue that &o is still impinging on a potential variable name. It's starting to feel like arguing about angels and pinheads; why is it ok to pun + and & and : so they do different things in different contexts, but not use ? because of some speculative fear of potentially punning variable names?

-----

1 point by rocketnia 5267 days ago | link

Those are all questions I worry about. ^_^ I'm really itching to post about Penknife's syntax, since I think it does a good job of dissolving these issues.

-----

1 point by evanrmurphy 5267 days ago | link

> I'm really itching to post about Penknife's syntax, since I think it does a good job of dissolving these issues.

I'd like to know more about this.

-----

1 point by rocketnia 5267 days ago | link

Ye sorta receive. ^_^ http://arclanguage.org/item?id=13071

-----

1 point by evanrmurphy 5267 days ago | link

I see what you mean. So what are some ideas for an eventual `?` ssyntax? (I realize this is kind of changing the subject...)

-----

1 point by rocketnia 5267 days ago | link

Well, one thing I've occasionally considered is having string?b become [isa b 'string]. :-p That would only do so much though, since you can just define (def a- (type) [isa _ type]) and say a-!string.b.

A while ago I was thinking of using a?b to mean (b a), with the question mark acting as sort of a backwards-pointing period. Now, for Penknife, I'm using a'b for that purpose, since it's sorta an upside-down period and it doesn't require the shift key (or even reaching off of the home row). However, a'b is parsed as two expressions in Arc, so using a`b or a?b for this purpose would probably be easier.

-----

1 point by akkartik 5267 days ago | link

I'm starting to see the reason for your fears around ? :)

-----

3 points by thaddeus 5275 days ago | link | parent | on: Noobing with news.arc

Great response - as always!

the date comparison could be easier:

  (deftem profile
      ...
      last-login (datestring))

   (let old ensure-news-user
      (def ensure-news-user (u)
        (with (u (old u) today (datestring))
          (when (> today (uvar u last-login))
            (++ (karma u)))
          (update-last-login u today))
          u))

-----

1 point by akkartik 5275 days ago | link

Since time always increases, you can also just:

  (unless (is (date) (uvar u last-login))
    (++ karma.u)
    (= (uvar u last-login) (date)))
update: Hmm, I just realized there's a few places I can use this macro:

  (mac updating(place expr . body)
    (w/uniq rhs
      `(let ,rhs ,expr
         (unless (is ,place ,rhs)
           (= ,place ,rhs)
           ,@body))))

  ..
  (updating (uvar u last-login) (date)
    (++ karma.u))
Perhaps we need to make the is parameterizable as well in the general case. Hmm, this could be a generalization of my firsttime (http://arclanguage.org/item?id=12889)

  (mac updating(place ? expr t iff 'is . body)
    (w/uniq rhs
      `(let ,rhs ,expr
         (unless (,iff ,place ,rhs)
           (= ,place ,rhs)
           ,@body))))

  (mac firsttime(place . body)
    `(updating ,place
        :body
          ,@body))
(requires my keyword args and new optional syntax: http://github.com/akkartik/arc)

Now you can run several kinds of test-and-update:

  (firsttime user!loggedin
     (prn *welcome-message*))

  (updating (uvar u last-login) (date)
    :body
      (++ karma.u))

  ; track largest value seen so far
  (ret max 0
    (each elem '(1 3 2 6 5)
      (updating max :iff > elem
        (prn "max now: " max))))

  max now: 1
  max now: 3
  max now: 6
  6
Hmm, I might get rid of firsttime altogether. Is updating the right name for this macro?

-----

2 points by rocketnia 5274 days ago | link

For this:

  (mac updating(place ? expr t iff 'is . body)
    (w/uniq rhs
      `(let ,rhs ,expr
         (unless (,iff ,place ,rhs)
           (= ,place ,rhs)
           ,@body))))
You're evaluating the subexpressions of 'place twice, which isn't necessary thanks to 'setforms. Also, I like to have macros macro-expand and evaluate their parameters from left to right if I can, just for similarity with 'do. (I still try to evaluate only as much as I need to though.) To accomplish these things, I'd go with this (untested) implementation instead:

  (def fn-updating (current-val setter-getter new-val comparator body)
    (unless (do.comparator current-val new-val)
      (.new-val:do.setter-getter)
      (do.body)))
  
  (mac updating (place ? expr t iff 'is . body)
    (let (binds val setter) setforms.place
      `(withs ,binds
         (fn-updating ,val (fn () ,setter) ,expr ,iff (fn () ,@body)))))
In the meantime, for the purposes of dates, I'd at least use 'iso instead of 'is. You're probably not going to get the same (date) list as one you have from earlier today. Even if (date) itself made that guarantee, you've potentially persisted and un-persisted the user data today.

In fact, since you've already made 'iso extensible, I'd use it as the default instead of 'is.

-----

1 point by akkartik 5274 days ago | link

All great points, thanks! I have in fact stopped using is everywhere in my code, don't know what I was thinking.

-----

1 point by akkartik 5274 days ago | link

Why the do's?

And what does the .new-val do?

-----

3 points by rocketnia 5274 days ago | link

Saying (do.foo ...) instead of (foo ...) just makes it so that 'foo isn't in function position. That means the local variable 'foo will always be used instead of some macro named 'foo that happened to be defined in another library.

The ssyntaxes .foo and !foo are short for get.foo and get!foo. Also, a:b ssyntax is handled before a.b ssyntax, giving the : lower precedence.

That means (.new-val:do.setter-getter) compiles as though it's ((get new-val) ((do setter-getter))), which makes it a roundabout way to accomplish ((do.setter-getter) new-val) with one less pair of parentheses, one less character, or (by my count) one less token.

It's really sort of silly in this case, since you save on so little while rearranging so much. In its favor, he (.a:b ...) idiom makes a bit more sense when it's a field/element access like (!width:get-rect ...) or (!2:setforms ...). It's especially helpful when it's part of a big chain of composed forms like (.a:b:.c:.d:e ...).

In my own code, including Lathe, I define this:

  (def call (x . args)
    (apply x args))
Then the code is just call.setter-getter.new-val, giving a savings of two characters, two pairs of parentheses, or (by my count) four tokens, all without causing the expressions to be rearranged.

Seems like a lot of brevity boilerplate, huh? :-p

-----

2 points by aw 5274 days ago | link

Saying (do.foo ...) instead of (foo ...) just makes it so that 'foo isn't in function position. That means the local variable 'foo will always be used instead of some macro named 'foo that happened to be defined in another library.

I remembered talking about this once and found the comment: http://arclanguage.org/item?id=11697

It looks like it would be an easy change to Arc to have local variables take precedence, though I haven't tried the patch myself.

-----

1 point by akkartik 5274 days ago | link

Oh nice. So is there a way using this trick to do

  a."b".c.d.g
without parens?

-----

3 points by rocketnia 5274 days ago | link

Not in Arc. The best you get is (.g:.d:.c:a "b"), I think. You can shove "b" into ssyntax with string!b, but you need to use it as an argument somehow, and any ssyntax containing .string!b will pass the 'string function itself as an argument instead of calling it first.

Of course, if "b" were a symbol instead of a string, it would just be "a!b.c.d.g".

In Penknife, the answer to your question is actually yes: It's "q.b'a.c.d.g", where q is the string-quoting operator and ' is an operator that acts like a reverse a.b. It's easy to stump Penknife too, but I'm hoping to make a full thread about Penknife's take on infix operators in a couple of days or so.

-----

2 points by thaddeus 5271 days ago | link

Optionally, in ac.scm:

  (define (has-ssyntax-char? string i)
    (and (>= i 0)
         (or (let ((c (string-ref string i)))
               (or (eqv? c #\:) (eqv? c #\~) 
                   (eqv? c #\&)                
                   (eqv? c #\%)                
                   ;(eqv? c #\_) 
                   (eqv? c #\.)(eqv? c #\!)))
             (has-ssyntax-char? string (- i 1)))))

  (define (expand-ssyntax sym)
    ((cond ((or (insym? #\: sym) (insym? #\~ sym)) expand-compose)
           ((or (insym? #\. sym) (insym? #\! sym)(insym? #\% sym)) expand-sexpr)
           ((insym? #\& sym) expand-and)
       ;   ((insym? #\_ sym) expand-curry)
           (#t (error "Unknown ssyntax" sym)))
   sym))

  (define (build-sexpr toks orig)
    (cond ((null? toks)
           'get)	    
          ((null? (cdr toks))
           (chars->value (car toks)))
          (#t
           (list (build-sexpr (cddr toks) orig)
                 (cond ((eqv? (cadr toks) #\!)
                        (list 'quote (chars->value (car toks))))
                       ((eqv? (cadr toks) #\%)
                         (list 'string (list 'quote (chars->value (car toks)))))
                       ((or (eqv? (car toks) #\.)(eqv? (car toks) #\!)(eqv? (car toks) #\$)(eqv? (car toks) #\%))
                         (err "Bad ssyntax" orig))
                       (#t 
                       (chars->value (car toks))))))))
Then:

  arc> (= test (obj "a" "my a val" "b" "my b val"))
  #hash(("a" . "my a val") ("b" . "my b val"))

  arc> test%a
  "my a val"
[edit: actually, it would be nicer to have the percent symbol represent the spaces in the string and have some other symbol signify string handling, but I never got around to it + my scheme foo is lacking :)]

-----

1 point by akkartik 5274 days ago | link

Is fn-updating generally useful?

-----

More