Arc Forumnew | comments | leaders | submit | rocketnia's commentslogin
2 points by rocketnia 5160 days ago | link | parent | on: Arcueid, a C implementation of Arc

Thanks for that link, for a completely off-topic reason. That's one of the best examples of the golden W hamburger place I've seen. :-p http://www.baka-raptor.com/WcDonalds/index.php

-----

2 points by rocketnia 5181 days ago | link | parent | on: Nu Arc compiler

In Nu, the default namespace is the one corresponding closely with Arc 3.1. And in this namespace, the default definition of 'map is pretty much Arc 3.1's. Therefore, "it's not the default" is true in Nu.

Another namespace in Nu is Arubic. Using this namespace is like using a language other than Arc 3.1, where 'map is a macro. If your problem is with Arubic itself, you're free to ignore the parts of Arubic you don't like (even if you want to use an library written in full Arubic). If the point is you think people shouldn't dilute the overall meaning of "map" with the meaning Pauan gives it in Arubic, that's a moral position, with Pauan as an unintentional villain!

---

What I think is that the meaning of "map" isn't tainted here, at least not any more than usual. In fact, I wanted 'map to be an 'each -like macro before I knew Pauan had the same thing in mind.

If I don't know what language we're talking about and I hear "map," I assume it'll be an abstraction that applies a given transformation to elements of a given data structure (whatever "abstraction," "transformation," "data structure," and "given" mean), along with some accidental complexity suitable to the language, such as the timing of computation, side effects permitted in the transformation, or late-bound dependencies (like 'map calling out to 'cons, such that rebinding 'cons changes what happens).

In a language where macros like 'after and 'each are more convenient to use than higher-order functions like 'protect and Anarki's 'trav, a macro for map is to be expected.

In Arc, almkglor[1] calls this macro "mapeach" in Anarki, and I call it "maplet" in Lathe. Pauan and I call it "map" as long as we don't have naming conflicts to worry about. As long as this macro is the primary way we use the map concept, using that name keeps our programs brief and frank.

[1] I'm not sure about this credit, but I found it at https://github.com/nex3/arc/blame/arc2.master/arc.arc and verified it with a Web search.

-----

2 points by rocketnia 5180 days ago | link

Whoops, Anarki's 'trav is a macro. I meant 'walk. ^_^;

-----

1 point by rocketnia 5181 days ago | link | parent | on: Nu Arc compiler

I can't find whatever information you're talking about. Would you mind quoting it and/or elaborating?

Don't put too much work into the explanation, 'cause I'm likely to come in at the end and say "but what about X?" :-p It sounds too good to be true.

-----

1 point by Pauan 5181 days ago | link

It's right near the top of the link:

  All constant-time procedures and operations provided by Racket are
  thread-safe because they are atomic. For example, set! assigns to a variable
  as an atomic action with respect to all threads, so that no thread can see a
  “half-assigned” variable. Similarly, vector-set! assigns to a vector
  atomically. The hash-set! procedure is not atomic, but the table is
  protected by a lock; see Hash Tables for more information. Port operations
  are generally not atomic, but they are thread-safe in the sense that a byte
  consumed by one thread from an input port will not be returned also to
  another thread, and procedures like port-commit-peeked and write-bytes-avail
  offer specific concurrency guarantees.
It mentions that hash table assignment is not thread-safe, however if you then go to the hash table page[1], it says this:

  A mutable hash table can be manipulated with hash-ref, hash-set!, and
  hash-remove! concurrently by multiple threads, and the operations are
  protected by a table-specific semaphore as needed. Three caveats apply,
  however [...]
In other words, Racket already handles everything, according to the docs. If you're ever worried enough, or run into any problems, it's not hard to wrap it in `atomic` yourself. I'd rather not have the cost of `atomic-invoke` for every assignment, especially if you run all your code in one thread (like I do).

By the way, Nu doesn't use `set!` for global assignment, so I'm not sure if global assignment in Nu is thread-safe or not. But I'd assume it is, since I think `namespace-set-variable-value!` is constant-time.

---

* [1]: http://docs.racket-lang.org/reference/hashtables.html

-----

2 points by rocketnia 5181 days ago | link

