Arc Forumnew | comments | leaders | submit | shiro's commentslogin
2 points by shiro 6113 days ago | link | parent | on: Arc portability

The biggest obstacle would be the [] support. Current implementation relies on the readtable feature, which isn't very common among Scheme implementations. You can implement your own read function, which would be much more work than the current bracket.scm.

Other non-standard features include hashtables, threads and some system functions, but you can find them in most full-featuered implementations. (Except break-thread, whose semantics is not in pthread IIRC, so pthread-based thread system may have some difficulties.)

-----

5 points by shiro 6116 days ago | link | parent | on: Kicking up more fuss about modules

Oh, if you want that, check out letrec*.

http://www.r6rs.org/final/html/r6rs/r6rs-Z-H-14.html#node_id...

-----

1 point by cchooper 6116 days ago | link

That's it, perfectly!

-----

3 points by shiro 6116 days ago | link | parent | on: Kicking up more fuss about modules

The closest thing you want is letrec in Scheme, which binds both functions and variables.

-----

4 points by shiro 6117 days ago | link | parent | on: Does Arc need modules? Maybe not.

Yes, you can do all those stuff in existing primitives. But maybe a little convenience macros will help, like auto-prefixing macros. And another thin macro that expands to 'with', with some bookkeeping features such as specifying the prefix in one place? Then let's call that macro 'module' and voila! You have modules.

