Arc Forumnew | comments | leaders | submitlogin
2 points by Pauan 5102 days ago | link | parent | on: Eval in JavaScript uses local scope.

Yes, I tried that but I found it to be significantly slower than using eval. It also requires you to use "return" if you want to actually return the value, whereas eval returns it automatically.
1 point by rocketnia 5102 days ago | link | parent | on: Eval in JavaScript uses local scope.

If all you want to do is pass arbitrary values into some evaluated code, Function() works for that, and it doesn't use the local scope:

  var foo = (function () {
      var n = {};
      return function ( s ) {
          return Function( "n",
              "\"use strict\";\n" +
              "return (\n" +
              s + "\n" +
              ");\n" +
              "//@ sourceURL=foo"
          )( n );
      };
  })();
  
  foo( "n.foo = 5" );
  foo( "n.foo + 10" );
Technically this implementation accepts a different segment of JavaScript syntax than yours does, but I doubt you care about that in particular.

It's nice that JavaScript provides a way to evaluate code in a local scope, but so far I haven't had much excuse to do that. Occasionally, I do insert an eval( "" ) just to force a closure to capture every single variable in its surrounding lexical scope, since that makes it possible to interact with all those variables when paused there in the Chrome Debugger.

1 point by Pauan 5102 days ago | link | parent | on: Eval in JavaScript uses local scope.

Another fun fact: in strict mode, variables created by "eval" are scoped to the call to "eval":

  (function () {
    "use strict";
    console.log(eval("var x = 5; x"));
    console.log(eval("x"));
  })()
The above will first print 5, and will then throw an error saying that "x" is undefined. This provides another technique to create hidden variables.

If the above code wasn't in strict mode, it would have printed 5 both times, and the variable "x" would have been local to the function block:

  (function () {
    console.log(eval("var x = 5; x"));
    console.log(eval("x"));
    return x;
  })()

"It perplexed me that you didn't take that point of view in the first place. :) From that blog post, Ruby's refinements strike me as a snowball of complexity arising from the existing use of monkey patching in the wild. Nulan has no entrenched user base and no monkey patching mechanism (that I can see offhand), let alone a culture of monkey patching, so you have little to worry about. :-p"

Because I was still mulling over the particular problems that that blog post mentioned, and trying to figure out if they applied to Nulan or not. And yes, it does strike me as overly complex, but then again, Ruby already has classes + mixins + 2 or more forms of eval + instance-specific anonymous classes + like 5 different kinds of blocks...

---

"Actually, since Nulan's an fexpr language, its programmers potentially have to second-guess how every single expression will be evaluated. XD Yes you may have idioms to manage this paranoia, but that doesn't make Nulan exempt to the blog post's point, since Ruby could develop idioms for this too."

I'm actually changing Nulan to use macros, for the sake of speed and to make it easy to compile to JS. But yes, fexprs do cause that same sort of second-guessing. And as both you and I pointed out, Nulan is vulnerable, but the idioms are different. In particular, hyper-static scope helps a lot I think.

---

"I wonder if Ruby could use something like ECMAScript 6's private names instead.[1] The method name itself would be looked up from the lexical scope, neatly letting the lexical context play a part in resolving a dynamically dispatched method call."

Well, that's pretty much exactly Nulan's "unique types" system. Not that I'm saying it'd be a bad thing for Ruby to implement that. And I'm actually quite pleased that that system is on the table for JS. I really do feel it's the most flexible type system while still remaining hygienic.


I wonder if Ruby could use something like ECMAScript 6's private names instead.[1] The method name itself would be looked up from the lexical scope, neatly letting the lexical context play a part in resolving a dynamically dispatched method call.

I could be missing something big here. I don't really know all the required use cases, having hardly used Ruby myself.

[1] http://wiki.ecmascript.org/doku.php?id=strawman:private_name...


"In any case, I feel that due to Nulan's different idioms and such, the kinds of problems Ruby has generally aren't an issue."

