Arc Forumnew | comments | leaders | submitlogin
Why does pr return it's first arg?
3 points by jazzdev 5408 days ago | 16 comments
Do you know if why pr returns it's first arg? It seems more reasonable that it should return nil. It doesn't appear to be an accident of implementation from looking at arc.arc - it appears quite intentional.

I'm writing my own HTML package (yeah, who isn't?) and it would be convenient if pr returned nil. So now I'm wondering why it doesn't.

I suppose you could wrap pr around something to debug it without changing the semantics of your code while debugging. Does anyone do that?



3 points by akkartik 5408 days ago | link

disp returns nil, so if you don't care about return value, just use that.

pr is useful when I want to insert debug statements. e.g.

  (do-something (prn:stage1 (stage2 val)))
Hmm, perhaps it should return the last arg instead, that way I can add strings in front, which seems more common than at the end..

-----

1 point by palsecam 5408 days ago | link

> Hmm, perhaps it should return the last arg instead, that way I can add strings in front, which seems more common than at the end.

yeah but that would be more costly. Getting the 'car of a linked list is cheap, getting the last element isn't (O(n)).

I think that is reason why it returns the car.

Instead of not returning anything, it returns something. I think this is good. this can be useful sometimes. For instance, (good) C code does that. It's rare to see a "void" function in C, better to instead return some infos, even if you don't see an obvious use of it for the moment.

But returning something should not be expensive. Therefore the 'car.

---

Jazzdev, where is the 'pr behaviour problematic? In my view, if you want it to return 'nil instead, it's basically that you don't care about, that you discard the result. Then getting the 'car changes nothing.

Anyway, akkartik is right, you can use 'disp. But be careful it can only prints one argument, and it takes an optional second one, the stream where to print the value (stdout by default), where 'pr takes any nb of args (and displays them to stdout).

Or I suppose you know this, you can do: (do (pr arg1...argN) nil).

Or even, if you're going to use it a lot: (def prnil args (apply pr args) nil)

---

In the same "please return something" philosophy, it could be good if 'each and co actually return something else than 'nil. Sometimes it was a problem for me, I used a 'each construct in a 'if test or something, and the test (obviously) failed.

'each returns the iterated table if it is called with "expr" being a table. Maybe it should return the iterated list when called with a list. (Or at least there should be coherence: the other solution is to make 'each always nil. Currently, the situation is confusing.)