In pg-Arc, '= on a variable is 'assign without 'atomic. Where 'atomic comes in is when there's a setforms thing to worry about.

And in that case, this...

  (= (car car.foo) (bar))
...turns into something like this:

  (atomic:with (gs1 car.foo gs2 (bar))
    ( (fn (val) (scar gs1 val))
      gs2))
This ensures that car.foo, (bar), and (scar gs1 val) all happen without interference in between. I suspect Racket at most protects those on an individual basis.

That said, I don't care about 'atomic myself. :-p

-----

1 point by Pauan 5181 days ago | link

"In pg-Arc, '= on a variable is 'assign without 'atomic. Where 'atomic comes in is when there's a setforms thing to worry about."

I am aware. It still seems to me that if you're dealing with threads, you should wrap assignment in atomic yourself if you're worried about such things. Code that doesn't deal with threads shouldn't have to use atomic.

Perhaps there should be an `a=` macro that's just like `=` but it calls `atomic`. Hm... I wonder... would it be possible to detect whether code is running in the default thread and if not, automatically wrap it in atomic...? May be more trouble than it's worth, though.

-----

2 points by rocketnia 5181 days ago | link

"May be more trouble than it's worth, though."

That's what I think. Anyone who cares can say (atomic:= ...) or (atomic:zap ...), so I only see a couple of reasons why we'd want to have the 'atomic implicit:

- We want to use it all the time anyway. (I doubt it, but it's hard to tell. I haven't used threads, and therefore I've never bothered to find a way to squeeze utility out of it.)

- There are people who do care, and they'd be better off if the people who didn't care still used 'atomic by accident. (Again, it's hard for me to tell if this is true.)

-----

3 points by rocketnia 5182 days ago | link | parent | on: Nu Arc compiler

"It truly does not make any sense to me why zap is defined like this"

