Arc Forumnew | comments | leaders | submit | evanrmurphy's commentslogin
1 point by evanrmurphy 5141 days ago | link | parent | on: Arc vs. Clojure on Google AdWords

Thanks for the feedback. The caps style is actually my present split test. I have caps and non-caps variations running for each of my 6 ads right now and will see in a few weeks how their performance compares.

I think a tailored landing page is a great idea. I worry about it not being clear enough how to get started for the people who click through.

-----

2 points by zck 5141 days ago | link

Fair enough on the caps issue. I can't argue with "if it works better...", even if I don't like it personally.

Who do you want to target with this campaign? People learning programming for the first time, people learning a Lisp for the first time, or Lispers new to Clojure (and Arc)? You'll require very different instruction pages for each group. I'd be happy to help you write something, if you're interested. I'm sure many of us here would be.

-----

2 points by evanrmurphy 5139 days ago | link

I'd love to get some collaboration from the community here.

I agree 100% about different group requiring different landing pages. Both of the ones you mentioned are good targets, I think. I'll do more keyword research to help dissect the audiences.

The beginning programmers' page is easiest, because pg's tutorial already targets them:

"This is a brief tutorial on Arc. It's intended for readers with little programming experience and no Lisp experience. It is thus also an introduction to Lisp."

All we have to do is put the tutorial in an iFrame next to the REPL, as thaddeus suggested. [1] I can take care of this one. (Eventually it would be cool to have an interactive tutorial though, as you suggested.)

Can I get your input on a page that targets a Lisper who's new to Arc? They can be coming from the Lisp dialect of your choice.

And what other audiences would you consider? Maybe people new to Lisp but coming from other programming experience, such as Ruby or JavaScript?

--

[1] http://tryarc.uservoice.com/forums/80605-general/suggestions...

-----

3 points by thaddeus 5139 days ago | link

I noticed within your feedback forum there were suggestions for REPL window size changes (for both bigger and smaller) and I got to thinking that when I wrote PetroEnergyNews (http://petroenergynews.ca/map ... which has a similar bounding box look to it) I did a bunch of experimenting with different systems, different monitors and different screen resolutions before coming up with the following optimal combinations:

  screen-x: "Narrowest" 700 
            "Narrower" 800 
            "Narrow" 900 *default*
            "Wide" 1200  
            "Wider" 1400 
            "Widest" 1600
 
  screen-y: "Shortest" 400 
            "Shorter" 500 
            "Short" 600 *default*
            "Tall" 750 
            "Taller" 900 
            "Tallest" 1200 
where the defaults were also the best suited to the iPad.

If you would like you can create an account on PetroEnergyNews then go to the user preferences to select the various combinations to get a feel for these sizes.

The "Widest" by "Tallest" setting is perfect for my 27 inch iMac screen :)

Anyways hopefully this info is helpful/useful.