It perplexed me that you didn't take that point of view in the first place. :) From that blog post, Ruby's refinements strike me as a snowball of complexity arising from the existing use of monkey patching in the wild. Nulan has no entrenched user base and no monkey patching mechanism (that I can see offhand), let alone a culture of monkey patching, so you have little to worry about. :-p

---

"It's not idiomatic to assign to variables [in Nulan]."

Well, I wonder what Ruby idioms were like before Rails came along! EDSLs often use every readability-enhancing language feature they can get their paws on, regardless of the intended stability of those features.

I'm a culprit of this. Consider my Arc module system, where namespaces work by assigning macros to the global scope, calling 'eval on a module body, and removing the macros again. When my module system didn't work in Jarc and Rainbow, I considered Jarc and Rainbow to be nonconformant, even after jazzdev and conanite fixed most of the obvious bugs and probably conformed to pg's intended design about as well as pg-Arc did. :)

---

Blog post: "It forces every Ruby programmer to second-guess every block of code they pass into someone else's library or someone else's method call."

You: "I'm fairly sure Nulan avoids [this issue]"

Actually, since Nulan's an fexpr language, its programmers potentially have to second-guess how every single expression will be evaluated. XD Yes you may have idioms to manage this paranoia, but that doesn't make Nulan exempt to the blog post's point, since Ruby could develop idioms for this too.

---

"[...]but I think this kind of "magical action at a distance" is consistent with Ruby's hyper-liberal viewpoint."

Yeah, when the blog post talks about a very dynamic language and warns about how certain features might threaten to break its local static invariants, there's a sense of whiplash there. :-p But it does make sense to care about local static reasoning in this case, since that's (at least informally) what refinements are supposed to bring to monkey patching in the first place.


Hm, thinking about it some more... Nulan does technically suffer from the same problems as Ruby, because of mutable variables. I took this example (https://gist.github.com/4110634#file_ref_8.rb) and tried rewriting it in Nulan style:

  # library1
  (types %inject)

  (def (add-all [ %inject f ])
    (f "" -> str acc (+ acc str)))

  # application
  (import library1)

  (def (my-+ x y) "@x plus @y")

  (def (my-array)
    [ %inject -> str f
                (do (each {"foo" "bar" "qux"} -> x
                      (set! str (let! + my-+ (f x str))))
                    str) ])
In this case, we're using "let!" to dynamically rebind "+" to "my-+". I think this is okay, though, for two reasons:

1) It's not idiomatic to assign to variables. Instead, it's idiomatic to use unique types to create extensible functionality. Mutability is discouraged in general (though allowed).

2) It's also possible to protect against such things, if you really want to. For instance, you could do this:

  # library1
  (types %inject)

  (def (add-all [ %inject f ])
    (f "" -> str acc (+ acc str)))

  (var + +)
This is clunky, though, having to manually make everything safe to use. If I really wanted to, I could make a macro that automatically protects against such things:

  # library1
  (protect-all
    (types %inject)

    (def (add-all [ %inject f ])
      (f "" -> str acc (+ acc str))))
But I, personally, don't see much reason for that since as point 1 said, it's not idiomatic to assign to variables.

Of course, I guess the same argument could be used for Ruby... if refinements aren't idiomatic, then it's not a big deal, right? Well, except that mutable monkey-patching actually is idiomatic in Ruby, refinements are just a way to scope them.

In any case, I feel that due to Nulan's different idioms and such, the kinds of problems Ruby has generally aren't an issue.


Cool. I wondered when Ruby would get something like that.

Thankfully, I believe Nulan avoids most of that issue by A) using global names, so you could simply define a new "camelize" function, no need to attach it to the String class, and B) a combination between mutable variables/hyper static scope/unique types/Arc-style "extend" to control extensibility.

I'd be interested in seeing some examples where Ruby's refinements are better than Nulan's system.

---

Some interesting points:

"Because there's no "using" anywhere in the code, and we're not extending some other class, most folks will assume we're simply concatenating strings here. After all, why would I expect my "+" call to do something else? Why should my "+" call ever do something else here?"