Another idea is to make the iteration constructs ('for and co, also) return t. Then they could be used w/out problem in 'if. It makes sense: "the job is done" => t. But for 'each, returning "expr" makes more sense.

Another idea is to keep returning nil, but I find this poor. That's basically killing the "everything is an expression that returns something [implicitely: useful]". That's just a bit better than Scheme #<void> (which is a pure abomination).

Similarly, Anarki does this I think, and so does my ac.scm, but a minimum is to make 'system returns t if success, nil otherwise (and a better solution might be to return the actual exit code of the proc). Don't make nil the new #<void>.

---

BTW, the other day I cried when I typed "var a = 42" in my browser Javascript console, and saw no result line. An assignement is "undefined" in Javascript. No result. It's a statement, not an expression. This is just stupid. I really was disappointed. I lose some time because I wonder why there were no result line, this is stunning in a REPL. They told me Javascript is Lisp in C clothing, my ass.

-----

3 points by waterhouse 5407 days ago | link

Given that you already iterate through the list, it can be made cheap to get the last item:

  (def prl args ;returns last arg
    (let u args
      (while cdr.u
        (pr car.u)
        (zap cdr u))
      (pr car.u)))
  arc> (prl 1 2 3)
  1233
Even though 'u points to the list 'args, I can modify 'u to point to another part of the list, and the list itself is not modified. I thus traverse the list only once here.

Also, note that the REPL prints the return value on the same line as whatever other output. I think this is annoying. Common Lisp has a 'fresh-line procedure that prints a newline to an output-stream if and only if at least one character has been printed to that stream and the last character printed was not a newline. It would be nice to use that in the toplevel procedure.

Having written that, I looked at the PLT docs and figured out how to at least tell whether nothing has been written to an output-port since the last time you checked, and I hacked the toplevel function in ac.scm to print a newline when the expression printed something, whether or not that something ended with a newline. I'm not sure whether I like this better:

  arc> "ach"
  "ach"
  arc> (pr "ach")
  ach
  "ach"
  arc> (prn "ach")
  ach
  
  "ach"
Changes in ac.scm (I haven't learned to use diff, so I'll record them like this):

  ;Relevant part of resulting definition of tl2 in ac.scm:
        (if (eqv? expr ':a)
            'done
            (let ((n (next-char-place))
                  (val (arc-eval expr)))
              (if (< n (next-char-place))
                  (newline))
              (write (ac-denil val))

  ;Then add this:
  (define (next-char-place)
    (let-values (((a b n) (port-next-location (current-output-port))))
      n))

-----

1 point by jazzdev 5408 days ago | link

> Jazzdev, where is the 'pr behaviour problematic?

I'm writing an HTML package. I'd like it to work as follows:

  (html
    (body
      (somesetup)
      "Some text"
      (pr "Some more stuff")
  ))
I'm experimenting with the idea that calling pr shouldn't be necessary, so the tag expansion eval's everything and if it gets back something non-nil then it pr's it. I'd like to still allow pr to work also, but since it returns non-nil then some things get output twice.

I want the user of the HTML package to use standard stuff, so having prnil is awkward. Allowing pr inside these tags isn't necessary. I'm just playing around and trying to see what feels natural to use.

Using pr to print something without changing the semantics of a functional program makes sense in theory, but in practice it doesn't see useful. Have you ever actually done it?

-----

1 point by conanite 5407 days ago | link

One way to make this possible is to have your 'html macro (I'm assuming it's a macro) unhygienically provide a lexical binding for 'pr for its body

  (mac html body
    `(do (pr "<html>")
         (let pr prnil ,@body)
         (pr "</html>")))
That way your callers can use 'pr like they're used to, but secretly and subversively you have them calling your own private pr function.

-----

1 point by jazzdev 5407 days ago | link

That's a clever way to do it. Thanks.

-----

1 point by palsecam 5407 days ago | link

> I'm experimenting with the idea that calling pr shouldn't be necessary

You may like that idea: http://arclanguage.org/item?id=11048

-----

1 point by jazzdev 5407 days ago | link

Yep. I'm trying to do the same thing. I need to follow this forum more regularly.

-----

1 point by akkartik 5408 days ago | link

Hmm, doesn't each return the entire list that was being iterated over? I'll look out for an example, but one of the ways I iterate over stuff often ends up stuffing the repl terminal with huge screenfuls of output if I forget my do1 nil..

-----

2 points by palsecam 5408 days ago | link

> Hmm, doesn't each return the entire list that was being iterated over?

No:

  arc> (each x '(1 2 3) (prn x))
  1
  2
  3
  nil
But you're right, the iteration macros are mainly used while testing in the REPL, while it's often a code smell to see 'each or 'for in the final file, in my XP.

So yes maybe the best solution is to keep them returning nil, to not clutter the screen.

But a better one would be a better REPL that collapse the output if it gets too big. This would protect from a whole class of "mistakes". I use a web-based REPL for administrating Arc powered websites that does this (like the one at dabuttonfactory.com:8080, only better). There were a thread on HN lately about a Python shell that does this too (collapsing output), unfortunately I don't remember its name.

"Improve the tools, improve the language", and not "keep the tools crappy, make the language crappier to fit the existing tools".

Current REPLs are very stupid when you think about it, and lots could be made to improve them. I mean, my Arc REPL in a terminal is not even as good as my Unix shell REPL. And I don't consider my Unix shell REPL awesome anyway.

Rah I should find you the link about the Python shell or clean the code (but it's JS anyway :-/) and show you my web REPL. Both do a bunch of trivial things that greatly improves the usability. For instance, they work on an "expression" level, not a "line" one. If you enter a multi-lines fn definition, then hit the up arrow, the whole definition is copied back in the input area, not just the last line. Mine also works with the mouse: I can click on a previous definition to copy it back in the input area, and this is much better for some cases that having to hit <UP> 20 times. Etc, etc.

Frankly, if "dynamic development with a REPL" means fighting with rlwrap, then I can be way more productive with editing a file in Emacs and a compile stage. Compiling is not that slow nowadays. Or have a look at Golang: it can even be blazing fast. Even Slime (for Emacs) is not really that better IMO.

There are even drawbacks with a REPL: you try things in it, but when you're done trying, you have to copy/paste the code to the editor window: you lose some time you would not have wasted with a static language! Big big hacks should be done in this area. Current REPLs are not good enough. That's the reason I know no Python guys that actually use `python' as a REPL. Us Lispers use one, but I guess it's mainly because we are used to it (i.e: Lisps tutorial begin by telling you to start a REPL, Python ones to start a text editor).

The ideas of making them less awkward (i.e: don't be terminal/line-oriented) and/or usable to some extend with a mouse are just little steps (but a giant leap in usability IMO). I hope some hero will come one day and will find a way to reunify the REPL and the editor window or something. Currently it's too much of a pain. How can I say, for instance, "OK, this redef I tried here in the REPL is better than the one in the source file, replace it"?

-----

2 points by shader 5408 days ago | link

Getting better repl tools, and language integration with the repl, are two of the main things I'm interested with in arc.

I've only gotten as far as 'src and 'ppr, but I was hoping to get to the point where arc automatically documented it's current code (i.e. the stuff actually running) and made it visible through both web and repl interfaces. In theory, 'src could be used to solve the repl -> editor -> file problem. If arc kept track of the original source files, and the current 'src associated with each of those functions, then it could possibly display the diff and even update the files if you wanted it to. Heck, why not give it a git interface, so that you can add and commit changes from the repl as well.

The reason I like arc is that the language is very easy to change, and I'm hoping to change it to make it the language that is most "aware" of itself, and its current source code.

Making a better repl is certainly something I'm interested in.

-----

2 points by akkartik 5408 days ago | link

Yeah the python shell was dreampie: http://akkartik.name/blog/2010-02-21-19-21-42-soc

-----

1 point by palsecam 5407 days ago | link

Yes this is it, thank you.

-----

1 point by akkartik 5398 days ago | link

"Hmm, doesn't each return the entire list that was being iterated over?"

"No:"

Ah, I wasn't going crazy after all!

  arc> (each (a b) (obj 1 2 3 4) a)
  #hash((3 . 4) (1 . 2))
So each returns the iteratee when it's a table. Looking at the source it's just outsourcing tables to maptable. I'm going to change it to return nil.

-----

1 point by pmarin 5408 days ago | link

Try 9term. you can edit the text buffer and resend it to arc. You will need to read carefully the man page if you are not familiar with Plan 9 tools.

-----

1 point by gus_massa 5405 days ago | link

I think that pr should return the last value, because to be consistent with other operators for multiple expressions. For example:

  (do 1 2 3) => 3
  (pr 1 2 3) => 1
  ((fn() 1 2 3)) => 3

-----