'zap is the only use I typically have for the 'setforms "binds" list (which ensures the subexpressions of 'place are only evaluated once).

Still, 'zap doesn't need to work that way: as long as I'm using a language where I know 'zap evaluates its place twice, I'm pretty much okay with it. It's a wart, but it's not an impediment.

To explore Arc-3.1-like options for a bit, here's a cleanup of Arc 3.1's definition of 'zap:

  (mac zap (op place . args)
    (with (gop                 (uniq)
           gargs               (map [uniq] args)
           (binds val setter)  setforms.place)
      `(atwiths (,@binds ,gop ,op ,@(mappend list gargs args))
         (,setter (,gop ,val ,@gargs)))))
If we allow 'setter and 'val to compile and evaluate before 'op and 'args, it gets shorter:

  (mac zap (op place . args)
    (let (binds val setter) setforms.place
      `(atwiths ,binds
         (,setter (,op ,val ,@args)))))
I prefer to arrange the compilation and evaluation orders from left to right ('op, 'place, 'args), using a technique like this:

  (mac place (place)
    (let (binds val setter) setforms.place
      `(withs ,binds
         (list (fn () ,val) ,setter))))
  
  (mac zap (op place . args)
    `(atomic:fn-zap ,op (place ,place) (list ,@args)))
  
  (def fn-zap (op (getter setter) args)
    (setter:apply op (getter) args))

-----

1 point by Pauan 5182 days ago | link

"'zap is the only use I typically have for the 'setforms "binds" list (which ensures the subexpressions of 'place are only evaluated once)."

Hm... yes, you're right, `(zap + (foo (bar qux)) 1)` evaluates `(bar qux)` twice, and I don't see an easy/obvious way to fix that in `=`. I'll need to think about this.

-----

2 points by rocketnia 5185 days ago | link | parent | on: A deterministic term-rewriting system

"I'm still unsure what the term 'animated' means in this context. Rocketnia?"

For the benefit of people here, this is what David Barbour had to say over email to akkartik and myself:

"Animated term rewriting means that the term is changing over predictable time, in a predictable manner - i.e. you could literally set up a viewer and watch the term change in real-time. If the term specified something like which jpeg image to display, or how to position a robot, this could easily be lifted to visual or physical animations. Animation, in general, is about controlling an external system over time."

I think the benefit here isn't (only) the ability to express animation, but (also) the ability to use a divergence-capable term rewrite system in a way that isn't the end of the world when the system actually does diverge.

-----

1 point by rocketnia 5189 days ago | link | parent | on: Interesting approach to ssyntax

"One thing that expressive languages like Ruby, Smalltalk, and Lisp teach us is that many 'design patterns' are actually language smells. The 'fluent interface' design patterns is just that: A sign that a language is missing a cascading message feature."

When a language has a (sub)community that uses so many side-effectful interfaces that cascading message syntax sounds like a relief, that community-wide design pattern might be the more appropriate thing to repair. :-p

Anyway, I use my own custom infix operator for such purposes: ;it.

  -- who needs this ---
  array
    .pop()
    .pop()
    .pop()

  -- when there's this --
  var it = array  ;it.
    pop()  ;it.
    pop()  ;it.
    pop();
Just kidding around. I haven't developed a pattern in these cases, probably because I program with few side effects and few OO interfaces and because I sooner or later bury any interface I'm working with under layers of my own.

-----

1 point by rocketnia 5189 days ago | link | parent | on: Interesting approach to ssyntax

What makes it ssyntax? The fact that it's not your everyday s-expressions? Isn't that backwards? :-p

-----

1 point by akkartik 5189 days ago | link

"Lark allows functions to be called using the dot operator. For example, instead of length(list) you can use list.length"

-----

1 point by rocketnia 5189 days ago | link

I'd like to know what makes that ssyntax too. I've heard ssyntax called "symbol syntax"[1] for its low-level details, as well as "special syntax"[2] for, well, reasons I haven't figured out, which is why I ask. :-p

Looking back at "Being Popular" and "Arc at 3 Weeks,"[3] I wonder if pg just used "ssyntax" as an in-code abbreviation for "syntax for s-expressions," in which case that's something lark totally is exploring. But even then, just the term s-expression can mean different things to different people, and I'm curious.

[1] http://arclanguage.org/item?id=12978, http://arclanguage.org/item?id=12151

[2] http://arclanguage.org/item?id=5552, http://arclanguage.org/item?id=15130

[3] http://www.paulgraham.com/popular.html, http://www.paulgraham.com/arcll1.html

-----

1 point by rocketnia 5196 days ago | link | parent | on: Nu Arc compiler

"Oh I'd love to have pervasive keywords like Python (or wart), but it seems Racket doesn't support that."

Racket has pervasive keyword arguments though, right? What's in your way?

-----

2 points by Pauan 5195 days ago | link

I assumed akkartik was talking about this:

  (def foo (a b)
    (list a b))

  (foo 1 2)       -> (1 2)
  (foo :b 2 :a 1) -> (1 2)
That doesn't work in Racket. You need to explicitly say that the arguments are keywords:

  (def foo (:a :b)
    (list a b))
In other words, arguments are either positional or keyword-based, but not both at the same time. This is different from Python, which lets you treat an argument as either one:

  def foo(a, b):
      return [a, b]

  foo(1, 2)     -> [1, 2]
  foo(b=2, a=1) -> [1, 2]

-----

1 point by rocketnia 5196 days ago | link | parent | on: Nu Arc compiler

You're not going to be able to use (with hash ...) with a local variable 'hash unless you have at least one of these:

- Fexprs. An fexpr implementation of 'with can use the complete value of 'hash as it determines how to treat the unparsed body.

- Static typing with record types, so that a macro can use the type of 'hash as it determines how to treat the unparsed body.

- Some variant of JavaScript-style scope chain semantics, in the sense that a bare variable reference means (or can mean) a field lookup in general. IMO, this would be the most straightforward to add to an Arc-3.1-like compiler, since it's a matter of compiling foo to (scope 'foo), (with foo ...) to (let ((scope (shadow (scope 'foo) scope))) ...), and other scope-related things in their own analogous ways.

- Something else I haven't thought of. :)

-----

2 points by rocketnia 5201 days ago | link | parent | on: Nu Arc compiler

Awesome! I've been looking forward to seeing some development on ar. ^_^ I like what you're doing with the argument lists. Have you found your arc2js progress useful for this? (Part of me wonders how Racket's local (define ...) forms compare against its other options in terms of performance. If they're faster, that could pose the same compilation annoyances as arc2js deals with. ;) )

As far as the name "Nu" goes, it's actually been taken by another lisp (and I'm not even talking about NewLISP :-p ): https://github.com/timburks/nu. Early this year I did a couple of months of Objective-C programming for work, and I GitHub-followed Nu in envy. XD

-----

1 point by Pauan 5201 days ago | link

"I like what you're doing with the argument lists."

You mean the "destructuring is just lambdas" thing?

---

"Have you found your arc2js progress useful for this?"

No, except insofar as writing arc2js taught me some useful compiler things. For the most part, it's been pretty simple and easy, so I don't think arc2js helped much, if at all.

---

"Part of me wonders how Racket's local (define ...) forms compare against its other options in terms of performance."

  (let a nil (%nocompile (racket-define a 5)) a)
  (let a nil (= a 5) a)
  Time: 1500-1600 ms

  (let a nil (%nocompile (racket-let* ((a 5)) a)))
  (let a nil (%nocompile (racket-let ((a 5)) a)))
  (let a nil (let a 5 a))
  (let a nil a)
  Time: 1400-1500 ms
It would seem Racket optimizes lambdas very heavily.

---

"As far as the name "Nu" goes, it's actually been taken by another lisp (and I'm not even talking about NewLISP :-p )"

Darn. I still like the name "Nu" enough that I want to keep it, though.

-----

3 points by rocketnia 5199 days ago | link

It took me a while to reply because I was planning to do some of my own testing before posting, but I haven't found the time.

---

"You mean the "destructuring is just lambdas" thing?"

No, I don't know what to think about that yet. I like it if and only if this works:

  (let (a . b) '(1 . 2)
    ...)
Even if that works, I almost expect Racket to copy a mutable argument list into an immutable one for use inside the lambda, so here's a second test case:

  (withs (foo (list 1 2 3)
          (a . b) foo)
    (= b.0 4)
    (is foo.1 4))
What I definitely do like is the use of Racket's optional arg syntax where possible. ^_^

---

"It would seem Racket optimizes lambdas very heavily."

IMO, those examples aren't normal uses of (define ...), since they're contexts where the variable is already defined. I'd like to test these cases:

  ; Racket code
  
  (let ((a 5)) a)
  ((lambda (a) a) 5)
  (begin (define a 5) a)
    ; NOTE: Make sure this (begin ...) isn't at the top level, or the
    ; definition will be global.
  (let () (define a 5) a)
  ((lambda () (define a 5) a))
One reason it's on my mind is the Racket 5.2 changelog (http://lists.racket-lang.org/users/archive/2011-November/048...):

  * Internal-definition expansion has changed to use `let*' semantics
    for sequences that contain no back references.  This change
    removes a performance penalty for using internal definitions
    instead of `let' in common cases, and it only changes the meaning
    of programs that capture continuations in internal definitions.
    Internal definitions are now considered preferable in style to
    `let'.
Then again, if they're going to make more semantic changes like this one, it might be better to avoid compiling to an internal-definition style. :-p

Anyway, from the phrasing in the changelog, it sounds like internal definitions are implemented in ways the programmer can already control at a lower level with things like 'let*, so you'll probably continue to find that internal definitions are at least as slow as other options.

-----

2 points by Pauan 5199 days ago | link

"I like it if and only if this works:"

That doesn't, but this does:

  (let (a . b) '(1 2 3)
    ...)
The reason is because racket-mlist->list expects a proper list. I can fix that easily.

Update: I fixed racket-mlist->list, but Racket's apply absolutely positively requires a proper list, so it looks like I can't use nested lambdas if I want to fix that: I'll have to use a Racket let*

---

"I almost expect Racket to copy a mutable argument list into an immutable one for use inside the lambda"

It's true, it does copy the list, so that returns nil. I wonder if I can work around that...

---

"One reason it's on my mind is the Racket 5.2 changelog"

I'm not using Racket 5.2. And even if I were, the reason for the speed tests was to see how it would perform within the (fn ...) expansion, where it does indeed overwrite an already-existing variable. (Though I could change it to use a gensym...)

-----

More