"The "module_eval" behavior of refinements simply goes too far. It forces every Ruby programmer to second-guess every block of code they pass into someone else's library or someone else's method call. The guarantees of standard method dispatch no longer apply; you need to know if the method you're calling will change what calls your code makes. You need to understand the internal details of the target method. That's a terrible, terrible thing to do to Rubyists."

I'm fairly sure Nulan avoids both these issues, but I think this kind of "magical action at a distance" is consistent with Ruby's hyper-liberal viewpoint.

1 point by Pauan 5104 days ago | link | parent | on: Libraries suck

Yes.
1 point by rocketnia 5105 days ago | link | parent | on: Libraries suck

"I did it because I want to eventually compile to JS, and JS only has 64-bit floating point."

JavaScript supports a 52-bit mantissa. Are you willing to have that discrepancy of one bit of precision?

1 point by Pauan 5106 days ago | link | parent | on: Libraries suck

I "solved" this issue by simply making all numbers 64-bit floating point. The change was really easy to make. I didn't do it because of this issue, though. I did it because I want to eventually compile to JS, and JS only has 64-bit floating point.

Though, it also makes it easier conceptually to only have to worry about floating point, as compared to floating point + integer + rational + complex + whatever else Racket has. Even if you divide numbers into exact/inexact like Racket does, that's still more complicated than only having a single numeric type.

2 points by rocketnia 5107 days ago | link | parent | on: Libraries suck

"I dunno, what happens in Racket?"

Actually, this is going a bit off topic, since it doesn't especially matter to me whether exact integers are a subset of inexact numbers. I thought I even deleted this part of my reply, but oh well. XD

Looks like the racket docs say[1] Racket's inexact numbers are IEEE floating-point numbers of single or double precision, with double being the default. So they don't preserve integers beyond 53 bits:

  > (define foo 12345678901234567890)
  > foo
  12345678901234567890
  > (inexact->exact (exact->inexact foo))
  12345678901234569216
  > (number->string foo 2)
  "1010101101010100101010011000110011101011000111110000101011010010"
  > (number->string (inexact->exact (exact->inexact foo)) 2)
  "1010101101010100101010011000110011101011000111110001000000000000"
Odd, this suggests Racket double-precision floating-point numbers have only 51 bits of mantissa rather than 52, so maybe they actually only preserve integers up to 52 bits...?

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

1 point by Pauan 5108 days ago | link | parent | on: Libraries suck

"Does Nulan give access to 'write? That would distinguish them."

Not if I change printing to always use floating point, or print exact floating points as integers. :P I actually did something like that with Arc/Nu: I was tired of typing in (/ 1 5) and getting back 1/5, so I changed Arc/Nu to print exact numbers as if they were inexact, so it would print 0.2. This only affects the printing, not the actual math stuff.

---

"I don't know about that. What happens with very large, but exact, integers?"

I dunno, what happens in Racket?

---

"Similarly, in JavaScript I tend to use Arrays of strings, storing them as their ECMAScript 5 JSON representation."

Ah yeah, that works, I didn't think of that.

1 point by rocketnia 5108 days ago | link | parent | on: Libraries suck

Does Nulan give access to 'write? That would distinguish them.

"I prefer to view integers as being a subset of floating point[...]"

I don't know about that. What happens with very large, but exact, integers?

---

"[...]so I prefer to see 1 and 1.0 as identical. Is there a reason not to?"

Actually I think there is a reason. We're talking about exact and inexact numbers here. If you want to know that a number is exactly 1, then 1.0 doesn't give you enough information. :-p

Personally, I think floating point numbers mostly get in the way. I'd rather treat them as a separate concept, even if it means using a special +. operator like OCaml.

I don't see much need for complex numbers, rational numbers, or negative numbers either, but at least their addition and multiplication are still associative.

2 points by rocketnia 5108 days ago | link | parent | on: Libraries suck

Lol, yep. The way I "implement" immutable lists in practical programming is by not modifying them. I rationalize that their static type would enforce this if only there were a type system. :-p

More specifically...

In Arc I can't really rely on using lists as keys, since (IIRC) Jarc converts table keys to strings using the same behavior as 'write. However, in practice I have used lists of alphanumeric symbols, since those have a predictable 'write representation on Jarc, a predictable Racket 'equal? behavior on pg-Arc, etc.

