Arc Forumnew | comments | leaders | submit | absz's commentslogin
6 points by absz 6487 days ago | link | parent | on: Defcall

Beautiful, nex3. Works exactly like I had been thinking about. But I'm not sure that making the table live in Arc is the best idea... it relies on a table of a certain name, which I think is slightly odd. Nevertheless, excellent--including the move of everything but functions into arc.arc. Exactly how it should work. Thank you.

Now, if I could only understand defset, I'd be all set... :)

-----

5 points by nex3 6487 days ago | link

I put the table in Arc because I was trying to make it work as closely as possible to stuff like defset and help. Although it is a little weird to have the core code relying on stuff going on in Arc-land, I think that's going to be necessary to give Arc as much power as possible.

Also, the core code only relies on call to be defined when it's actually trying to resolve a functional-position object. Doing something like (= call nil) will only fail when you actually try to use a non-function object as a function.

-----

3 points by absz 6487 days ago | link

That's a good point. And I do understand when it will fail, it just seemed odd. But you do make a good point about the power; it also reduces the number of axioms.

-----

4 points by CatDancer 6487 days ago | link

Think of it that the ideal Arc would be written entirely in Arc, except that some parts are written in Scheme for performance reasons or to allow something to actually run.

-----

7 points by kens 6487 days ago | link

I've been thinking that the Scheme code (ac.scm) should be split in two parts: an "axiomatic" part that defines car, cdr, set, annotate, etc; and a "library" part. The "library" part would define OS stuff like current-gc-milliseconds, open-socket, probably atomic-invoke, etc. There would probably be a "math" library that defines exp, sqrt, and all the missing math functions.

This structure would make it clear what parts of the language are really axioms, and what parts are in Scheme for performance, convenience, or because they are OS things that require low-level hooks.

-----

3 points by sacado 6487 days ago | link

