Arc Forumnew | comments | leaders | submit | fallintothis's commentslogin
2 points by fallintothis 5505 days ago | link | parent | on: A better syntax for optional args

I dunno. It seems I'm in the minority, but I actually like using (o arg default). Only problem is the whole "don't use o when destructuring" thing, which is annoying. As for these options, I agree with conanite. In general, overloading either quotes or dots would be weird.

You could tweak o to be nicer to multiple parameters, though. E.g.,

  (def trim (s (o where 'both) (o test whitec))
    ...)
could instead be

  (def trim (s (o where 'both test whitec))
    ...)
Not that it's much of an issue:

  arc> (def optional (parm) (caris parm 'o))
  #<procedure: optional>
  arc> (ratio (andf alist ~dotted [some optional _]) (vals sig))
  75/494
  arc> (ratio (andf alist ~dotted [>= (count optional _) 2]) (vals sig))
  15/494
  arc> (apply max (trues (andf alist ~dotted [count optional _]) (vals sig)))
  3

-----

2 points by akkartik 5505 days ago | link

If CL's &optional came to this forum it would say: If you're going to have a new keyword like o, at least take advantage of the fact that required args can't follow optional args, and get rid of the parens.

I like how you argue this is a rare case :) My motivation is purely aesthetic, under the assumption that I'll want to make a long term commitment to a 100-year language something like arc. I'm trying to add keyword arguments to arc, and I figure I might as well revisit this decision at the same time.

-----

3 points by conanite 5505 days ago | link

Two problems with (o arg default) - you need to remember not to use 'o at the start of a destructuring list (and not get confused when you see (def handle-request ((i o ip)) ...) ), and as akkartik says it's paren-inefficient, a single keyword to delimit required/optional args would mean fewer tokens.

The first problem is easy to fix though - use a symbol that's less likely to be an arg name to identify optional args. How about '= ?

  (def myfun (a b (= c (something)) ...)
it has the advantage of similarity with ruby:

  def myfun a, b, c=something
disadvantage: looks silly when you don't supply a default value:

  (def myfun (a b (= c) ...)

-----

2 points by fallintothis 5505 days ago | link

get rid of the parens

Thinking about the paired-o suggestion I made, it has an advantage over grouping single optional arguments by their own parens -- at least if you have more than 2 optional parameters :). Compare:

  (a b (opt-c default-c))
  (a b (o opt-c default-c))

  (a b (opt-c default-c) (opt-d default-d))
  (a b (o opt-c default-c opt-d default-d))

  (a b (opt-c default-c) (opt-d default-d) (opt-e default-e))
  (a b (o opt-c default-c opt-d default-d opt-e default-e))
It's like a 3-character &optional, just with one of the characters at the end. However, you'd need to specify nil defaults by hand (though you might let an uneven pair at the end default to nil). E.g.,

  (def markdown (s (o maxurl) (o nolinks))
    ...)
could, at best, become

  (def markdown (s (o maxurl nil nolinks))
    ...)
But paired-o also generalizes the current behavior, so we could still use the original signature.

Just food for thought.

-----

1 point by akkartik 5505 days ago | link

Yeah I'd thought of that and forgotten about it :) Thanks.

-----

1 point by akkartik 5505 days ago | link

BTW, could you tell me how common destructured args are in the arc codebase, just for comparison?

-----

3 points by fallintothis 5504 days ago | link

In def and mac parameter lists, they're pretty much never used.

  arc> (def destructuring (parm) (and (acons parm) (isnt (car parm) 'o)))
  #<procedure: destructuring>
  arc> (ratio [errsafe:some destructuring _] (vals sig))
  1/247
  arc> (keep [errsafe:some destructuring _] (vals sig))
  (((y m d)) ((y m d)))
They're most often used implicitly with let.

  $ grep "(let (" *.arc | grep -v "(let (unquote"
  app.arc:      (let (f url) afterward
  app.arc:                        (let (typ id val sho mod) it
  app.arc:  (let (nums (o label "")) (halve s letter)
  app.arc:         (let (ms ds ys) toks
  arc.arc:      (let (vars prev setter) (setforms place)
  arc.arc:    (let (binds val setter) (setforms place)
  arc.arc:    (let (binds val setter) (setforms place)
  arc.arc:    (let (binds val setter) (setforms place)
  arc.arc:    (let (binds val setter) (setforms place)
  arc.arc:    (let (binds val setter) (setforms place)
  arc.arc:        (let (binds val setter) (setforms place)
  arc.arc:        (let (binds val setter) (setforms place)
  arc.arc:    (let (binds val setter) (setforms place)
  arc.arc:  (let (y m d) (date s)
  arc.arc:  (let (binds val setter) (setforms place)
  html.arc:      (let ((opt val) . rest) options
  news.arc:             (let (t1 t2 t3 . rest) toks
  srv.arc:  (let (i o ip) (socket-accept s)
  srv.arc:                (let (type op args n cooks) (parseheader (rev lines))
  srv.arc:  (let (type op args) (parseurl (car lines))
  srv.arc:  (let (type url) (tokens s)
  srv.arc:    (let (base args) (tokens url #\?)
  srv.arc:      (let (kill keep) (split (rev fnids*) nharvest)

-----


http://github.com/nex3/arc/blob/master/help/arc.arc is from Anarki, a community-maintained branch of "vanilla" Arc. They wrote such a help function, but there are also other extensions and differences that are reflected in said help.

If you're new to Lisp, http://ycombinator.com/arc/tut.txt isn't a bad start. But there are countless Lisp tutorials out there.

As for online documentation: http://files.arcfn.com/doc/ + http://www.arcfn.com/2009/06/whats-new-in-arc3.html; see also http://arclanguage.org/item?id=12228, wherein I technically have another suggestion for learning Arc without the web ;).

-----

3 points by fallintothis 5512 days ago | link | parent | on: Extending + to do int plus list

This isn't exactly like your code, since I wasn't sure what you wanted it to do in the case of three or more arguments, but the important part is the alternative to map.

  (extend + (n xs . rest) (and (number n) (acons xs) (no rest))
    (treewise cons [only.orig _ n] xs))

  arc> (+ 3 '((1 2) (3 4)))
  ((4 5) (6 7))
  arc> (+ 3 '(1 2 3))
  (4 5 6)
  arc> (+ 5 '((((((5)))))))
  ((((((10))))))
  arc> (+ 3 '(1 2 3) '(4 5 6))
  Error: "+: expects type <number> as 2nd argument, given: (1 2 3 . nil); other arguments were: 3 (4 5 6 . nil)"

-----

3 points by rocketnia 5512 days ago | link

After hammering on your approach a little, here's what I get. ^_^ I like to maintain the fact that Arc's '+ is left-associative, so I don't give myself as much leeway with the additional arguments.

  ; We're going to have a meaning for number-plus-list, so we override
  ; the default number behavior, which assumes that only numbers can be
  ; added to numbers.
  (extend + args (let (a b) args (and number.a number.b))
    (let (a b . rest) args
      (apply + (do.orig a b) rest)))
  
  (extend + args (let (a b) args (and cdr.args number.a alist.b))
    (let (n xs . rest) args
      (apply + (map [+ n _] xs) rest)))
Saying (treewise cons [only.+ _ n] xs) is a bit more wordy than necessary here, but it's still a good way to accomplish (+ n xs) inline, without extending '+.

Oh, hey, if 'treewise could tell that it had a cyclic data structure, it would also be a more robust option... but that's not the case yet. Should it be?

-----

1 point by fallintothis 5512 days ago | link

I like to maintain the fact that Arc's '+ is left-associative

Good work!

if 'treewise could tell that it had a cyclic data structure, it would also be a more robust option

Do you mean cyclic structure like this?

  arc> (= xs (list 1))
  (1)
  arc> (do1 nil (= (cdr xs) xs))
  nil
  arc> (xs 1000)
  1
Never thought of that. I mean, the P part of the vanilla Arc REPL breaks on them, and I've never been compelled to use cycles. When are cyclic lists used? I guess if you're representing certain graphs?

In a more general sense, I wear my opinion about treewise on my sleeve: http://arclanguage.org/item?id=12115.

-----


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

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

Personally, I use Vim (efficient text editing), Yakuake (shell is an F12 away), and rlwrap (make bad REPLs good!) for, well, really any language I use. Specifically for Arc, I wrote a Vim syntax highlighter & ftplugin: http://arclanguage.org/item?id=10147. Also, it's useful to

  alias arc='pushd ~/arc &> /dev/null; rlwrap -q"\"" mzscheme -f as.scm; popd &> /dev/null'

-----

3 points by akkartik 5517 days ago | link

Here's what my as.scm has to remove the need for the pushd/popd:

  (define arc-dir* (getenv "ARC"))
  (define start-dir* (path->string (current-directory)))
  (current-directory arc-dir*)

  (require mzscheme)

  ... ; other requires from as.scm

  (current-directory start-dir*)

  (tl)
Now all I have to do is setup $ARC in my shell startup, and I can run arc from anywhere and load source files transparently from the current directory.

-----

1 point by akkartik 5515 days ago | link

I went to push this idea to anarki, but it turns out there's already magic to call arc from anywhere:

  (parameterize ((current-directory (current-load-relative-directory)))
    (aload "arc.arc")
    (aload "libs.arc"))

-----

1 point by akkartik 5517 days ago | link

Is this the right Yakuake? http://yakuake.uv.ro/?page_id=6 Can you elaborate on what it buys you?

-----

1 point by fallintothis 5517 days ago | link

Yeah, that's the right one. More "officially", http://yakuake.kde.org/ leads you through some links until you finally get to its KDE-Apps page at http://kde-apps.org/content/show.php?content=29153. But you'd probably just get it from your distro's package repository, anyway. For instance, I'm on Debian right now, and the package description does the elaboration for me, so:

  $ apt-cache show yakuake | grep -A7 -m1 "Description:"
  Description: a Quake-style terminal emulator based on KDE Konsole technology
   YaKuake is inspired from the terminal in the Quake game: when you press a key
   (by default F12, but that can be changed) a terminal window slides down from
   the top of the screen. Press the key again, and the terminal slides back.
   .
   It is faster than a keyboard shortcut because it is already loaded into memory
   and as such is very useful to anyone who frequently finds themselves switching
   in and out of terminal sessions.

-----

2 points by fallintothis 5519 days ago | link | parent | on: How do you use arc in real world?

is duck typing the name? I can't remember

Hehehe. Well, it has to be something with an animal! :) I think you mean camelCase.

</useless contribution>

-----


For one, you have an extra set of parentheses around the body of the function. Parenthetical expressions like

  (expr1 expr2 expr3 ...)
by default will evaluate expr1 and try to call it with the arguments expr2, expr3, .... E.g.,

  arc> (+ 1 2 3)
  6
This sort of rule applies recursively, so

  ((if (> 1 2) - +) 1 2 3)
first evaluates

  (if (> 1 2) - +)
which returns the function + (since (> 1 2) is false), meaning the original evaluates to

  (+ 1 2 3)
There are a few places where parenthetical expressions don't evaluate this way, but they're usually pretty obvious -- like the argument list in a def. For more details, the tutorial is a decent place to start learning about Arc & Lisp programming: http://ycombinator.com/arc/tut.txt.

To fix your code immediately, you'd just need to get rid of the extra parens. Expressions in the body of a function evaluate one after the other, returning the value of the last expression, so given

  (def f (x)
    (+ x 1)
    (+ x 2))
a call to f will calculate (+ x 1) and (+ x 2), returning the latter.

But you should (generally) only use = for global variables. For local ones, you'll want with or let.

  arc> (let x 1 (+ x 1))
  2
  arc> (with (x 1 y 2) (+ x y))
  3
So, the function

  (def s (x y)
    (with (mx (avg x) my (avg y))
      (reduce + (map *
                     (map [- _ mx] x)
                     (map [- _ my] y)))))
should do what you want. You could still perform some "map fusion". Roughly speaking,

  (map f (map g xs))
is equivalent to

  (map [f (g _)] xs)
and you save on doing multiple passes through your lists. Thus,

  (def s (xs ys)
    (with (mx (avg xs) my (avg ys))
      (reduce + (map (fn (x y) (* (- x mx) (- y my)))
                     xs
                     ys))))
is arguably cleaner, though the two-argument function does clutter stuff up a bit.

You can also write the same "destructive" style of code in Arc, though it's normally frowned upon.

  (def s (x y)
    (let xy 0
      (for i 0 (- (min (len x) (len y)) 1)
        (++ xy (* (- (x i) (avg x))
                  (- (y i) (avg y)))))
      xy))
Finally, to format code,

  put a blank line, then at least two spaces before the line of code
  (similarly for subsequent lines)
See http://arclanguage.org/formatdoc for details.

-----

2 points by alimoeeny 5521 days ago | link

Thanks a lot, that was great help, I never felt so excited about coding in a new language since Commodore 64's BASIC!

-----

3 points by fallintothis 5529 days ago | link | parent | on: Ask: Macros, names, and symbols

1. Not exactly, but it helps to think of it that way.

  (mac foo (x)
    (list 'bar x))

  (foo abcdef)
is conceptually like

  (eval ((fn (x) (list 'bar x)) 'abcdef)) ; = (eval (list 'bar 'abcdef))
Notice how the argument 'abcdef was quoted. Macros don't evaluate their arguments, but the code they generate might (e.g., if bar was a function, it'd try to evaluate abcdef as a variable).

They aren't actually implemented that way. eval operates at run-time and doesn't have access to lexical variables. Macros expand at compile-time, so it's as if you had the expansion there to begin with. E.g.,

  arc> (let y 10
         (eval 'y))
  Error: "reference to undefined identifier: _y"
but

  arc> (mac foo (arg) arg)
  #3(tagged mac #<procedure: foo>)
  arc> (let y 10
         (foo y))
  10
because

  (let y 10
    (foo y))
macroexpands into

  (let y 10
    y)
before it ever runs.

Anonymous macros are plausible, but they might force work to be done at run-time -- essentially, you're right that eval's the closest thing to it. But since macros happen at compile-time, you can't do something like

  ((if (mem 'x stuff) push pull) 'y stuff)
The compiler sees the macros push and pull, but they're not applied to anything, so it doesn't expand them. Then at run-time, you get an error as it tries to evaluate each form. You have a macro trying to act like a function (i.e., at run-time).

This topic comes up every so often: http://arclanguage.org/item?id=11517.

2. do is for when you want to execute a series of expressions, but do it in just one s-expression (i.e., the thing wrapped in do). You see it a lot in macros; e.g., defs in arc.arc:

  (mac defs args
    `(do ,@(map [cons 'def _] (tuples args 3))))
It converts

   (defs f (x) (+ x 1)
         g (y) (- y 1))
into

   (do (def f (x) (+ x 1))
       (def g (y) (- y 1)))
which is just one list. You couldn't return multiple values, so the macro couldn't expand into

  (def f (x) (+ x 1))
  (def g (y) (- y 1))
directly.

Another place you see do a lot is in if statements. Each clause is one expression, but if you want to do multiple things in one expression, you need the do. E.g.,

  (if test
      (do (pr #\t) (pr #\h) (pr #\e) (pr #\n))
      else)
will print "then" if test is true, otherwise it'll do else. This wouldn't work without the do:

  (if test       ; if "test"
       (pr #\t)  ; print #\t
      (pr #\h)   ; else if (pr #\h)
       (pr #\e)  ; then (pr #\e)
      (pr #\n)   ; else if (pr #\n)
       else)     ; then "else"
P.S. That's the return value of the statement.

  arc> (pr "a") ; prints "a" WITHOUT a newline, then returns the first thing it
                ; printed (the string "a")
  a"a"
  arc> (prn "a") ; print "a" WITH a newline, then returns the first thing it
                 ; printed (the string "a")
  a
  "a"
To learn more about macros, my debugger tool might be helpful: http://arclanguage.org/item?id=11806. Let me know if it is!

-----

3 points by fallintothis 5531 days ago | link | parent | on: ~ applied to variables

It'd be easy, since ac.scm just changes ~foo into (complement foo) (except (~foo x) is changed to (no (foo x)), but that doesn't affect us here). Just change complement.

  (mac complement (f)
    (w/uniq (gf args)
      `(let ,gf ,f
         (if (isa ,gf 'fn)
             (fn ,args (no (apply ,gf ,args)))
             (no ,gf)))))

  arc> (= foo "bar")
  "bar"
  arc> ~foo
  nil
  arc> (keep ~even '(1 2 3))
  (1 3)
  arc> ~nil
  t
  arc> ~
  #<procedure: no>
But I don't think I've ever made that slip. Function complementation isn't the same as Boolean-not, so overloading the visually distinctive ~foo based on context rubs me the wrong way. I'd probably get used to it, though.

Edit:

Another thought occurs. It could produce some weird bugs. E.g., you might mistakenly pass a variable to a keep that complements, which will work, but keeps t or nil instead of using a function.

  arc> (= foo "bar")
  "bar"
  arc> (keep ~ssyntax '(a.b a:b nil a b c))
  (nil a b c)
  arc> (keep ~foo '(a.b a:b nil a b c))
  (nil)
  arc> (keep ~nil '(a.b a:b nil a b c))
  nil
It doesn't seem so bad, but when you have it buried in some definition like

  (def foo (bar baz)
    (...  (keep ~bar baz) ...))
it might be hard to find.

-----

2 points by fallintothis 5532 days ago | link | parent | on: (map [string ".*" _] "test")

FYI: http://arclanguage.org/formatdoc

(Welcome to the forum!)

-----

1 point by ainar-g 5532 days ago | link

Thanks!

Also, I wonder, if there is a way to replace pairs of characters? I mean, if we need a code, that would replace 'number two number' with 'one two three,' should we use (multi)subst for it or is it done the other way (regexps etc)?

-----

5 points by fallintothis 5542 days ago | link | parent | on: Help please

The install guide (http://arclanguage.org/install) is out of date. For one, the latest Arc (which works with the latest versions of mzscheme) is 3.1, which you can get from http://ycombinator.com/arc/arc3.1.tar. Secondly, mzscheme -m used to mute the "Welcome to MzScheme" banner, but now:

  $ mzscheme -h | grep "\-m"
    -m, --main : Call `main' with command-line arguments, print results
So, just try

  $ mzscheme -f as.scm
or, if you're using the latest (which was renamed Racket: http://racket-lang.org/new-name.html),

  $ racket -f as.scm
You might also notice that in the newer versions, ^C at the Arc prompt doesn't put you back into the Scheme REPL like it says in the install guide. If you want that option, use

  $ racket -i -f as.scm
But that will display an extra "Welcome to Racket" sort of message. See http://arclanguage.org/item?id=10488.

Also, a couple suggestions:

(1) In the future, your post titles should be more descriptive; using more exclamation marks doesn't get you more help. :P E.g., you could've titled this post something like "Weird error. main: not defined or required into the top-level environment".

(2) To format code, put a blank line

  and then but two or more spaces before the line of code
  (and also for subsequent lines of code).
If it doesn't work right the first time you post, you're allowed to edit for a certain amount of time. A quick formatting reference: http://arclanguage.org/formatdoc.

Good luck and welcome to the forum!

-----

1 point by dipanjanpal 5541 days ago | link

Ya. Thanks very much.

-----

More