[edit:

1. oops, that's only the map inset window, so it doesn't include the top and bottom bars, but still they should be easy to guesstimate at about 24px each.

2. I guess the width really doesn't matter when you can just set to 100%, but it may help with the height settings?]

-----

1 point by evanrmurphy 5139 days ago | link

Noted, thanks!

-----

2 points by evanrmurphy 5174 days ago | link | parent | on: Arc2js, an Arc to JavaScript compiler

Don't forget rocketnia's awesome in-browser REPL:

http://rocketnia.kodingen.com/af/try-lava-script/

It shows you the compiled JavaScript for your input expression as well as the evaluated result. For example:

  ; Defining a function
  > (= add1 (fn(x­) (+ 1 x)))        
  add1=(function(x){return 1+x;})   
  => function (x){return 1+x;}      

  ; Calling the function
  > (add1 2)
  add1(2)
  => 3

-----

1 point by Pauan 5174 days ago | link

Ah, nice.

-----


Update: http://searchyc.com/ and http://af.searchyc.com/ are both back online for the moment.

-----

3 points by typhon 5182 days ago | link

They're down again.

-----


Contents pasted below for convenience with clickable links. Anybody else miss the Arc Forum search that SearchYC provided?

--

It's a bittersweet situation as we rejoice pg adding a search bar to Hacker News [1] while mourning the sudden loss of SearchYC [2].

One service SearchYC provided that remains a need unfulfilled even with the new feature is search for Arc Forum, pg’s other news.arc forum [3]. This had been an extremely valuable resource to the Arc language community as SearchYC had been to the Hacker News community. It kept the old gem posts and comments which tend to get buried in the forum accessible to new readers.

How can we address the problem of lost search to Arc Forum and the other Hacker News sister forums in the wake of SearchYC’s shutdown?

[1] http://news.ycombinator.com/item?id=2619736

[2] http://news.ycombinator.com/item?id=2605959

[3] http://arclanguage.org/forum

-----

2 points by rocketnia 5185 days ago | link

I used http://af.searchyc.com/ a lot, but for the past month or two I was noticing new posts weren't showing up in the search results, so I kinda expected something to happen to it pretty soon anyway.

I'm having a lot of "shoulda mentioned that earlier" moments recently. O-o;

-----

2 points by akkartik 5185 days ago | link

I use it some, but perhaps not as much as you. Perhaps thriftDB will do one for us as well if we ask nicely?

-----

1 point by evanrmurphy 5185 days ago | link

andres said he could help do it if there's an existing crawler for Arc Forum. http://news.ycombinator.com/item?id=2620661 Has anybody written an AF crawler they'd like to volunteer for getting search back?

-----

2 points by akkartik 5185 days ago | link

I'm happy to contribute the hackerstream crawler. I tried crawling the arc forum a few months ago and it worked just fine.

If hnsearch is crawling HN can't they just reuse the crawler? Or is it past data they're looking for?

-----

1 point by evanrmurphy 5251 days ago | link | parent | on: Problems with Lisp

"Is a cons based list system actually bad? Are there better things that we could be doing?"

They are interesting questions. I think that to be a lisp, a language must homoiconic and applicative. A cons-based system is non-essential, and I am coming to agree with the OP that it's not the best way.

Update: Hmm... my mind is playing tricks on me now. What would you use besides conses to represent s-expressions? Maybe that is a good role for cons.

I wonder if there's a way to merge quote and lambda into a single operator. This is done in concatenative languages like Joy and Factor, where a "quotation" acts both as quoted code and as an anonymous function. But I'm struggling to see how you could translate that into applicative terms.

-----

2 points by rocketnia 5251 days ago | link

"What would you use besides conses to represent s-expressions?"

What are s-expressions? I thought their main aspect was the way they used lists to represent user-extensible syntax forms (macro forms, function applications, fexpr applications). I'm not a fan, but even if we assume s-expressions, they could always use some other implementation of lists.

--

"I wonder if there's a way to merge quote and lambda into a single operator."

Do you mean like in PicoLisp, where all user-defined functions are lists containing their code, and applying a list causes it to be interpreted as a function?

I don't like it, 'cause you don't get any lexical scoping this way. It probably makes more sense in Factor and Joy thanks to the lack of local variables for run-time-created functions to capture.

-----

1 point by Pauan 5251 days ago | link

"What would you use besides conses to represent s-expressions? Maybe that is a good role for cons."

Arrays, objects, pretty much anything that can represent nested sequences. In fact, in PyArc, conses are a class, because Python has an infatuation with classes.

I don't see conses as an "ultimate abstraction". They're a low-level primitive that can be used to create lists, among other things. To put it bluntly, they're a simple way to implement linked lists. The only difference between an array and a linked list is performance, but they have the same semantics.

Most popular languages choose arrays as their default sequence type, but Lisp chose linked lists. I could represent conses as Python lists (which are like arrays), and then define car so it returns list[0] and cdr so it returns list[1:].

As far as Arc programs would be concerned, everything would look exactly the same. The only difference is that certain operations (like prepending) would be slower with arrays than with a linked list. Specifically, (cons 'a some-list) would be O(n) rather than O(1).

So... to say that conses are bad is like saying that arrays are bad. They're both sequence types, that have differing performance characteristics. Use whichever one happens to suit your language/program the best.

-----

1 point by evanrmurphy 5251 days ago | link | parent | on: Problems with Lisp

Indeed. One might say the real problem with Lisp is that it's not concatenative. ;)

I think the OP's point about cons cells is excellent. I've spent a long time thinking cons cells were some ultimate abstraction only to realize they're more of a pretty hack.

-----

2 points by rocketnia 5251 days ago | link

I don't think cons cells are a hack, but I do think it's a hack to use them for things other than sequences. Since we almost always want len(x)=len(cdr(x))+1 for cons cells, rather than len(x)=2, they aren't really useful as their own two-element collection type.

-----

1 point by Pauan 5251 days ago | link

Yeah, I'm tempted to agree with you. In the arc.arc source code, pg even mentions a solution to improper lists: allow any symbol to terminate a list, rather than just nil.

Of course, an easier fix would be to change `cons` so it throws an error if the second argument isn't a cons or nil. Honestly, are improper lists useful often enough to warrant overloading the meaning of cons? We could have a separate data-type for B-trees.

That's one area that I can actually agree with the article, but that has nothing to do with conses in general (when used to create proper lists), only with improper lists. And contrary to what he says, it's not an "unfixable problem", instead it would probably take only 2 lines of Python code to fix it.

One thing though... function argument lists:

  (fn (a b . c))
Of course I can special-case this in PyArc, so . has special meaning only in the argument list. This is, in fact, what I do right now. But that may make it harder for implementations in say... Common Lisp or Scheme (assuming you're piggybacking, rather than writing a full-on parser/reader/interpreter).

If so... then you may end up with the odd situation that it's possible to create improper lists, using the (a . b) syntax, but not possible to create improper lists using `cons`

---

By the way... how about this: proper lists would have a type of 'list and improper lists would have a type of 'cons. Yes, it would break backwards compatibility, but it might be a good idea in the long run. Or we could have lists have a type of 'cons and improper lists have a type of 'pair.

-----

2 points by rocketnia 5250 days ago | link

I don't know if improper lists are really a problem, just hackish. :) My "solution" would be to remove the need for them by changing the rest parameter syntax (both in parameter lists and in destructuring patterns).

---

"how about this: proper lists would have a type of 'list and improper lists would have a type of 'cons."

I don't think I like the idea of something's type changing when it's modified. But then that's mostly because I don't think the 'type function is useful on a high level; it generally seems more flexible to do (if afoo.x ...) rather than (case type.x foo ..), 'cause that way something can have more than one "type." Because of this approach, I end up using the 'type type just to identify the kind of concrete implementation a value has, and the way I think about it, the concrete implementation is whatever invariants are preserved under mutation.

That's just my take on it. Your experience with 'type may vary. ^_^

-----

1 point by evanrmurphy 5255 days ago | link | parent | on: Example pastebin, a first sketch

From the OP:

"we see the actual example of Arc running in the actual environement, not some different or pretend environment that might be the same as the cell phone or might not."

And from https://eval.to/, after clicking the "start new repl" button:

"Note the connector allows this website to execute arbitrary code on the computer running the connector. If you'd rather not do this on your own personal computer, you may wish to use a virtual machine or an Amazon EC2 instance, etc."

Presuming that most people concerned about security would opt to run the connector through a virtual machine or something other than their personal computer, wouldn't we be effectively running the Arc examples through just the kind of different or pretend environments that we're trying to avoid?

I probably just have some misunderstanding about the project's intent or how the pastebin is going to work. Looking forward to your response. :)

-----

1 point by aw 5255 days ago | link

Actually I imagine that many people won't have a problem with running the connector, in much the same way that many people don't have a problem with downloading and running other kinds of software from the Internet on their personal computer.

And... naturally there will be other people who don't want to run the connector, and so the site will be less useful to them.

Since this is a new and different kind of system, I wanted to be clear about what's going on -- so no one is surprised, and can make their own informed decision about if and where they want to run the connector.

I also plan to eventually have an API so that, among other things, people can perform their own runs and post the results to the site.

-----

2 points by evanrmurphy 5261 days ago | link | parent | on: Arc3.1 optimizations

"The problem is that it's trying to fulfill two different purposes at the same time (addition and concatenation), so there's inconsistencies with regard to numbers."

If your numbers were Church encoded, then I guess there wouldn't be a difference between addition and concatenation. XD (This is given that concatenating functions is the same as composing them.)

-----

1 point by Pauan 5261 days ago | link

Yeah, sure, and let's just use lambdas for everything:

  (def cons (x y) (fn (f) (f x y)))
  (def car (x) (x (fn (a b) a)))
  (def cdr (x) (x (fn (a b) b)))

  (car (cons 'a nil)) -> a
:P

On a semi-related note, I had an interest a while back for implementing a Lisp interpreter using only lambda-calculus. I'm still not sure if it's possible or not, but I wanted to give it a try.

-----


"You cannot be concerned about ephemeral consing any more. It's a real design misfeature to prioritize that."

I'd like to hear this elaborated upon.

-----


Oh wow. Did we never consider this possibility in either thread dedicated to the topic? +

  ; arc3.1                     ; arc4.0 ?
  
  (def foo (a b (o c) (o d))   (def foo (a b . (c d))
    ...)                         ...)
Beautiful! And you could even get a rest parameter in addition to the optionals by making it an improper list:

  > (def bar (a b . (c . args))
      (list a b c args))
  #<procedure:bar>
  > (bar 1 2)
  (1 2 nil nil)
  > (bar 1 2 3)
  (1 2 3 nil)
  > (bar 1 2 3 4)
  (1 2 3 (4))
  > (bar 1 2 3 4 5)
  (1 2 3 (4 5))
It seems too good to be true. What's the problem with this?

---

+ http://arclanguage.org/item?id=12565, http://arclanguage.org/item?id=13029

-----

2 points by waterhouse 5268 days ago | link

All Lisp readers I know of treat '(a . (b c)) the same as '(a b c).

  arc> (read "(a . (b c))")
  (a b c)
The reason is, '(a . something) = (cons 'a 'something), and if "something" is a list like '(b c d), then '(a . (b c d)) = (cons 'a '(b c d)) = '(a b c d). You can even write code like this.

  * (+ 1 . (2 3)) ;SBCL
  6
This has the advantage of making it really easy to write a basic print function:

  (def print (x)
    (if (acons x)
        (do (disp "(")
            (print (car x))
            (disp " . ")
            (print (cdr x))
            (disp ")"))
        <handle remaining cases: x is a symbol, number, string...>))
I'm not too strongly opposed to this change, but it would break compatibility with probably all existing Lisp readers, including Racket's.

-----

2 points by rocketnia 5268 days ago | link

This is a bit off-topic, but technically, Racket's syntax reader treats (a b c) and (a . (b c)) differently. It wraps just about every node of the parse tree in a syntax object, but the exceptions are the nodes which, from a certain point of view, aren't even part of the parse tree: The list tails which aren't written using their own parentheses. In particular, the (b c) part of (a b c) isn't wrapped, but the (b c) part of (a . (b c)) is.

  Welcome to Racket v5.1.
  > (define abc (read-syntax #f (open-input-string "(a b c)")))
  > (define a.bc (read-syntax #f (open-input-string "(a . (b c))")))
  > (define abc.null (read-syntax #f (open-input-string
                                       "(a b c . ())")))
  > abc
  #<syntax::1 (a b c)>
  > a.bc
  #<syntax::1 (a b c)>
  > abc.null
  #<syntax::1 (a b c)>
  > (syntax-e abc)
  '(#<syntax::2 a> #<syntax::4 b> #<syntax::6 c>)
  > (syntax-e a.bc)
  '(#<syntax::2 a> . #<syntax::6 (b c)>)
  > (syntax-e abc.null)
  '(#<syntax::2 a> #<syntax::4 b> #<syntax::6 c> . #<syntax::10 ()>)

-----

2 points by akkartik 5268 days ago | link

Yeah I would be opposed to using . this way. But we could use a different token.

-----

2 points by Pauan 5268 days ago | link

But, let's suppose we used ? for the token:

  (def foo (a b ? (c d . e)))
How is this better than:

  (def foo (a b ? c d . e))
?

True, the first version is more explicit, and the parens make it clearly distinct from the required arguments, but then again, it does add an extra level of (arguably useless) parens. I still think it looks nicer than Arc's current version, though:

  (def foo (a b (o c) (o d) . e))

-----

1 point by evanrmurphy 5268 days ago | link

If you're going to create a special syntax for optional arguments, then I agree with you. But why uphold the required arg / optional arg distinction at all? If you make function arguments always optional (as they are in JavaScript) then you need no special syntax, and it is more concise.

What do you think of this model?

-----

3 points by Pauan 5268 days ago | link

As somebody who has programmed for years in JavaScript, I am certainly used to that style, but I actually like required arguments, even if simply for error checking. If I call a function, and it spits back an error, "function expected 3 arguments (2 given)" then I have a pretty good idea of what to do. When the function call fails silently, however, it can lead to some icky debugging situations.

On the other hand, even if you made all arguments default to nil, you could still have some error checking:

  (def foo (a b c))
  
  (foo)          ; (a b c) are (nil nil nil)
  (foo 1 2)      ; (a b c) are (1 2 nil)
  (foo 1 2 3)    ; (a b c) are (1 2 3)
  (foo 1 2 3 4)  ; error: expected 0 to 3 arguments (4 given)
  
In other words, it would still be possible to throw an error when calling with too many arguments, but not when calling with too few.

I'm pretty okay with that tradeoff, actually. It gives up a bit of safety and control, but in exchange it avoids the whole icky mess with "what syntax do we use for optional arguments?" I might try that approach with my interpreter.

In fact, we could reverse the question. Rather than asking, "how do we define optional arguments?" we can instead ask "how do we define required arguments?"

In other words, all arguments default to nil, but it's possible to specify certain arguments as required, or to change what the default is. That may be a better approach than specifying what arguments are optional.

There is one problem with that approach, though. You'll likely want a way to specify what the default for an argument is, which would require some sort of construct. But then if you want to specify a required parameter, you'll need a second (different) construct.

If you have required arguments by default, then you can use a single construct to specify both optional arguments and what their defaults are. But if you have optional arguments by default, you need two constructs.

Unless you're suggesting to not have required arguments at all, and require the programmer to manually write an (if (no a)) check and manually throw an error? That could work, assuming most people don't need required arguments most of the time.

You could even wrap it up in a macro or something, like this:

  (def foo (a b c)
    (require a))
Which could be extended for type information as well:

  (def foo (a b c)
    (require a 'sym))
Which would specify that the first argument is required, and must be a symbol. I kinda like that approach, though it does mean running checks at runtime. On the other hand, how frequently do you actually need those checks? Not very often, right? So that could work, I think.

-----

3 points by Pauan 5268 days ago | link

By the way, here's a quick mock-up for the `require` macro. This assumes that all arguments are optional:

  (mac require (n t)
    `(if (no ,n)         (err (string "parameter " ',n " is required"))
         (no:isa ,n ,t)  (err (string "parameter " ',n " must be of type " ,t))))
When called with (require a) it will throw an error if `a` is nil.

When called with (require a 'cons) it will throw an error if `a` is nil, or if `a` is not of type 'cons.

-----

1 point by shader 5268 days ago | link

Here's a more functional version of require that works in the current version of arc:

  (mac require (n (o typ))
    `(if (and ,typ ,n (no:isa ,n ,typ))
           (err:string "parameter "
                       ',n
                       " must be of type "
                       ,typ)
         (no ,n)
           (err:string "parameter " ',n " is required")))

-----

1 point by shader 5268 days ago | link

Interesting, there is a bug (feature?) in arc that lets you shadow t in a function, even though you normally can't rebind it. Should this be fixed?

-----

1 point by Pauan 5267 days ago | link

Why? `t` is a nice and short variable name, and I would expect it to work fine in function arguments. Hm...

  ((fn (t) t) 10)         -> 10
  ((fn (quote) quote) 10) -> 10
  ((fn (nil) nil) 10)     -> nil
Seems nil doesn't like being shadowed. Works fine in my interpreter, though: they all return 10.

-----

1 point by rocketnia 5267 days ago | link

"Seems nil doesn't like being shadowed."

That's because ((fn (nil) nil) 10) is supposed to be the same as ((fn (()) nil) 10), which destructures the first 0 elements from 10, treating it as a degenerate cons list.

I actually use this behavior in practice, 'cause it's useful to have at least some parameter syntax that doesn't bind any variables, rather than just using a conventional name like "ignored" or "_". Most of the time I use it in a much-too-big 'withs block, where I abuse one of the bindings for sanity-checking or other side effects. ^^ I wouldn't worry about it too much, though.

On the other hand, nil's behavior makes it more verbose to determine whether something's usable as a local variable; you have to check (and x (isa x 'sym) (~ssyntax x)). In Lathe, I define 'anormalsym to do just that. (It might be more accurate to change it to (and (isa x 'sym) (~in x nil t 'o) (~ssyntax x)).)

Speaking of (~ssyntax x), ssyntax is usable for local variable names, but naturally, you can only get the values from the Racket side:

  arc> (let a.b 4 a.b)
  Error: "reference to undefined identifier: _a"
  arc> (let a.b 4 ($ a.b))
  4
That could be a bug.

Personally, I'd like for ssyntax-burdened names in parameter lists to use user-defined parameter syntaxes, like custom forms of destructuring and whatnot. And then, just for axiomatic simplicity's sake, I'd like things like 'o to be implemented using the same system, using ssyntax-burdened names like ".o".

-----

1 point by Pauan 5267 days ago | link

"That's because ((fn (nil) nil) 10) is supposed to be the same as ((fn (()) nil) 10)"

nil is such a weird value... it's false, a symbol, and an empty list all at once. I actually had to do some hacky stuff to get () and '() and nil to behave properly.

"Speaking of (~ssyntax x), ssyntax is usable for local variable names, but naturally, you can only get the values from the Racket side:"

I plan to implement ssyntax at the reader level in my interpreter, so that shouldn't be an issue.

-----

1 point by evanrmurphy 5267 days ago | link

"nil is such a weird value... it's false, a symbol, and an empty list all at once."

Perhaps nil should serve as the base case for every type, rather than just some of them. In Arc, it already does this for booleans, lists and strings (""), but it could be extended to support tables (#hash()), numbers (0), symbols (||) etc. Then it would have more consistency as a concept.

-----

2 points by shader 5267 days ago | link

I've run into issues with nil the symbol vs nil the value. It's caused some issues when I wanted to use arc's sym type to represent variable names in a class compiler project I'm working on. If the variable's name is nil, all kinds of things stop working, and I've had to resort to calling some scheme functions directly to get proper handling of symbols.

I wish that 'nil and nil could be kept somewhat separate, with the first being just a symbol, and the second being equivalent to the base value for all data structures. Otherwise the symbol type is broken and inconsistent, since you cannot accurately (or at least completely) represent code as data.

-----

2 points by Pauan 5267 days ago | link

My interpreter treats 'nil and nil as separate. Not sure if that's a good idea, but I'd rather wait and see if it causes problems before changing it:

  (is 'nil nil) -> nil
This does raise one interesting question, though... obviously 'nil is a sym, but if (is nil ()) is t, then shouldn't (type nil) return 'cons?

-----

2 points by waterhouse 5265 days ago | link

() is not a cons cell, and while (car ()) is well-defined (to be nil), you can't set the car or cdr of (). It is definitely not a cons.

On the other hand, (listp nil) should be true. In fact, its precise definition should probably be (and is, or is equivalent to) this:

  (def listp (x)
    (or (is x nil)
        (and (acons x)
             (listp (cdr x)))))

-----

1 point by Pauan 5265 days ago | link

However, I can easily define nil so that it's type is 'cons, but it would throw an error if you try to assign to the car or cdr. That may break code that assumes that 'cons != nil though.

Actually, I could represent nil as an actual 'cons cell, so that assigning to the car or cdr would work. Crazy? Probably. Especially since nil is a singleton and you can't create more nil's, so it would be a global change.

Right now, nil does have a type of 'sym, but it seems weird to treat it mostly like an empty cons cell, but not have it's type be 'cons. So I figured I could play around with it and try giving it a type of 'cons and see how badly it breaks stuff.

-----

2 points by evanrmurphy 5264 days ago | link

"Actually, I could represent nil as an actual 'cons cell, so that assigning to the car or cdr would work. Crazy?"

That's a bit crazy. :)

PicoLisp does something reminiscent. Every one of its data structures (numbers, symbols, nil and conses) is implemented using the low-level cons cell structure (i.e. a pair of machine words). [1] They talk about nil's representation fulfilling its dual nature as both a symbol whose value is nil and a list whose car and cdr are nil; both the symbol predicate and the list predicate return true when applied to nil:

  : (sym? NIL)
  -> T
  : (lst? NIL)   
  -> T
I'm not sure that they let nil's car and cdr be assignable though, because "NIL is a special symbol which exists exactly once in the whole system." [2]

--

[1] http://software-lab.de/doc/ref.html#vm

[2] http://software-lab.de/doc/ref.html#nilSym

--

Update: Oops, I just noticed a lot of this comment could be considered redundant with the grandparent comment by waterhouse. Sorry for that.

-----

1 point by rocketnia 5267 days ago | link

My code has a lot of [string:or _ "nil"] to handle that kind of stuff. ^_^ I've just kinda accepted it ever since http://arclanguage.org/item?id=10793.

I do 'string lists into strings a lot, but that would continue to work if 'nil and '() were separate values.

-----

1 point by aw 5267 days ago | link

If the variable's name is nil, all kinds of things stop working

Can you give an example? Are you trying to use nil as a variable name in Arc, or simply to have a data structure representing variables where some of those variables are named nil?

-----

1 point by shader 5265 days ago | link

I'm creating a compiler for a class, and I've been representing variable names in the ast with symbols. I could use strings instead, and may end up doing so, but symbols seemed a more elegant solution.

-----

1 point by Pauan 5267 days ago | link

So nil would be an ubertype that basically just means "empty"? One issue with that is (is 0 nil) would be t... I think it's useful to not always treat 0 as the same as nil.

Not sure about empty tables, though... maybe it would be fine to treat those as nil. We already treat an empty list as nil, after all.

Actually, what you're suggesting is very similar to how languages like JavaScript and Python do it, with 0 null undefined NaN "" etc. being falsy. So in JS, you can do this:

  var foo = 0;
  if (foo) {} // will not be evaluated

  foo = "";
  if (foo) {} // will not be evaluated

  foo = NaN;
  if (foo) {} // will not be evaluated
On the other hand, both those languages support actual Booleans, and they treat an empty array as true.

-----

1 point by rocketnia 5267 days ago | link

Yeah, t should be rebindable, right? :)

-----

2 points by aw 5267 days ago | link

http://awwx.ws/localt0

-----

1 point by rocketnia 5267 days ago | link

I mean making 't an everyday global variable, like fallintothis does at http://arclanguage.org/item?id=11711.

As far as prior work goes, I think this topic is the last time "can't rebind t" came up: http://arclanguage.org/item?id=13080 It's mainly just me linking back to those other two pages, but it's also a pretty good summary of my own opinion of the issues involved.

-----

2 points by aw 5267 days ago | link

In my runtime project, nil and t are ordinary global variables... if someone turns out to want the rebind protection feature, I'll add it as a compiler extension (which other people could then choose to apply, or not, as they wished).

-----

1 point by Pauan 5267 days ago | link

What I would do is make it print a warning, but still allow it. Something like, "hey, you! it's a bad idea to rebind nil; you'll probably break everything! use (= nil ()) to fix the mess you probably made"

-----

1 point by aw 5267 days ago | link

Printing a warning is a good idea. Whether rebinding nil could be fixed with "(= nil ())" is an interesting question, you might (or might not, I haven't tried it) find that rebinding nil breaks Arc so badly that = no longer works... :-)

I see a potential for a contest here: how to permanently break Arc (with "permanently" meaning you can't recover by typing something at the REPL), in the most interesting way, using the fewest characters. (Non-interesting being, for example, going into an infinite loop so that you don't return to the REPL).

-----

1 point by Pauan 5267 days ago | link

Quite possibly. It should work in my interpreter, though. Actually, () and nil are two different values, but they're special-cased to be eq to each other. It was the only way I found to make one print as "nil" and the other print as "()" From an external point of view, though, they should seem the same.

Also, if = doesn't work, assign probably would:

  (assign nil '())
I just tested it in my interpreter:

  (assign nil 'foo) -> foo
  nil               -> foo
  'nil              -> nil
  ()                -> ()
  '()               -> nil
  (assign nil '())  -> nil
  (is nil ())       -> t
  
So yes, it should be possible to recover, even after overwriting nil (at least in my interpreter. I don't know about MzScheme)

-----

1 point by Pauan 5267 days ago | link

Exactly! nil too. :P

-----

2 points by shader 5267 days ago | link

Hmm... the issue with that is that you might start having to quote nil or t whenever you want to actually mean nil or t, instead of just typing them in normally.

I do wish there was an easier way to tell whether or not a value was provided as nil, or was left empty and defaults to nil. Maybe doing destructuring on rest args would help solve that problem in most cases?

-----

1 point by Pauan 5267 days ago | link

"I do wish there was an easier way to tell whether or not a value was provided as nil, or was left empty and defaults to nil."

I too have sometimes wished for that in JavaScript, but let me tell you a little story. I was writing a syntax highlighter, and got it working fine in Chrome and Firefox 3.5, but there was a bug in Firefox 3.0.

You see, I was using this bit of code here:

  output.push(text.slice(curr.index[1], next && next.index[0]));
If `next` doesn't exist, it will pass the value `undefined` to the `slice` method. In JS, if you don't pass an argument, it defaults to `undefined`, so this is supposed to behave like as if I hadn't passed in the argument at all.

But in Firefox 3.0, the slice method behaves differently depending on whether you pass it `undefined`, or don't pass it any arguments. So, I had to use this instead:

  if (!next) {
      output.push(text.slice(curr.index[1]));
  } else {
      output.push(text.slice(curr.index[1], next.index[0]));
  }
This was (thankfully) fixed in 3.5. The moral of the story: most of the time it doesn't matter whether the caller passed nil, or didn't pass anything. You can treat the two situations as the same.

Consider this hypothetical example in Arc:

  (your-func 5 (and x y z))
If x, y, or z are non-nil, it will be passed in as usual. On the other hand, if any of them are nil, it will be like as if you had used (your-func 5 nil).

By behaving differently when nil is passed in vs. not passing in an argument, you might cause the above example to break. Or perhaps it would work, but the behavior would be subtly different... introducing bugs.

By having different behavior depending on whether an argument is passed or not, you force callers to do this, instead:

  (iflet val (and x y z)
    (your-func 5 val)
    (your-func 5))
Note the redundancy. In fact, this is even more important in Arc (compared to JavaScript) because you can use any expression, such as (if), a macro call, etc.

So... let me ask: what situations do you really need to know whether the caller actually passed in nil, or didn't pass anything at all?

-----

1 point by rocketnia 5266 days ago | link

Great point. In fact, I don't check whether an optional argument was passed very often, and the times I do, I usually expect to regret it at some point, for exactly that reason. ^_^

-----

1 point by rocketnia 5267 days ago | link

"I do wish there was an easier way to tell whether or not a value was provided as nil"

I share this sentiment. One thing we could do is have a space of hidden-from-the-programmer variables which tell you whether other variables have been bound. They can be accessed using a macro:

  (= given-prefix* (string (uniq) "-given-"))
  (mac given (var)
    ; NOTE: I don't think this will work properly for nil, but nil is
    ; never a local variable name anyway.
    (sym:+ given-prefix* var))
The implementation of argument lists would need to be aware of 'given-prefix* and bind the prefixed variables at the same time as the regular ones.

---

"Maybe doing destructuring on rest args would help solve that problem in most cases?"

What do you mean by that?

-----

2 points by shader 5267 days ago | link

Well, if you use a rest arg for all optional values, and then use some form of destructuring bind on that list to extract your optional arguments, then you can tell whether or not they were passed in or merely defaulted to nil by just searching the arg list.

  (def test args
    (if (assoc 'c args)
          (pr "c was passed")
        (pr "c was not passed")))

-----

1 point by rocketnia 5267 days ago | link

I still don't follow. We can already manage the argument list manually, but in most of the suggestions here, we can only do it if we don't destructure it in the signature (unless we use more complicated kinds of destructuring).

  ; Current options:
  
  (def test args
    (let ((o c)) args
      (pr:if (len> args 0)
        "c was passed"
        "c was not passed")))
  
  (let missing list.nil  ; This is just something unique.
    (def test ((o c missing))
      (pr:if (is c missing)
        "c was not passed"
        "c was passed")))
  
  
  ; Some hypothetical options and non-options:
  
  (def test (& (c))
    (pr "no way to tell if c was passed"))
  
  (let missing list.nil
    (def test (& (c))
      (pr "still no way to tell if c was passed")))
  
  (def test (& args)
    (let ((o c)) args
      (pr:if (len> args 0)
        "c was passed"
        "c was not passed")))
  
  (def test (& (&both args (c)))  ; Destructure twice.
    (pr:if (len> args 0)
      "c was passed"
      "c was not passed"))
  
  (def test ((o c nil c-passed))
    (pr:if c-passed
      "c was passed"
      "c was not passed"))
  
  (def test ((o c))
    (pr:if given.c
      "c was passed"
      "c was not passed"))
  
  (def test (c)  ; Parameter lists are just destructuring.
    (pr:if given.c
      "c was passed"
      "c was not passed"))
  
  (def test (&both args (c))
    (pr:if (len> args 0)
      "c was passed"
      "c was not passed"))

-----

1 point by Pauan 5267 days ago | link

Brilliant! In fact, you could write a macro that would do that for you:

  (mac defreq (name args . body)
    `(w/uniq gen
       (def ,name ,(map (fn (x) `(o ,x gen)) args)
         ,@(map (fn (x) `(if (is ,x gen) (err:string "parameter " ',x " is required"))) args)
         ,@body)))

  (defreq foo (x y) (+ x y))
  (foo)     -> x is required
  (foo 1)   -> y is required
  (foo 1 2) -> 3
It probably breaks with rest arguments, but I think you could get those working too.

-----

1 point by Pauan 5267 days ago | link

Or this version, which is even better:

  (mac defreq (name vars . body)
    (if (isa vars 'cons)
          (let exp (len vars)
            `(def ,name args
               (let giv (len args)
                 (if (< giv ,exp)
                       (err:string "expected " ,exp " arguments (" giv " given)")
                     (apply (fn ,vars ,@body) args)))))
        `(def ,name ,vars ,@body)))


  (defreq foo (x y) (+ x y))
  (foo)     -> error: expected 2 arguments (0 given)
  (foo 1)   -> error: expected 2 arguments (1 given)
  (foo 1 2) -> 3
  
  (defreq foo args args)
  (foo)     -> ()
  (foo 1)   -> (1)
  (foo 1 2) -> (1 2)
  
It fails on functions that take required and rest args, though:

  (defreq foo (x y . args) (list x y args)) -> error
Err... right, you were talking about detecting if an argument was nil or not given... but I realized that the same technique could be used to write a version of def that implements required arguments even in a language where every argument is optional.

-----

1 point by Pauan 5267 days ago | link

Only if you actually rebind them. It's like using `quote` as a variable name: you can do it, but most people won't because that's silly. I just think it's nice to allow it, on the off chance it's actually useful. It just feels weird to arbitrarily say "you can't rebind nil and t" but allow rebinding of everything else.

-----

1 point by Pauan 5267 days ago | link

Using err:string is good, but why the unnecessary `and` check? Am I missing something?

-----

1 point by shader 5267 days ago | link

The and is required to make sure that a type was provided, otherwise it will always fail the type check. Also, if you leave n out of the and clause, it will still pass if the required type is sym. Maybe that should be fixed.

-----

1 point by Pauan 5267 days ago | link

Hm... yes, you're right. Odd, I remember it working fine when I tested it earlier. This should work correctly:

  (mac require (n (o t))
    `(if (no ,n)                  (err:string "parameter " ',n " is required")
         (and ,t (no:isa ,n ,t))  (err:string "parameter " ',n " must be of type " ,t)))

-----

2 points by Pauan 5269 days ago | link

Time to implement this in my Arc interpreter written in Python. I should be able to get (. (a b)) to work too.

Also, if (. (a b)) is equivalent to ((o a) (o b)), then how would you specify the value, like with ((o a 'x) (o b 'y))?

-----

2 points by Pauan 5268 days ago | link

It only took about 30-60 minutes to implement it. My Arc interpreter now supports both forms of optional parameters:

  (def foo (a b (o c) (o d) . e))  ; works
  (def foo (a b . (c d . e)))      ; works
  
I plan to support this style of optionals too:

  (def foo (a b ? o d . e))
  
  
This could all change, though. I haven't yet seen the Perfect Holy Grail for optional arguments. I kinda like Python's take on it, though:

  def foo(a, b, c=None, d=None, *e):
      pass
      
Which would be this, in Arc:

  (def foo (a b c=nil d=nil . e))
  
Which would expand to:

  (def foo (a b (= c nil) (= d nil) . e))

-----

1 point by Pauan 5266 days ago | link

Okay, scrap that. Here's my new plan:

Make all arguments optional, as per evanrmurphy's suggestion. I'll still support the (o foo) form for backwards compatibility, but I think it has so many problems that it really should be avoided.

This isn't final, but I figure the simplicity of not supporting required parameters will be a net gain most of the time. If you really need required parameters, it's possible to write macros that do the checks at run-time. I might even provide such a macro in arc.arc so you don't have to write it yourself.

Also, for telling Arc what the default is (it's normally nil), you use the (= foo 'bar) form, like so:

  (def foo ((o a 1) (o b 2))) ; pgArc
  (def foo ((= a 1) (= b 2))) ; PyArc
I also plan to support argument-ssyntax, which is basically ssyntax that is only expanded in the argument list. Then you can do this:

  (def foo (a=1 b=2))
This should, of course, be customizable within Arc.

-----

1 point by evanrmurphy 5269 days ago | link

The first wart I observe is that it provides no way to specify all the parameters as optional, e.g. the arc3.1 definition

  (def baz ((o a) (o b))
    ...)               
cannot be expressed.

Hmm... perhaps I like the scheme where parameters are always optional best. Its concision cannot be beat!

-----

1 point by akkartik 5269 days ago | link

Why, does this not work? (I still haven't installed clojure. JVM, yuck..)

  (def baz (& a b)
    ..)

-----

1 point by evanrmurphy 5269 days ago | link

That should work in Clojure, you're right. + But the analogous code in an arc-like would not work because (. (a b)) is not a valid list (since it has no car).

---

+ Technically, it would be:

  (defn baz [& [a b]]
    ..)

-----

1 point by akkartik 5269 days ago | link

There's a lot more wrong with it :) You can't have non-atoms after the dot either.

I was taking for granted that you have to change the reader, or replace dot with a different token.

-----

1 point by akkartik 5269 days ago | link

Yeah it's an interesting blind spot. I think it was the dot syntax - we 'knew' that you can't have anything after dot except an atom.

-----

More