"in Scheme for performance" : in never thought I would ever read that sentence :) (You're right, by the way)

-----

1 point by CatDancer 6487 days ago | link

Being able to call out to MzScheme from Arc would be cool. Then all the "library" parts could be written in Arc.

  (def file-exists (name)
    (scheme-istrue ((scheme-fn file-exists?) name)))
Here "scheme-fn" is a macro that returns an Arc function that calls the named Scheme function, but still expects to be passed Scheme values and returns a Scheme value. Then various functions such as "scheme-istrue" can be used to convert Arc values to Scheme values and back again.

-----

5 points by kens 6487 days ago | link

Anarki provides the $ operator, which allows callout to MzScheme.

I've been thinking that support for a foreign function interface such as SWIG would also be good.

-----

6 points by nex3 6487 days ago | link

Anarki has a macro, $, for just this. In general, I've taken to using $ for what kens refers to as the "library" part. Note, for example, files.arc.

-----

1 point by CatDancer 6487 days ago | link

Very nice!

-----


That should be doable with a new primitive and a modification to ar-apply in ac.scm. If we define a primitive called, say, behave (or rather, a much better name that I can't think of), such that

  (behave
    'foo
    (fn (self x y) (list (+ (self 0) x) (- (self 1) y)))
lets us write

  (let obj (annotate 'foo '(42 2))
    (rep (obj 5 2)))
to return (47 0), and we modify ar-apply to look for that, this should work. behave should be something like

  (define (ar-behave type fn)
    (hash-table-put! ar-behaviours type fn))
, and ar-apply would change

  ((ar-tagged? fn) (ar-apply (ar-rep fn) args))
to

  ((ar-tagged? fn) (ar-apply (hash-table-get ar-behaviours (ar-type fn)) args))
. (This is all untested.) In fact, we could even move everything but functions (and macros?) out of ar-apply and define them all in arc.arc, removing the (ar-tagged? fn) check, but this would break on the release of a new arcn.tar.

(Oh, and behave is still an awful name.)

-----

2 points by nex3 6487 days ago | link

I'm working on something like this. Should be done in half an hour or so.

-----

2 points by nex3 6487 days ago | link

It took longer than I expected, but http://www.arclanguage.org/item?id=3743.

-----

2 points by almkglor 6487 days ago | link

Here's an objection:

The main reason my settable-fn works the way it does is so that attachments are orthogonal to annotations. An object might be tagged, or it might be attached, or it might be both tagged and attached.

This means that I could have defined a function, tagged it as 'table, and provided a '= attachment for a setterfunction and a 'keys attachment for a redef'ed 'maptable and 'keys. This way, I don't have to modify, say, 'each, which will simply see that it's a 'table and pass it to 'maptable, without ever realizing that it isn't a hash table.

With this system, everything that takes an attachment must be a settable-fn (or whatever). This means that I need to modify about a dozen existing macros so that it will work with my "table", so that they will also check for a 'keys attachment, and probably I want to do this for any new macros. Sure, I could redef 'type, but this gets more complicated (and potentially risky).

So no, I don't agree with this mod, because I think attachments should be orthogonal to annotations. I don't want an object-with-attachments to have its own annotation, I want the user to specify his or her own annotation for the object. In any case, since the mod has been pushed on arc-wiki, I'll have to work around it, possibly having to redef 'type.

-----

2 points by nex3 6487 days ago | link

I'm not sure I understand... defcall doesn't really have much at all to do with annotate. It certainly doesn't change the way it works. It just allows you to make user-defined types work like functions.

In any case, it seems un-Lispy to me in general to redefine behavior by attaching things to objects. I'd rather see us redefining and tweaking the verbs rather than adding information to the nouns. This is also, I think, how PG envisioned annotate and friends working. From http://www.paulgraham.com/ilc03.html :

"If you want to overload existing operators to do the right thing when given your new type, you don't need anything new in the core. As long as you have lexical scope, you can just wrap a new definition of the operator around the old one. So if you want to modify print to display objects of your new type foo in a special way, you write something like this:

  (let orig print
    (def print (x str)
      (if (is (type x) 'foo)
          ...new code...
          (orig x str))))"
I don't know, maybe I'm being short-sighted and attaching information to objects is really necessary. But I'm not seeing a case right now where it wouldn't be just as easy (or easier, with stuff like defcall) to define something like an attached-object type and use that.

-----

1 point by almkglor 6487 days ago | link

defcall specifies how an 'annotate 'd object will work in function position. This meant that it dispatches on the claimed type of the object, not on the real type. So (at least in your original version) a type masquerading as another type will be difficult to implement:

  arc> (= test (annotate 'table (fn (x) x)))
  #3(tagged table <procedure>)
  arc> test!x
  Error: "Can't get reference"
I've since modified it so that if it's a tagged type, 'ref will perform apply on its representation. This means that currently, (= call 'type ref) will cause an object typed as 'type to dispatch as if it were its representation, while using (defcall 'type ...) will dispatch based on its type (meaning it can't be faked)

-----

3 points by nex3 6487 days ago | link

I don't think types should masquerade as other types, by which I mean they shouldn't annotate themselves with other types' symbols. I think the proper way to act like another type is to behave like that type, not to annotate yourself with that type.

The thing is, if we expect (annotate 'table (fn (x) x)) to act just like a table out of the box, we have a lot of work to do. Every table axiom has to check for an annotation and recurse if one exists.

This may not seem so bad for tables, but consider: if ((annotate 'table (fn (x) x)) 'foo) works, shouldn't ((annotate 'cons (fn (x) x)) 1)? What about (+ (annotate 'num (fn (x) x)) 2)? What does that even mean?

It seems to me that the easiest and most consistent way to mimic other types is to annotate with a new type but to redef functions like keys and defcall to work in functional position.

I urge you to check out settable-fn2.arc if you haven't already - I re-implement get- and add-attachment using this style of annotation, and it comes out quite nicely. Rather than annotating the attached functions with their types, I add a 'type attachment which overrides isa. It appears to work fine with file-table.arc, too.

-----

2 points by almkglor 6486 days ago | link

From my point of view, attachments should be orthogonal to types. Basically, an attachment is any piece of information you want to attach to an object, and lives and dies with that object. That an attachment is used to overload 'keys or '= is just a use of the attachment concept.

For example, we might want to build a reader which keeps track of line numbers. The reader's output is still 'cons and 'sym, etc., but with an attachment. Each 'cons cell has a 'linenumber attachment which we can use. For example, a macro whose syntax has been violated would be able to report the line number where this violation occurs. This is useful if the macro is used often enough and there is a need to locate the line number of the error, or if its syntax is like CL 'loop and you expect it to span several lines.

In all cases, the cons cell produced by this hypothetical reader is a cons cell. Its representation is a cons cell and is only a cons cell. However, we can extract additional data from it. After it passes through the evaluator and is discarded as trash, its attached data can be thrown away.

In any case file-table.arc only cares that settable functions work, and settable functions only care that attachments work. Whether we make attachments orthogonal to types, or separate stuff-with-attachments as types may not really matter so much anyway. This is Lisp, after all.

-----

2 points by nex3 6486 days ago | link

I agree that attachments should be orthogonal to types - that's why I added the "isa" overloading to settable-fn2. But I don't think annotations should be orthogonal.

The thing is, there's no way we'll be able to add arbitrary attachments to any object and have it continue to behave just as if there were no attachments in Arc. We'd need to either modify the core to give each object a Python/Ruby/Javascript/etc-style implicit table, which I don't think PG is likely to be very fond of (and which I don't think is a good idea besides); or we need to accept that there will be some cases where we won't be able to get attachments without a few compromises.

-----

3 points by almkglor 6486 days ago | link

Well, I thought annotations were types!

Anyway:

"Now this is the noble truth of the origin of suffering: it is this attachment which leads to renewed existence, accompanied by delight and lust, seeking delight here and there, that is, attachment to sensual pleasures, attachment to existence, attachment to extermination."

Therefore... buddha pg, please enlighten us and deliver us from attachment! ^^

-----

3 points by nex3 6486 days ago | link

Buddha PG will free us from suffering by means of s-expression Koans and well-timed whacks to the head!

-----

1 point by nex3 6487 days ago | link

Also, I just pushed a settable-fn2.arc that uses annotations how I envisioned them being used (it's also pure Arc). Hopefully that should make it more clear what I'm thinking.

-----


No; as nex3 observed, it's supposed to add it (cf. http://www.paulgraham.com/ilc03.html). Why? This is more general. Right now, we can define reptag to do what you want:

  (def reptag (typ obj)
    (annotate typ (rep obj)))
If we just had reptag, we couldn't define annotate.

Also, annotate obeys two useful identities:

  (type (annotate x y)) --> x
  (rep  (annotate x y)) --> y
However, reptag does not:

  def                      --> #3(tagged mac #<procedure>)
  (rep (annotate 'fn def)) --> #3(tagged mac #<procedure>)
  (rep (reptag   'fn def)) --> #<procedure>
Because of the type-replacing behaviour, that identity does not hold for tagged objects. I consider that a strike against it as well.

-----

2 points by almkglor 6487 days ago | link

Hmm. I suppose the real issue here is what pg intends to happen when annotating already tagged objects:

  (= y (annotate 'bar 'hmm))
  (annotate 'foo y)
3 options:

  ; option1: current Arc
  arc> (annotate 'foo y)
  #3(tagged foo #3(tagged bar hmm))
  arc> y
  #3(tagged bar hmm)
  ;option2: my proposal
  arc> (annotate 'foo y)
  #3(tagged foo hmm)
  arc> y
  #3(tagged foo hmm)
  ;option3: something else entirely
  arc> (annotate 'foo y)
  #3(tagged foo hmm)
  arc> y
  #3(tagged bar hmm)

-----

4 points by nex3 6487 days ago | link

I think it's pretty clear that he intends option 1, as that's how it actually works, and as absz pointed out, is a strict superset of the functionality of option 2 (and has nicer properties, too).

-----

1 point by absz 6488 days ago | link | parent | on: Scala style match-case

Is there any reason not to do

  (withs (ahus (uniq) bracket-sample `[,ahus])
    ...)

?

-----

1 point by cooldude127 6488 days ago | link

uniqs aren't really important since these are just functions. if they were macros, that would be another story.

-----

1 point by almkglor 6488 days ago | link

Hmm. You appear to be correct, this might be better.

-----

2 points by absz 6489 days ago | link | parent | on: Ask PG : are they axioms or core functions ?

In fact, the Anarki version of Arc* includes a redef macro:

  (redef + args
    (if (no (keep [is (type _) 'mytype] args))
      (apply old args)
      (frob args)))
This also prevents "* * * redefining +" from being printed.

* EDIT: Used to just say "Arc," but that was wrong.

-----

5 points by kens 6489 days ago | link

I think the redef macro is in the Anarki version, not official Arc

As an aside, has Paul Graham shown any enthusiasm for the Anarki changes?

-----

5 points by sacado 6489 days ago | link

"As an aside, has Paul Graham shown any enthusiasm for the Anarki changes?"

Obviously not.

-----

3 points by cchooper 6489 days ago | link

I can't blame him. Cutting bloat in the language core is clearly a goal. Nothing should go into the official Arc release unless it has proven its value in real code (which is basically News.YC at the moment).

-----

6 points by sacado 6489 days ago | link

Sure, he has to keep the control over the things. The point is that a few bug fixes (the mkdir problem for example), simple conveniences (see arc.sh) and even trivial optimisations (arc< to name it) available in anarki are of interest even in the official release. I'm not talking about experimental stuff (infix numeric notation, vectors, standalone exe, maybe docstrings, experimental module systems, ...)

But I think the few fixes and conveniences should really be taken into consideration. I can't believe none of them are of interest.

-----

2 points by cchooper 6489 days ago | link

They probably are of interest, but let's not forget he's running a company in his spare time, and 3 releases in about 3 weeks is a pretty good work rate.

Of course, as someone running Arc in Windows, I'd love it if a bit more stuff worked out of the box (e.g. the blog, which didn't work in Arc1 IIRC). That's why I'm probably going to switch to developing on Anarki and then testing it on vanilla Arc afterwards.

-----

3 points by sacado 6489 days ago | link

Note that I don't criticize Paul's attitude there. 3 releases in less than a month is much more than what I expected. He didn't release early, but at least he releases often :) I just meant a few things would deserve a little more consideration, at least in the next few weeks/months ?

-----

3 points by cchooper 6488 days ago | link

I'm hoping things will speed up now that the News.YC code is in.

-----

2 points by jbert 6487 days ago | link

You might increase the chances of stuff getting merged back into PG's arc if you separate the different types of changes into different git branches.

That reduces the burden of code review/merging/demerging on the person you'd like to pull the changes (PG).

e.g. have a bugfix branch containing only "obvious" fixes.

It can be difficult to disentangle a bunch of different changes (what depends on what). That's a barrier to adoption.

-----

3 points by byronsalty 6489 days ago | link

Somebody is reading my mind.

-----

1 point by absz 6489 days ago | link

So it is. Thanks.

That's a good question--I'm not really sure, since I just use the Anarki.

-----

2 points by eds 6489 days ago | link

Wow, that's really nice. I think I'll use it in the definition of infix.arc.

-----

2 points by absz 6489 days ago | link | parent | on: ++ nil -> 1 via arithmetical plus

That would be interesting, but would change the current semantics. Right now:

  arc> (= s "abc") 
  "abc"
  arc> (= l nil)
  nil
  arc> (++ s "4")
  "abc4"
  arc> (++ l '(a))
  (a)
I don't know if that's a good or a bad thing, but it's worth remembering.

-----

8 points by absz 6489 days ago | link | parent | on: Lazy evaluation in Arc

On some level, the answer is "yes, of course."

  (mac delay body
    `(annotate 'promise (fn () ,@body)))
  
  (def force (promise)
    ((rep promise)))
This was lightly adapted from http://cadrlife.blogspot.com/2008/02/lazy-lists-in-arc.html which I found on this forum -- read it for a more complete version with various supporting functions, etc. It's not primitive lazy evaluation, but it should work for many of the same things that delay/force work for.

-----

1 point by cooldude127 6489 days ago | link

i knew i had seen it done for arc already. it was that article.

-----

3 points by absz 6490 days ago | link | parent | on: ~Tilde and macros

(1) Technically, it's the same as (complement f), which I believe is roughly equivalent.

(2) Because it's nicer-looking, especially with things like keep.

-----

4 points by aston 6489 days ago | link

But, unlike ~ and complement, no:litmatch works.

-----

8 points by pg 6489 days ago | link

no:litmatch only works in functional position, because (no:litmatch ...) is transformed in ac into (no (litmatch ...)).

To add to the confusion, it's a bug that ~litmatch doesn't work in functional position. There's a comment reminding me to fix that in ac.scm.

-----

1 point by parenthesis 6489 days ago | link

Wouldn't it make more sense for (~foo ...) to expand into (no (foo ...)) ?

-----

2 points by absz 6489 days ago | link

Not really, because that would make (keep ~foo lst) meaningless, and that's an important use of ~.

-----

2 points by cooldude127 6489 days ago | link

complement only works with functions. behind the scenes, it uses apply to call the original function. at first glance, i don't really see a reason that it couldn't just do the same as (compose no _)

-----

2 points by eds 6489 days ago | link

I know compose works on macros... but I'm not sure you are straight on, because compose uses apply as well. And the macro expansions look exactly the same.

  arc> (macex '(compose no litmatch))
  (fn gs1639 (no (apply litmatch gs1639)))
  arc> (macex '(complement litmatch))
  (fn gs1641 (no (apply litmatch gs1641)))
  arc> ((compose no litmatch) "a" "aston")
  nil
  arc> ((complement litmatch) "a" "aston")
  Error: "vector-ref: expects type <non-negative exact integer> as 2nd argument,
  given: \"a\"; other arguments were: #3(tagged mac #<procedure>)"
So what is going on here?

-----

3 points by cooldude127 6489 days ago | link

except calls to compose are optimized away in the interpreter. they are treated specially. look in the `ac' function in ac.scm. i'm guessing this is the reason.

-----

1 point by absz 6489 days ago | link

Ah! That must be the problem, yes. Good catch!

Now, what to do about the other case... there's still no reason it should bug out, it seems to me. Or rather, it should be implementable in such a way that it shouldn't.

-----

2 points by cooldude127 6489 days ago | link

mainly, we need an apply that works for macros. why would that not be possible, i wonder.

-----

5 points by pg 6489 days ago | link

What you're thinking of can be done, but only by calling eval explicitly. I.e. what you could do thus with or if it were a function:

  (apply or args)
you have to do thus with the real or, which is a macro:

  (eval (cons or args))
This is not surprising, because macros live in the world of expressions, not values.

-----

3 points by aston 6489 days ago | link

What you mean is, we need macros to be first class.

-----

1 point by absz 6489 days ago | link

Yes, we do. But if I recall (and I can't test, because the git "wiki" broke for later mzschemes again), apply does work for macros, except it evaluates all the arguments first, which breaks things like and. Someone should test that, though; as I noted, I can't.

EDIT: No, sorry. It doesn't work. My bad.

-----

4 points by nex3 6489 days ago | link

Just as a note, the wiki is fixed now.

-----

1 point by absz 6489 days ago | link

I saw, that's how I was able to test. Thank you for setting it up, by the way! And if you fixed it, thank you for that; if someone else did, thank them for that.

-----

3 points by nex3 6489 days ago | link

You're welcome :-). It would be wfarr who fixed this issue, though.

-----

1 point by cooldude127 6489 days ago | link

well, yeah. basically.

-----

1 point by absz 6489 days ago | link

Compose does something very similar behind the scenes as well, in fact. So there's no reason it should work either.

-----

1 point by absz 6490 days ago | link | parent | on: Suggestion: constructors

I'm fairly inexperienced with pure functional programming à la Haskell and ML, but isn't this a weaker variant of tagged unions? Tagged unions are (in my mind) fantastic, but what you have allows only one tagged union in the entire program. It would be nice to have Haskell's "maybe" union, which could be (just x) or (nothing). Then you could also have, say, a binary tree type* which could be (node x) or (branch btree-left btree-right), and they would be different things.

* Yes, I know that lists can represent binary trees. Yes, that's often better. However, I needed an example.

-----

1 point by absz 6490 days ago | link | parent | on: A macro for creating infix DSLs

Oooh, I like this! That's a very nice way to handle infix. Minor nitpick, though: why not just have arith be

  (mac arith body
    `(let ^ expt
       (infix ((+ 1 left) (- 1 left) (* 2 left) (/ 2 left) (^ 3 left)) ,@body)))
? That way you also get floating point and complex exponents for free.

-----

3 points by cchooper 6490 days ago | link

Because I couldn't find the expt function. D'oh!

I'll edit the above to use it.

Hmmm... where did the edit button go?

-----

4 points by absz 6490 days ago | link

Editing is time-limited; I can't decide if that's a good or a bad thing.

-----

More