Similarly, in JavaScript I tend to use Arrays of strings, storing them as their ECMAScript 5 JSON representation.

  myTable[ JSON.stringify( k ) ] = v;
3 points by akkartik 5108 days ago | link | parent | on: Libraries suck

Ha, here rocketnia is on my side! I don't need permission from racket or arc or javascript to make a list immutable. I just have to not modify it!
2 points by akkartik 5108 days ago | link | parent | on: Libraries suck

Yeah, sorry the names have been so useless in penetrating my meaning. I'm planning to write a followup without ever using the words 'abstraction', 'library' or 'service'. Libraries don't have to suck, if you know about how they work. My definition of abstraction was that you had to know something about how it was implemented. It's just that in the real world we don't know how 99% of our libraries work, and our entire eco-system encourages that, and library writers don't care to make their implementations easy to understand because, "Who does that, go read about how it works? Just a couple of guys who'd figure it out anyway, even if I gave them no documentation."

To answer your question, the first example that pops to mind is file uploads in arc. Let's consider a parallel universe where arc came with multi-part file upload support, and it worked for you out of the box, and you never came here with questions, and I never delved into how it worked. In that scenario the file-upload features in arc would be a service to me, not an abstraction. But in this universe all those things happened[1], and now file-upload is an abstraction for me. I know I can get into its guts if I find something wrong. I've become a lion when it comes to file upload (http://paulgraham.com/boss.html).

Notice that this would be true even if the code I wrote for it were identical to the code the authors of arc would have written (ha!). This isn't a technical argument but a social one: Programmers should learn about their dependencies. Or put another way: Ask not if something is an abstraction. Ask if it is an abstraction to you.

(Did that make sense?)

[1] http://arclanguage.org/item?id=16224

1 point by Pauan 5108 days ago | link | parent | on: Libraries suck

Basically. You could drop down and use Racket's "fixnum?" and "integer?" functions, but Nulan doesn't provide them. If you find any way to distinguish them, I'd like to hear it.

I prefer to view integers as being a subset of floating point, so I prefer to see 1 and 1.0 as identical. Is there a reason not to?

1 point by Pauan 5108 days ago | link | parent | on: Libraries suck

"Even when I'm programming normally in Arc or JavaScript, I use tables with immutable list keys all the time. It doesn't seem unusual to me."

You silly. Neither JavaScript nor Arc have immutable lists. Well, I guess you could use Object.freeze on an array in JS, but... even if you did that, it would just serialize the array to a string.

2 points by lark 5108 days ago | link | parent | on: Libraries suck

Can you provide an example of why a library sucks while an abstraction wouldn't?
1 point by rocketnia 5108 days ago | link | parent | on: Libraries suck

"I personally haven't."

Oh, well then welcome to the club. XD

I looked around for where I talked about this before, and I guess I was mainly thinking about this time: http://arclanguage.org/item?id=13701

I touched upon it in at least one other conversation after that: http://arclanguage.org/item?id=14596

However, I can't find any more than that. Maybe I just took it for granted at that point.

It is the policy I applied in Cairntaker. Cairntaker interns all immutable tables so they can be compared for deep equality even after some of their entries have been garbage-collected. Equality support is essential in Cairntaker since any value can be used as a table key.

1 point by Pauan 5108 days ago | link | parent | on: Libraries suck

"This is a matter where Pauan and I agree on the ideal semantics completely."

Quite the rare thing indeed.

---

"I seem to remember us advocating egal a few other times (though I don't think I've ever called it "egal")."

I personally haven't. I only recently started to actually care about egal because of Nulan using immutable objects.

I do remember us having discussions before about making "iso" the default equality in Arc, which is what wart does, but I don't recall us talking about cons mutability.

2 points by rocketnia 5108 days ago | link | parent | on: Namespaces plox

"They're using it as an example , that's all."

Oh, I can understand that point of view. Just because there were multiple examples doesn't mean each one introduced new requirements.

---

"It's obvious that you want to think in terms of package managers, and that's fine, but I really do not get the impression that's what they're talking about."

I just wanted to help you elaborate on the claims you're making about Nulan's namespace system. You originally quoted Casey McCann's examples, but I perceived them differently than you did, and I had trouble understanding your perception from your terse example code.

Now this seems to be resolved. We may not agree in our interpretations, but now I understand your claims relative to both of them. Thanks for your patience with me. ^_^

---

"Ultimately, it seems the only way to resolve this is to actually ask them what they meant."

I don't think that's necessary, but it would be interesting to know.

2 points by akkartik 5108 days ago | link | parent | on: Libraries suck

Yeah I take that back about immutable list keys :)