Really, it is possible to build module system if you have enough primitives (what's 'enough' depends on what model you use). The point of modules is not whether it is possible or not. The point is that whether everybody agrees on a certain convention or not.

-----

3 points by cchooper 6117 days ago | link

I originally considered writing a macro that did just that, and even called it module, but then I realised I was packing a load of disparate features into one macro just so it would look like modules in other languages.

I don't think the point is conventions (at least not yet). I think the point is that modules may be an onion, and we don't want any onions.

-----

3 points by shiro 6117 days ago | link

If the module system is an onion, it is an onion with a kernel, which is really a convention.

For example, see SLIB, the portable Scheme library http://www-swiss.ai.mit.edu/~jaffer/SLIB.html . It is designed to work with R5RS Scheme and before, thus it can't use any module systems. Instead, it uses a naming convention, which works pretty well. The problem is that, to make it work, all programmer should agree and follow the convention. The so-called module systems make it easier to follow the rules by making the existence of the convention invisible (automatically adding prefixes effectively, e.g.) or preventing programmers from doing things differently.

In the discussion of R6RS Scheme, where a module system is finally adopted, some people argued that you can roll your own modules system out of given primitives, and it had proven in the production code. But if you use your own module system (or convention) and I use my own, it will be difficult to mix two libraries. Whatever it is, we have to agree on one way. I think that's really the point.

-----

1 point by cchooper 6117 days ago | link

I agree that module systems are conventions. What I don't agree with is that we should uncritically adopt them in Arc. There may be a better way. What that is, I don't know.

-----

4 points by shiro 6117 days ago | link

Then the important question is this: When you say better, better in what sense? Beyond a certain point, people have different criteria and your better and my better start to diverge.

In Scheme, traditionally such matter has been settled by "agreed to disagree"---instead of choosing one, the spec just leave it unspecified. But R6RS had a goal to allow portable libraries, so it had to choose one; for modules, it is better to have lesser but one module system, rather than having multiple ("better" in various regards) or than having none.

In Arc, ultimately it's up to pg.

[Edit: To be more PC, R6RS module is 'less agreed'. I do understand it is better in its design criteria.]

-----

1 point by cchooper 6117 days ago | link

Good question. I'm sort of inspired by PG's thoughts about object-oriented programming, that it would be better if they offered the features a la carte rather that packaging them up into objects.

-----

2 points by shiro 6117 days ago | link | parent | on: Does Arc need modules? Maybe not.

How about

    `(def ,(read (+ (coerce prefix 'string) "-" (coerce name 'string))) ,args ,@body)

-----

1 point by cchooper 6117 days ago | link

Doesn't work I'm afraid:

  (= pre 'blub)

  (your-def-with-prefix pre foo (x) (+ 1 x))
  => #<procedure: pre-foo>

  (my-def-with-prefix pre foo (x) (+ 1 x))
  => #<procedure: blub-foo>
Without being able to take the prefix from the value of a variable, the whole macro is pointless.

-----

1 point by eds 6117 days ago | link

Then why don't you just eval prefix?

  `(def ,(read (+ (coerce (eval prefix) 'string) "-" (coerce name 'string))) ,args ,@body)
It's still much less clunky than expanding the whole def under an eval.

-----

1 point by cchooper 6117 days ago | link

Still has a bug:

  (let pre2 'blub (your-def-with-prefix pre2 foo (x) (+ 1 x)))
  => Error: "reference to undefined identifier: _pre2"

-----

2 points by cooldude127 6117 days ago | link

that's because the macro gets expanded (and thus eval is called on pre2) before the let form is evaluated.

-----

1 point by eds 6117 days ago | link

Yeah, I see the problem with that.

cchooper, did you actually ever run your version? I can't make your orginal version work.

  arc> (def-with-prefix pre foo (x) (+ 1 x))
  Error: "reference to undefined identifier: _x"
And looking at the macro expansion, I'm not at all surprised:

  arc> (macex '(def-with-prefix pre foo (x) (+ 1 x)))
  (eval (list (quote def) ; my indentation
              (read (+ (coerce pre (quote string)) "-" (coerce (quote foo) (quote string))))
              (x) (+ 1 x)))
args and body would need to be quoted for this to work properly. And then it admittedly does what it is supposed to, but it becomes even uglier.

  (mac def-with-prefix (prefix name args . body)
    `(eval (list 'def
            (read (+ (coerce ,prefix 'string)
                     "-"
                     (coerce ',name 'string)))
            ',args
            ,@(map [list 'quote _] body))))

-----

1 point by cchooper 6116 days ago | link

Hmmm... it worked last night. I must have made a mistake when I copied it into the comment. Thanks for catching it.

Here's a slightly less clunky version:

  (mac def-with-prefix (prefix name args . body)
    `(eval (join (list 'def 
                  (sym (+ (coerce ,prefix 'string)
                          "-"
                          (coerce ',name 'string)))
                  ',args)
            ',body)))
I also tried doing it with nested quasiquotes but that just looked worse.

-----

1 point by shiro 6117 days ago | link

Ah, I got it.

-----

1 point by cooldude127 6117 days ago | link

yes, like that. there is no reason i can see for a macro to have to call eval except in the MOST EXTREME cases.

-----

2 points by cchooper 6117 days ago | link

Unfortunately, Arc makes this an extreme case. There's just no way to get the exact required behaviour in Arc without using eval.

In Arc:

  arc> (= foo 's)
  s
  arc> (set foo 4)
  4
  arc> foo
  4
  arc> s
  Error: "reference to undefined identifier: _s"
In Common Lisp:

  CL-USER> (setf foo 's)
  S
  CL-USER> (set foo 4)
  4
  CL-USER> foo
  S
  CL-USER> s
  4

-----

5 points by shiro 6119 days ago | link | parent | on: Top 10 differences between Scheme and Arc

Not anymore with R6RS---it has over 650 functions. Besides, I tend to think SRFIs as a part of 'modern' Scheme, as some of them are quite essential for day-to-day programming. (Although the popularity varies a lot---cf. http://practical-scheme.net/wiliki/schemexref.cgi?SRFI )

-----

2 points by shiro 6119 days ago | link | parent | on: Docstrings: a contrarian view

Although this is not a very general example, I tend to oppose in-code docstrings for a couple of reasons.

One of the primary reasons is that we have developers in multiple countries with different primary languges; forcing everyone to write English docs would actually discourages some from writing docs. And mixing multiple languages in the source was a source of troubles (although it's getting better now for most tools and editors can cope with utf-8.)

Another is that I see sometimes documentation and code need different structures; tying one doc for each global entry skews documents away from the better structure. (This doesn't exclude having a small 'remainder' entry per each code, which is useful as you said that you can check in the editor. )

-----


It's a good argument in general sense, but I doubt it is applicable in library API.

If you're delivering a final product, users don't care if some design is deliberate or not; they care it is good or bad. If you're deriving mathematic proof, others don't care if some choice is deliberate or not; they care if it is correct, beautiful, or useful to prove other theorems. That's because changing your choice afterwards won't break existing products or proofs that relied on the previous choices.

In the case of library API, changing your choice does break existing software relying on the library. In the current Arc case it is forewarned so it's perfectly fine, but at some point (50years from now, maybe?) you have to let more people write software on it; by that moment it should be clear that what they can rely on and what they cannot.

-----

2 points by pg 6123 days ago | link

by that moment it should be clear that what they can rely on and what they cannot

The only difference if the implementation is the spec is how they know what they can rely on. If the implementation is the spec, they decide by reading the source; if it's a document writen in English, they decide by reading that.

-----

4 points by shiro 6123 days ago | link

Implementation can be, and will be, changed, inevitably. Then does the language change as well, or the languages remains the same but just implementation is improved? How can you tell the difference purely from the source?

Some Scheme implementation evaluates arguments left to right. You can see that by reading the source. In future, it may switch it right to left, maybe for better optimization. The spec in natural language, or more formal and abstract form like in Appendix A of R6RS, can explicitly say the order of evaluation is unspecified. How you tell your users that they should not rely on the evaluation order purely by the source code, given the state of most programming languages?

Ideally I like to think the source only describes the spec and the compiler and runtime figure out the details, so maybe spec-by-source and spec-by-other-notation will converge in future. Is that what you are talking?

(Please don't argue that unspecified evaluation order is bad or not; I just use that example to illustrate the point. And incidentally, since Arc is defined in terms of Scheme, the order of argument evaluation order is just unspecified as well. But it's just delegating the spec to a lower level.)

-----

1 point by soegaard 6123 days ago | link

Actually PLT Scheme guarantees left-to-right order, but that doesn't change your point.

-----

2 points by akkartik 6123 days ago | link

One point everybody else is missing: since arc explicitly makes no claims of backwards compatibility, the notion of a spec is meaningless.

If the goal of a language is to be readable there's nothing wrong in the implementation being the spec. Consider it a form of self-hosting, or of eating your own dogfood.

---

An implementation in a reasonably high-level declarative language is a more reasonable 'spec' than one in C. More features are amenable to checking just by reading rather than by execution.

When something is obviously a bug it doesn't matter if it's a bug in the spec or the implementation.

Those two categories -- obvious bugs, and questions about what is or is not in the language that should be answered by reading the code rather than executing it -- seem to cover all the objections people have raised.

-----

1 point by shiro 6123 days ago | link

At least I'm talking about the attitude of spec-by-source in general, not particularly about Arc, FYI.

Edit: I agree that more abstract, declarative language is closer to spec-by-source. If somebody says Prelude.hs is the spec of Haskell's standard library I agree. But the core language semantics is still not in Haskell itself, is it? (I'm still learning. Correct me if I'm wrong.)

-----

1 point by almkglor 6123 days ago | link

Right. And nobody needs comments in source code, ever.

-----

1 point by akkartik 6123 days ago | link

What!?

FWIW, here's how I think comments in source code can be improved, especially in exploratory programming: http://akkartik.name/codelog.html

-----

1 point by almkglor 6123 days ago | link

Oh come on. What are comments but prose descriptions of the code?

Anyway please reparse my post with <sarcastic>...</sarcastic> around it.

-----

1 point by akkartik 6123 days ago | link

I did get the sarcasm, but not how you extrapolate from my comment.

"There's nothing wrong with a code spec in this case" != "prose specs suck"

-----

1 point by almkglor 6123 days ago | link

Hmm. Must be a bug in my reader ^^

-----


I don't know much about MzScheme internals. But from my experience of implementing Gauche Scheme, inlining basic operators such as <, +, and - is a big performance boost, and I suspect MzScheme does similar thing as far as these ops are not redefined. Calling them many times via 'wrapper' function like ac.scm does diminishes that effect.

That said, one possible overhead is that, although arc compiler tries to use ar-funcall2 to avoid consing the arguments, they are consed after all since _+ and _< are defined to take a list of arguments.

The original (time (fib 34)) took 79.8s on my machine.

Changing arc< in ac.scm with this:

    (define arc<
      (case-lambda
        [(a b)
         (cond [(and (number? a) (number? b)) (< a b)]
               [(and (string? a) (string? b)) (string<? a b)]
               [(and (symbol? a) (symbol? b)) (string<? (symbol->string a)
                                                        (symbol->string b))]
               [(and (char? a) (char? b)) (char<? a b)]
               [else (< a b)])]
        [args
         (cond ((all number? args) (apply < args))
               ((all string? args) (pairwise string<? args #f))
               ((all symbol? args) (pairwise (lambda (x y)
                                               (string<? (symbol->string x) 
                                                         (symbol->string y)))
                                             args
                                             #f))
               ((all char?   args) (pairwise char<?   args #f))
               (#t                 (apply < args)))]))
made (time (fib 34)) 72.8s, and further changing _+ with this:

    (xdef '+
          (case-lambda
            [() 0]
            [(a b)
             (cond [(and (string? a) (string? b)) (string-append a b)]
                   [(and (arc-list? a) (arc-list? b))
                    (ac-niltree (append (ar-nil-terminate a)
                                        (ar-nil-terminate b)))]
                   [else (+ a b)])]
            [args
             (cond [(all string? args) 
                    (apply string-append args)]
                   [(all arc-list? args) 
                    (ac-niltree (apply append (map ar-nil-terminate args)))]
                   [else (apply + args)])]))
made (time (fib 34)) 49.5s.

But generally, these kind of tuning for microbenchmarks only have small effect in real applications, for microbenchmarks magnifies inefficiency of very specific parts.

(Afterthought: It won't be too difficult to write a Scheme macro that expands variable-arity type-dispatching functions like _+ or _+ into case-lambda form. Then this kind of optimization can be applied without cluttering the source too much.)

-----


> Isn't its specification the source itself

The problem of source-is-the-spec approach is that it tends to overspecify things. Especially it is difficult to represent something is "unspecified" in the form of the source code.

What's the benefit of not specifying something in the specification? It leaves a room for different implementation strategies, each of which is specialized for certain situations. It allows putting off design decisions when there's not enough experience to decide which choice is better. It can warn the user that the existing choice is purely arbitrary and it may be changed when circumstances change (note: the big point is that, a concrete implementation has to choose one, even when from the specification point of view the choice doesn't matter.)

For example, Arc's current map is restart-safe for list arguments but restart-unsafe for string arguments (restart-safe means, if you evaluate (map f xs), and a continuation is captured within f, and later the continuation is restarted and the restart doesn't affect the result of the previous return value of map.) Is it a part of "spec" so that one can rely on that exact behavior? Can I rely on that the restart actually modifies the string returned from the first call? Or is it just an arbitrary implementation choice and one should assume the result of mixing map and call/cc is unspecified?

(BTW: In R5RS Scheme it was unspecified. In R6RS Scheme it is explicitly specified that map must be restart safe.)

-----

2 points by cadaver 6123 days ago | link

Amateur warning:

I think, having a prose spec that leaves things unspecified may not be particularly useful in Arc's case; unspecified behaviour in a prose spec is difficult to automatically test for.

Generally, the benefit of a prose, incomplete spec, to program writers, would be compatibility to an abundance of implementations (in either space or time). It helps program writers approach any number of implementers, without actually meeting any particular one. But to make this happen both must read and follow the spec. This is hard on the program writer in particular; he can't automatically test for a spec in the prose, he must read the spec and test his program with an abundance of implementations.

Will Arc's target audience follow a prose spec? Will anyone?

-----

More