Arc Forumnew | comments | leaders | submitlogin
1 point by Pauan 4931 days ago | link | parent

  (letr self (object ...)
    self)
If this pattern is common enough, it shouldn't be hard to make a macro for it. In fact, that'd be a good idea, to provide a w/object macro:

  (w/object self ...)


1 point by Pauan 4931 days ago | link

https://github.com/Pauan/ar/commit/6ea1169af4fa08d37c2d0a35f...

You can now use w/object to refer to the object. If you want finer-grained control, use let, letr, or whatever else you would normally use.

---

Question: should it be like afn and preselect the name "self" for you, or should it allow you to name it whatever you want? You can already use letr to give it a custom name, so I think it makes sense for w/object to preselect the name, like afn.

Of course, then it should probably be called "aobject" so it follows the same pattern as "afn" and "aif", but I think that looks funky. A better suggestion for naming conventions would be great.

Alternatively, I could have "object" automatically select the name "self", and then have a different macro that wouldn't name it. That's probably the best way, since it's really frequent to want to refer to the object from inside the object's methods.

---

Or I could go the Python route and make it so the first parameter of methods is "self":

  (object ... (fn (self ...) ...))
But I honestly don't like that, mostly because of the extra verbosity. I'd rather use "letr" to bind it once, rather than putting an additional argument into every single method... However, there are some nice benefits with this approach. Let's say you define a function:

  (def x (self) ...)
You can then slap it onto an object easily:

  (object x x)
This would also allow you to easily use methods from other objects:

  (= foo (object x (fn (self) ...)))
  (= bar (object x foo<-x))
And it would allow you to call one object's methods on a different object:

  (= foo (object x (fn (self) ...)))
  (= bar (object))

  (foo<-x bar)  ; self is now bar, rather than foo
In a sense, by having self as the first argument to methods, it's sorta-dynamic, rather than lexical. That's useful, but I dislike the verbosity. A way to get the same dynamicism while being less verbose would be great. I don't think ar's dynamic variables will quite work. I mean, yes, they'd work, but it'd still be too verbose for my liking.

---

