Arc Forumnew | comments | leaders | submit | evanrmurphy's commentslogin

I finally figured it out. Inside "..." is an flink whose continuation function references x. srv.arc's default threshold for harvest-fnids is very large, so my memory was getting completely consumed before any fnids could be harvested; hence the references to each x were lingering, and mzscheme couldn't consider them garbage.

My workaround is to have foo make a call to harvest-fnids with a calculated threshold much lower than the default, and now memory consumption isn't getting out of hand. Thanks for everyone's help!

-----

1 point by evanrmurphy 5494 days ago | link | parent | on: Try Arc

stderr is now officially shunted! :P

I use a variation of waterhouse's eval-w/stdout [1] that is essentially the original wrapped in an on-err:

  ; don't especially like name

  (def eval-w/stderr/out (expr)
    (tostring:let result nil
      (on-err (fn (c) (pr "Error: " #\" (details c) #\"))
              (fn ()  (let output (tostring (= result (eval expr)))
                        (unless (empty output)
                          (pr output))
                        (write result))))))
Let me know what you think next time you're at http://tryarc.org/.

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

-----

1 point by evanrmurphy 5494 days ago | link | parent | on: Trivial arc webapp

Is this served online somewhere or only accessible via download?

-----

1 point by bhoung 5494 days ago | link

unfortunately it's only accesible via download. maybe down the track.

-----


Thanks, aw. That's a really thorough method I'll plan to try.

Another question is are you using Arc 3.1 or Anarki?

Arc 3.1, and that's a great point about defop's return value.

-----

2 points by evanrmurphy 5507 days ago | link | parent | on: File I/O Challenge

It's not really a challenge, but I submitted this for Arc and tried to make it as concise as possible:

  (w/stdout (outfile "fileio.txt")
    prn!hello
    prn!world)
  (pr:cadr:readfile "fileio.txt")

-----

3 points by rocketnia 5506 days ago | link

The "append" requirement is pretty specific. The file has to be opened for appending, if that's possible. The Python 2 and Groovy examples have complaints along these lines in the comments.

Furthermore, even though I think it was vague in the original description of the problem, the complaints indicate that the "world" line should be read into a variable.

So the first thing I tried was this, riffing off of evanrmurphy's version:

  (w/stdout (outfile "fileio.txt") prn!hello)
  (w/stdout (outfile "fileio.txt" 'append) prn!world)
  (pr:= line (string:cadr:readfile "fileio.txt"))
No luck. The file contains "helloworld" on one line. It doesn't even have a trailing newline. This is thanks to a lack of flushing, which is fixed on Anarki.

Speaking of bugs, do we even close these file handles?

And what if someone's mysteriously edited the file in between our commands, and we end up reading in a million-line file or barfing on a paren mismatch?

Also, most of the responses seem to use "putStrLn", "println", etc. for displaying the line that was read, even though that sacrifices two characters' worth of brevity. :-p

Let's try that again.

  (w/outfile f "fileio.txt" (disp "hello\n" f))
  (w/appendfile f "fileio.txt" (disp "world\n" f))
  (w/infile f "fileio.txt" (repeat 2 (= line readline.f)))
  prn.line
Whoops, I have a stray #\return at the end, 'cause I'm on Windows. To account for that, I can use Anarki and thereby take advantage of aw's 'readline fix and the flushing fix I mentioned earlier. If I use Anarki and take garply's lib/util.arc suggestion, this is what I get:

  (load "lib/util.arc")
  
  ; to be consistent
  (mac w/stdappendfile (name . body)
    (w/uniq gf
      `(w/appendfile ,gf ,name (w/stdout ,gf ,@body))))
  
  (w/stdoutfile "fileio.txt" prn!hello)
  (w/stdappendfile "fileio.txt" prn!world)
  (w/stdinfile "fileio.txt" (repeat 2 (= line (readline))))
  prn.line
That's what I'd settle for in this I/O demonstration, but if it were my own code I'd end up using Lathe, just so I could continue to support official Arc on Windows:

  (= lathe-dir* "my/path/to/lathe/arc/")
  (load:+ lathe-dir* "loadfirst.arc")
  (use-fromwds-as ut (+ lathe-dir* "utils.arc"))
  
  (w/outfile f "fileio.txt" (disp "hello\n" f))
  (w/appendfile f "fileio.txt" (disp "world\n" f))
  (w/infile f "fileio.txt" (repeat 2 (= line ut.readwine.f)))
  prn.line
Unfortunately, this code will break on Jarc 17 and Rainbow, and it's really their fault. :-p Mainly, both of them seem to overwrite the file rather than appending to it, and no workaround for that is coming to mind--well, except for ignoring that part of the problem statement. I'll start a new bug report thread.

For what it's worth, here's how I'd write it in Groovy:

  def file = "fileio.txt" as File
  file.withWriter { it.writeLine "hello" }
  file.withWriterAppend { it.writeLine "world" }
  def line = file.withReader { it.readLine(); it.readLine() }
  println line
My favorite posted answer is this PowerShell one. The comments lead me to believe it's cheating somehow, but the brevity is really impressive. This is pretty much what I'd expect a file I/O DSL to look like.

  sc fileio.txt 'hello'
  ac fileio.txt 'world'
  $line = (gc fileio.txt)[1]
  $line

-----

2 points by waterhouse 5506 days ago | link

I'm afraid I'll have to nitpick.

1. It specifically says "Append the second line "world" to the file." Not just print those two lines, but print the first line and then append the second line to the file.

2. Your code doesn't close the output port. This really isn't a problem in a basic example like this, but if you did it repeatedly...

  arc> (repeat 4000 (w/stdout (outfile "fileio.txt") prn!hello prn!world))
  Error: "open-output-file: cannot open output file: \"/[elided]/fileio.txt\" (Too many open files; errno=24)"
Most of the examples on that page do append and do close their files. So... here's my version, which I think is more correct.

  (w/outfile f "fileio.txt"
    (disp "hello\n" f))
  (w/appendfile f "fileio.txt"
    (disp "world\n" f))
  (pr:cadr:readfile "fileio.txt")
Incidentally, "(w/stdout f prn!hello)" is just a little bit longer than "(disp "hello\n" f)".

-----

1 point by evanrmurphy 5506 days ago | link

1. It specifically says "Append the second line "world" to the file." Not just print those two lines, but print the first line and then append the second line to the file.

True, but then under Clarification, it says: "You don't need to reopen the text file after writing the first line." I'd thought that continuous write would pass for writing and then appending with the same file open, but I guess it doesn't.

The non-closed output port is a very good point though, and that disp makes it shorter is clever.

---

Update: I submitted your version to http://stackoverflow.com/questions/3538156/file-i-o-in-every... with an attribution.

-----

1 point by waterhouse 5506 days ago | link

Cool, I have been attributed! :-) I feel I have to make another embarrassing nitpick, though, which is that we want the second line, rather than the second s-expression (they happen to be the same here, but the task is to demonstrate how to do these things). I would therefore go for one of these as the third piece of code:

  (w/infile f "fileio.txt"
    (pr:cadr:lines:allchars f))

  ; Or, though this doesn't close the input port:
  (pr:cadr:lines:allchars:infile "fileio.txt")

  ; Both of the above, though fun, will read all characters
  ;  in the file, only to return the second line. Better:
  (w/infile f "fileio.txt"
    readline.f
    (prn readline.f))

-----

1 point by evanrmurphy 5506 days ago | link

How about this variation of your latest:

  (w/infile f "fileio.txt"
    (repeat 2 (= l readline.f))
    (prn l))
While a bit longer, it eliminates the duplication of readline.f and addresses the concern about not reading "world" into a variable.

-----

1 point by waterhouse 5506 days ago | link

The original spec says this...

  4. Read the second line "world" into an input string.
  5. Print the input string to the console.
Is an "input string" a variable? I guess it can't be much else... Ok. And I would put "prn.l" instead of "(prn l)"--not that it makes much difference, but I really like using ssyntax. Otherwise, I think I'm satisfied with this code.

And we've all written variations on this here, don't worry about calling it mine. It's a piece of code that we are collectively beating (or artfully crafting) into shape.

-----

3 points by fallintothis 5505 days ago | link

Otherwise, I think I'm satisfied with this code.

Never fear! I'll show up to flog this horse in the nick of time! Y'know, before rigor mortis sets in.

Here's another I/O utility I think would be useful.

  (def readlines (n (o str (stdin)))
    (let line nil
      (repeat n (= line (readline str)))
      line))
It undoubtedly sets a variable (just not a global), though I think the "challenger" phrased the requirement as such because of a C-centric view: allocate a chunk of memory for the string, then read the string into there (which, technically, even a simple (readline) does). Anyway... With all of these, the Arc code would look something like

  (tofile   "fileio.txt" (prn "hello"))
  (ontofile "fileio.txt" (prn "world"))
  (fromfile "fileio.txt" (prn (readlines 2)))
And I don't think I could squeeze more out of that without getting overly specific. Of course, readlines is a conventional name for something that just reads all the lines of a stream, but I think we could reasonably use names closer to Arc's allchars and filechars.

  (def all-lines ((o str (stdin)))
    (drain (readline str)))

  (def filelines (name)
    (fromfile name (all-lines)))
Not sure if the latter should be hyphenated, though.

-----

1 point by garply 5507 days ago | link

If we can count w/stdoutfile from lib/util.arc as part of the Arc language (and I'd argue that we should), we could drop another token:

  (w/stdoutfile "fileio.txt"
      prn!hello
      prn!world)
    (pr:cadr:readfile "fileio.txt")
10 tokens is pretty good - Skimming the list, I think Arc pretty much mops the floor in terms of concision.

-----

4 points by fallintothis 5506 days ago | link

Hm. Anarki calls them w/stdoutfile and w/stdinfile, but wouldn't tofile and fromfile be more consistent with tostring and fromstring (and shorter, besides)? Then there's the whole appending business. Can't really give an optional parameter to tofile like outfile has, and toappendfile runs together horribly. What about just appendfile?

Then, using readline instead of readfile so that it (1) doesn't try to parse things as s-exprs and (2) doesn't read the entire file for no good reason, I'd envision it thus:

  (tofile "fileio.txt" (prn "hello"))
  (appendfile "fileio.txt" (prn "world"))
  (fromfile "fileio.txt" (readline) (pr (readline)))

-----

1 point by waterhouse 5506 days ago | link

Excellent. Have added to my personal Arc library (which is a big fat file named "a", to which I keep appending things):

  (mac tofile (f . body)
    (w/uniq gf
      `(w/outfile ,gf ,f
         (w/stdout ,gf
           ,@body))))
  (mac fromfile (f . body)
    (w/uniq gf
      `(w/infile ,gf ,f
         (w/stdin ,gf
           ,@body))))
I'm not sure about the appendfile--given what infile and outfile do, it sounds like a procedure that creates an output-port that appends to a file. Maybe to-appendfile, appendtofile, appendingfile, tofile/append... Alternatively, we could make keyword arguments happen in Arc, and then you would just throw ":append t" or something inside the call to tofile. That would also allow for further extension with, e.g., an :if-exists argument.

-----

1 point by rocketnia 5506 days ago | link

How about 'tolog? Are files opened for appending for other reasons, in practice? This would also keep with the to-means-output, from-means-input pattern.

-----

2 points by fallintothis 5506 days ago | link

I'd try to err on the side of generality. And I'm not quite as concerned about to:output / from:input, if the names are still "clear enough".

As to waterhouse's suggestions, I had considered those names. I suppose if you read appendfile as a noun instead of a verb-and-noun, it's confusing (though infile and outfile don't really have the same problem, so it's not the train of thought my brain follows). It's hard modifying a name like tofile with a long word like append. We already have two words in tofile, so adding a third without hyphenation is stretching it, and adding hyphens breaks the flow with the other names (fromfile, tostring, etc.). We could go for something shorter, like addtofile, which delineates itself well without hyphens because each word is one syllable. If we can't avoid hyphens, using / instead (e.g., tofile/a or tofile/append) flows better, but isn't that great.

Another name that occurred to me -- and is probably my favorite so far -- is ontofile, which is still simple enough to not need hyphens, communicates intent (appending something onto a file), and worms the word to in there, painting it with the to:output / from:input correlation. Thoughts?

-----

2 points by evanrmurphy 5506 days ago | link

Another name that occurred to me -- and is probably my favorite so far -- is ontofile, which is still simple enough to not need hyphens, communicates intent (appending something onto a file), and worms the word to in there, painting it with the to:output / from:input correlation. Thoughts?

+1! ontofile is a great name, in my opinion, for all the reasons you listed.

I searched for a good portmanteau in the vein of mappend, but I don't think there is one. fappend? Sounds like frappuchino. filepend is decent, but I think I prefer ontofile.

-----

2 points by fallintothis 5506 days ago | link

fappend? Sounds like a frappuchino.

Hahaha! Or worse: http://www.urbandictionary.com/define.php?term=fap :P

filepend is decent, but I think I prefer ontofile.

Agreed on both counts. But that's a clever one; I hadn't thought to try out portmanteaus yet.

-----

1 point by evanrmurphy 5506 days ago | link

Oh wow, didn't know that one. Who knew you could get street smarter hanging out on Arc Forum??

-----

1 point by garply 5506 days ago | link

Regarding logging, I use a log function such that (log "my log message here") appends to a globally identified file log*.

-----


I put ($.collect-garbage) at the top of my defop and tested again. Memory leaks just the same. Somebody in Arcland is forgetting to take the trash out because Scheme has none to collect. :P

Nice macro, by the way. I added it to my utils with an attribution.

-----


Maybe you should try out a more recent version of Racket?

That's a good idea. I'm still using MzScheme 4.2.1. (Although before I go to the trouble, maybe I should skim recent release notes to see if they were supposed to have fixed any memory leak.)

Thanks for doing that test. I just tried again on my case and it got to > 450 MB (90%+ on a 512 machine) without showing signs of GCing! I'll push it even further later to see if it collects; if it does, then maybe I just need to lower a global like threadlimit* if I don't want the entire machine to get consumed.

-----


I haven't tried http.arc and dispatch.arc yet. Were those written by palsecam? Have you had good experiences with them?

-----

2 points by garply 5509 days ago | link

Yes and yes. With pg's code, I kept having to make ugly hacks to things like respond to be able to do what I wanted. It was hard to modify, hard to maintain, and hard to debug. IMO, palsecam's code is much more flexible and much better written. I highly recommend it.

-----

2 points by evanrmurphy 5510 days ago | link | parent | on: Care to share your arc project?

thaddeus developed http://petroenergynews.ca/ in Arc.

Also, you may already know this, but both this forum and http://news.ycombinator.com/ are Arc webapps.

-----

2 points by evanrmurphy 5510 days ago | link | parent | on: More documentation

I can definitely imagine better documentation easing the learning curve for new Arc programmers. Source code can be self-explanatory, but only after you've attained a certain base level of fluency with the language. Just as the axioms have to be defined in Racket [1] before Arc can work, they have to be defined in your head before it can make any sense. So I applaud your effort.

One resource that really helped me learn faster was rntz's help system, available in Anarki [2]. It's very convenient because it gives you access to documentation right at the repl:

  arc> help.acons
  [fn]  (acons x)
   Determines if `x' is a `cons' cell or list.
      Unlike 'alist, this function will return nil if given an empty list
      See also [[atom]] [[alist]] [[dotted]] [[isa]] [[cons]] [[list]] 
  nil
  arc> help.alist
  [fn]  (alist x)
   Return true if argument is a possibly empty list
      Unlike 'acons, this function returns t when given an empty list
      See also [[atom]] [[acons]] [[dotted]] [[isa]] [[cons]] [[list]] 
  nil
  arc> help.afn
  [mac] (afn parms . body)
   Creates a function which calls itself with the name `self'.
      See also [[fn]] [[rfn]] [[aif]] [[awhen]] [[aand]] 
  nil
Had you known about this already?

[1] Or Java (http://github.com/conanite/rainbow, http://jarc.sourceforge.net/), or JavaScript (http://jonathan.tang.name/files/arclite/index.html), or Arc itself (http://arclanguage.org/item?id=11128). ^_^ I think someone did a port to Common Lisp not too long ago as well, but I can't find the link.

[2] http://github.com/nex3/arc/commits/master/help/arc.arc

-----

2 points by fallintothis 5509 days ago | link

Source code can be self-explanatory, but only after you've attained a certain base level of fluency with the language. Just as the axioms have to be defined in Racket before Arc can work, they have to be defined in your head before it can make any sense. So I applaud your effort.

Well put. My thoughts exactly. The suggestion wasn't necessarily to learn Arc by spending a couple days reading the source. I mean, that's what I did, but I was already familiar with Common Lisp. Just that once you reach a certain point (as Preston seems to have), it's not so daunting to say "hey, I wonder what filechars does?" and go read

  (def filechars (name)
    (w/infile s name (allchars s)))

-----

1 point by prestonbriggs 5510 days ago | link

I didn't know of it. Sounds great. Plus, I should be able to write a little program to grovel through his system and gen up a TeX document like mine.

Thanks for the pointer, Preston

-----

More