This is all most interesting. I'm going to keep an eye out for nice programs that exploit structural equality on mutable structures. Y'all should do the same!

1 point by akkartik 5108 days ago | link | parent | on: Libraries suck

Yeah, I didn't fully understand egal when I said I agreed with it if the language distinguishes immutable objects. I want my equality to be more lenient in comparing mutable objects. Even with mutable objects it seems a lot more common that I just care about structural equality rather than identity.
1 point by Pauan 5108 days ago | link | parent | on: Libraries suck

"This still feels like a crazy abstraction. If two mutable objects look the same I want equality to say they are the same by default, not go into contortions to cover its ass because I might choose to mutate them and I might then care about reference equality."

Then make the objects immutable. Then equal makes sense. Then the compiler/interpreter knows what your intent is. Then you avoid all these issues. Then you don't even need a distinction between eq/equal: you can just use egal.

This seems to me to be the obvious choice, but it seems we disagree.

2 points by rocketnia 5108 days ago | link | parent | on: Libraries suck

"The only new drawback I learned about was that equal can't handle cycles. I hadn't thought of that. It's not hard to fix, but the obvious fix costs performance every single time."

Just as a point of interest, Racket handles this with its built-in types:

  > (define foo (mlist 1))
  > (set-mcdr! foo foo)
  > foo
  #0=(mcons 1 #0#)
  > (define bar (mlist 1 1))
  > (set-mcdr! (mcdr bar) bar)
  > bar
  #0=(mcons 1 (mcons 1 #0#))
  > (map (lambda (f) (f foo bar)) (list equal? eqv? eq?))
  '(#t #f #f)
Racket also allows 'equal? to have custom behavior for user-defined types, and the custom behavior can handle cycles too. (http://docs.racket-lang.org/reference/booleans.html#(def._((...)

I brought this up briefly at http://www.arclanguage.org/item?id=12860.

2 points by rocketnia 5108 days ago | link | parent | on: Libraries suck

This is a matter where Pauan and I agree on the ideal semantics completely. I seem to remember us advocating egal a few other times (though I don't think I've ever called it "egal").

That said, I don't care if this functionality is provided as a two-argument function or as (addr ...).

---

"Yeah, using lists and hashes as hash keys is kinda crazy. And mutating such hash keys is utterly crazy."

I built Cairntaker on the idealistic notion that every single first-class value is a mutable or immutable weak table. That includes the table keys. Cairntaker does make compromises for performance and FFI, but none that betray this ideal.

Even when I'm programming normally in Arc or JavaScript, I use tables with immutable list keys all the time. It doesn't seem unusual to me.

2 points by rocketnia 5108 days ago | link | parent | on: Libraries suck

Within Nulan, is it impossible to distinguish between 1 and 1.0? Your definition treats those as identical, but that's okay if they really are.
2 points by akkartik 5109 days ago | link | parent | on: Libraries suck

Thanks for all that detail! Yeah, using lists and hashes as hash keys is kinda crazy. And mutating such hash keys is utterly crazy.

I think the crux is this: "..it is possible to distinguish two mutable objects by mutating one of them and checking to see if the same change occurs in the other object."

This still feels like a crazy abstraction. If two mutable objects look the same I want equality to say they are the same by default, not go into contortions to cover its ass because I might choose to mutate them and I might then care about reference equality.

However I have a lot more respect after your comment for how egal can simplify lots of thorny concurrency issues.

More