P.S. If I understand correctly, ar does actually have extensible ssyntax... so I can try and add that in. That'll make it so much nicer to deal with objects. Here's a comparison. The first is the current way, and the second is how it would look with ssyntax:

  (get-attribute foo 'x)
  foo<-x

  (set-attribute foo 'x ...)
  (= foo<-x ...)

  (del-attribute foo 'x)
  (del foo<-x)

-----

1 point by Pauan 4931 days ago | link

Hm... I gave it some thought, and ar's dynamic variables might actually be okay. It'll make certain things a little bit clunkier, though.

For starters, there would be an implicit "self" variable. Rather than using "letr", "object" would use "w/self". The problem with doing that, is then you would need to call w/self yourself:

  (= foo (object ...))

  (w/self foo ...)
Why? Well, let's say foo grabs an attribute from bar:

  (= bar (object x (fn ...)))
  (= foo (object x bar<-x))
If you try to call foo<-x, the self parameterization will be all screwy, so you'll need to use w/self to fix that. One way to fix this would be to have a "bind" function or similar:

  (= foo (object x bind:bar<-x))
Clunky, but probably still better than giving every method a "self" attribute... I could even try making it so the object constructor automatically binds... that might work. In any case, it would be kinda hacky and clunky, but if it worked, we could get very concise code, while still gaining the benefits of pseudo-dynamic scope[1].

---

* [1]: I'm calling it pseudo-dynamic because I think it's mixing dynamic/lexical together. "self" should by default always be scoped to the object that is currently being used, but it should be possible to override that with "w/self".

Things get more complicated when an object's method calls a different object's method, or when you introduce things like inheritance... I can tell you how I would expect it to behave, the complicated part is actually making it behave that way.

Putting in a "self" parameter for all methods would basically solve this, but at the cost of verbosity, and a couple other things. For instance, you wouldn't be able to use existing functions like "is" or whatever as methods, because their argument list would be different.

So you'd have two types of functions: those designed for objects, and those that aren't. I think it'd be better to not have that kind of separation.

-----

1 point by rocketnia 4931 days ago | link

"Things get more complicated when an object's method calls a different object's method, or when you introduce things like inheritance... I can tell you how I would expect it to behave, the complicated part is actually making it behave that way."

This is where something like Lathe's secretargs might come in handy. If we made a secretarg for "the object the function is invoked on", then it's like every function in the language has it as a lexical parameter. Traditional function calls just pass a default value, and traditional functions just ignore it; you have to use special-purpose forms to define secretarg-aware functions and pass those arguments in. Is this close to what you have in mind?

A failure secretarg is how I hacked in failcall to Arc--and now JavaScript--and I'd also make a secretarg if I ever felt the need to implement keyword arguments. (Secretargs are essentially first-class keyword args without names, but one of them can hold a more traditional keyword table or alist.) The catch is that things like Arc's 'memo and JavaScript's Function#bind() aren't written with secretargs in mind, so they're not especially seamless with the language. But I think it would be cool to add secretargs to ar on a more core level. ^_^

Here's where I originally talked about secretargs: http://arclanguage.org/item?id=13825

And here are my implementations in Arc and JavaScript:

https://github.com/rocketnia/lathe/blob/master/arc/dyn.arc (search for "secretarg")

https://github.com/rocketnia/lathe/blob/master/js/lathe.js (search for "sarg")

-----

1 point by Pauan 4930 days ago | link

"If we made a secretarg for "the object the function is invoked on", then it's like every function in the language has it as a lexical parameter."

Hello dynamic variables!

---

"Is this close to what you have in mind?"

Sorta. The idea is close, but your way looks too verbose. I want it to be really short and simple, so having to define methods in a special way does not sound good to me. I want them to be plain-old functions.

I have an idea for what I'm going to do, and I think it'll work, because in ar, lexical variables always take precedence over dynamic variables, from what I can tell.

By the way, right now I only care about ar compatibility, since ar has nifty stuff like dynamic variables, which I've come to love. This not only should make the library shorter and easier to understand, but I honestly can't care much about compatibility, because my library kinda needs defcall, which isn't standardized[1] (and isn't even in Arc/3.1 at all)

---

* [1]: Or so I hear. I don't actually know.

-----

1 point by rocketnia 4930 days ago | link

"Hello dynamic variables!"

Secretargs happen to be implemented in terms of dynamic boxes. The benefit of secretargs is that you can always pass the default value just by making a normal function call; you don't risk propagating the existing dynamic value into the call. Whether that's important in this case is up to you. ^_^

Oh, and I don't think we can say anything's standardized in Arc at all. :-p The standards are just implicit assumptions around these parts.

-----

1 point by Pauan 4930 days ago | link

Note: when I said "standardized" I meant in the "de facto standard" way. Arc has plenty of those. :P

I agree that Arc has essentially no de jure standards[1], and I think that's a good thing.

---

* [1] The closest thing would probably be whatever pg does, but that would only concern the question of what official Arc is; other implementations would be free to either follow official Arc, or branch off into a different Arc-like dialect.

-----

1 point by akkartik 4931 days ago | link

I'm talking about inside the print method you mentioned. Say I want to print the length of the table inside print(), how would I get that?

-----

1 point by rocketnia 4931 days ago | link

I assume Pauan meant you'd change this...

  (object print (fn () "%custom printing!%"))
...to something like this:

  (letr self (object)
    (set-attribute self 'print
      (fn () (+ "#<table of length " len.self ">"))))
At least in the short term, 'til there's some kind of 'w/object to help out. ^_^

-----

1 point by Pauan 4930 days ago | link

No no no. I mean, yes, your way will work, but you can just use letr. No need to use set-attribute:

  (letr self (object print (fn () (+ "#<table of length " len.self ">")))
    self)
You can use set-attribute if you want to, but there's no need, since letr behaves like Scheme's letrec.

And soon I'll try changing it so you don't even need the letr:

  (object print (fn () (+ "#<table of length " len.self ">")))

-----

2 points by rocketnia 4930 days ago | link

Ohh, okay! Sorry, for some reason I thought 'letr was another name for 'ret or something. For some reason 'letrec didn't cross my mind. XD

In this kind of situation I use a utility I call 'named:

  (named foo bar)
  -->
  (ret foo nil (= foo bar))

-----

1 point by akkartik 4930 days ago | link

Ah!

What was that word I used to tactfully criticize your writing those many months ago? Well, Pauan is making you look good >;)

Another way to say that: you've taken the trouble to cater to my idiosyncratic dyslexias. Many thanks!

-----