Arc Forumnew | comments | leaders | submitlogin

IMHO, it's awesome. Do you guys know when it will come out?

He is a YC employee.

From kogir's YC profile: "I work at YC on Hacker News (and other things)." https://news.ycombinator.com/user?id=kogir

2 points by Twoflower 4877 days ago | link | parent | on: Still under development?

Thanks! :D

https://plus.google.com/115420443999474491901/posts/dF12ruJc...

Hm. I didn't realize my name was Gabriel Scherer and I had already made a comment.

That's handy, because it saves me the trouble of writing it all out.


Also, I just noticed that the same user who has committed to the HN github has a port of Arc to be a Racket language: https://github.com/kogir/rarc-public . Do we know if he's a YC employee? Is this the next version of Arc?

I originally found this on HN (https://news.ycombinator.com/item?id=5979066), but this can be verified by going to the bottom of any HN page and clicking "bugs". I'm excited for the possibility of new releases of Arc.

Both '= and 'def can modify a variable in the caller's local scope. This is something a normal function can't do.

  (def my-len (lst)
    
    ; Introduce a local variable `n'.
    (let n 0
      
      (each element lst
        
        ; Modify that variable.
        (= n (+ 1 n)))
      
      n))
(Technically 'def only seems to be intended for the global scope. It prints a warning when the global variable is already bound, even if it actually modifies a local variable.)

---

"Is there a deeper reason for these irregularities, or is it just convenience?"

The syntax of languages like Arc, Lisp and Scheme is actually a bit more irregular than it looks: The code (a b c) usually means to do a function call, but this isn't true if "a" is the name of a special form or a macro. Special forms are how the language provides structured syntaxes other than function calls. Macros are user-defined special-form-like syntaxes that specify a way to translate ("macroexpand") their syntax into some already-supported code.

In Arc, both '= and 'def are macros provided with the language. They act as shorthand for using Arc's 'assign special form.

Kernel is a different kind of language. It goes back to lisp's roots a little, and lets users define syntaxes based on a way to interpret them at run time. Since the user can write their interpretation code however they want to, it's challenging to predict what these programs will do until they execute, which is why nobody (I think) has managed to write a good optimizing compiler for Kernel.

---

"Why are lists compared via `iso' but strings are compared via `is'?"

This is a very irregular part of Arc. You can compare lists using either operator, but 'iso does a deep comparison and 'is compares by reference. However, 'is actually special-cases strings and compares them by their contents, just like 'iso.

I think this choice was made because strings aren't usually modified. Arc lets you mutate a string if you really want to, but it's not a very commonly used feature.

---

"Is it because strings are a symbol but lists aren't?"

Technically, string and symbol are separate data types.

Technically, the empty list (nil) is a symbol. :)

If your point is that Arc's strings are interned like symbols, that's probably not true, since they're mutable. It's really an awkward special case in the language.


(1) Yes this has occurred to me as well. I think it's pretty much just convenience. Though languages like Kernel and even Common Lisp (with constant need to quote or function-quote) reduce that inconvenience anyway.

Even if you choose to quote names in definers, you still need some irregularity in evaluation policies to build short-circuiting or/and.

(2) Arc is built on racket, and strings in racket (and other schemes?) are interned and so fast to compare just by their address. Lisp choose not to intern strings.

I tend to avoid these complexities and just use 'iso' everywhere. (My language has only an equivalent of iso: https://github.com/akkartik/wart/blob/955497c1ca/045check.wa...)

Past discussions: http://arclanguage.org/item?id=248, http://www.arclanguage.org/item?id=16304

6 points by akkartik 4881 days ago | link | parent | on: Still under development?

I think it's 'meta hackable' in that you can choose what 'hackable' means :) Some people may choose "last release 4 years ago" to mean it's dead. I choose to think 'stable release' doesn't apply to arc. Even PG's versions have the disclaimer "future releases can break anything", which doesn't seem very stable. So I and a few others choose to "break anything" of our own accord :)

'Hackable' isn't black or white, but a question of priorities. SBCL and Racket care about keeping the implementation hackable, but have other higher priorities: it should provide a stable interface that doesn't break user code, it should provide a compiler, it should run programs really fast, it should help teams of people work on large projects, etc. With arc, hackability is the #1 priority. It's a pretty good platform for experimenting with language features, or at least a pretty good forum for discussing language features you build on any platform. But it's not the ideal place if you just want features and don't care how they're built.

3 points by akkartik 4886 days ago | link | parent | on: ACL2

I took a very basic -- and very awesome -- class in grad school from J Moore, one of its creators: http://www.cs.utexas.edu/~moore/classes/cs389r
2 points by evanrmurphy 4886 days ago | link | parent | on: ACL2

They have a web-based repl/tutorial at http://tryacl2.org/

The neatest thing I've found on there so far is the theorem proving abilities. Lesson 3 shows you can input things like (thm (= (+ a b) (+ b a))) and it will return a proof that the sum of a and b is equal to the sum of b and a.


(author of the article here)

I'm honored! (And I didn't even have to sign up for GitHub!) How'd you find your way over to this forum?

You're absolutely right in that I've glossed over quite a few details, and clearly I've screwed up in some ways that were immediately obvious to you :P

If it seems like I found it immediately obvious, it's only because I took a long time writing & discovering these thoughts before committing them to one big post. :)

yes, this is exactly the point. The idea is that if you're giving it a name, that actually means something pretty significant, much more than just a way to access some specific bit of RAM with a bunch of ascii characters as reference.

I would like to have the short forms from my examples in ObjC though.. writing "x:x y:y width:width height:height" is actually something that happens quite a bit ^_-

I do lack perspective on this because I've never used Objective-C. I mean, apparently other people already commit to more-verbose ways of having strict "name enforcement" (though I'm not sure how many people use Objective-C by choice...). It's certainly an interesting idea, and I hope to see how it develops.

4 points by fallintothis 4888 days ago | link | parent | on: Nested Macros

First, you have to understand what the quasiquote and unquote are doing. It doesn't seem at first glance like you're clear on that, so a mini-lesson:

  arc> 'the-basic-purpose-of-quote-is-to-prevent-a-symbol-from-being-evaluated
  the-basic-purpose-of-quote-is-to-prevent-a-symbol-from-being-evaluated
  arc> '(quote can be used on any expression --- including a list)
  (quote can be used on any expression --- including a list)
  arc> (list 'you 'can 'think 'of ''(a b c) 'as 'shorthand 'for '(list 'a 'b 'c))
  (you can think of (quote (a b c)) as shorthand for (list (quote a) (quote b) (quote c)))
  arc> `quasiquote-is-pretty-much-just-like-quote
  quasiquote-is-pretty-much-just-like-quote
  arc> (list 'so '`(a b c) 'is 'like '(list 'a 'b 'c))
  (so (quasiquote (a b c)) is like (list (quote a) (quote b) (quote c)))
  arc> `(but suppose you want to write (list 'a 'b c) --- where the last element has no quote)
  (but suppose you want to write (list (quote a) (quote b) c) --- where the last element has no quote)
  arc> `(then quasiquote gives you a shorthand by using "unquote")
  (then quasiquote gives you a shorthand by using "unquote")
  arc> `(so `(a b ,c) is like (list 'a 'b c) and `(a ,b c) is like (list 'a b 'c) etc)
  (so (quasiquote (a b (unquote c))) is like (list (quote a) (quote b) c) and (quasiquote (a (unquote b) c)) is like (list (quote a) b (quote c)) etc)
  arc> `(thus this list will have 4 instead of (+ 2 2) right here: ,(+ 2 2))
  (thus this list will have 4 instead of (+ 2 2) right here: 4)
  arc> `(because `(a b ,(+ 2 2)) is like (list 'a 'b (+ 2 2)))
  (because (quasiquote (a b (unquote (+ 2 2)))) is like (list (quote a) (quote b) (+ 2 2)))
  arc> (list 'see? 'a 'b (+ 2 2))
  (see? a b 4)
  arc> 'voila
  voila
With that understood, each isn't doing what you think it is.

  arc> (each x '(1 2 3)
         (prn "\"each\" is used for side-effects on the variable x = " x)
         (prn "but ultimately, it will return nil")
         (prn "we can call pure functions on x")
         (+ x 1)
         (prn "but we won't see anything they return")
         x
         (* x 12)
         (when (is x 3)
           (prn "witness: the next line is the return value of \"each\"!")))
  "each" is used for side-effects on the variable x = 1
  but ultimately, it will return nil
  we can call pure functions on x
  but we won't see anything they return
  "each" is used for side-effects on the variable x = 2
  but ultimately, it will return nil
  we can call pure functions on x
  but we won't see anything they return
  "each" is used for side-effects on the variable x = 3
  but ultimately, it will return nil
  we can call pure functions on x
  but we won't see anything they return
  witness: the next line is the return value of "each"!
  nil
This is because each is a macro that (on a list) expands

  (each x xs
    (do-something x)
    (do-something-else x))
into something like

  ((rfn loop-around (elements)
     (when (acons elements)
       (let x (car elements)
         (do-something x)
         (do-something-else x))
       (loop-around (cdr elements))))
   xs)
You can see that when we get to the end of the list xs, the variable elements is passed in as nil. Because (acons nil) is nil, we don't return anything from the overall when expression. As such, you can be sure that the return value of each on a list is always going to be nil, because that's how it ends its recursive journey.

Thus,

  arc> `(if I "unquote" (each x '(1 2 3) x) into this list I just get ,(each x '(1 2 3) x))
  (if I "unquote" (each x (quote (1 2 3)) x) into this list I just get nil)
What you actually want is to accumulate the results of manipulating each element of the list. That's what map is for:

  arc> (map (fn (x) (+ x 1)) '(1 2 3))
  (2 3 4)
  arc> `(if I "unquote" (map [* _ 12] '(1 2 3)) into this list I get ,(map [* _ 12] '(1 2 3)))
  (if I "unquote" (map (fn (_) (* _ 12)) (quote (1 2 3))) into this list I get (12 24 36))
So, let's take a look at how

  (mac css (sel props . nest)
    `(do ,(pr (string sel "{"))
      ,(gen-css-properties props)
      ,(map [gen-nested-css _ sel props] nest)))
works out. But let's take it slow and just use macex1 to expand the macro by one step---I don't want to get lost in recursion. Because your macro is making calls to pr at macro-expansion time, I'm going to take the liberty of drawing it out even further, so as the output from the prompt doesn't get too confusing:

  arc> (let macro-expansion (macex1 '(css "#foo" (id 5) (".bar" (z-index 2))))
         (prn)
         (ppr macro-expansion)
         (prn))
  #foo{
  (do "#foo{"
      (do (if 5 (pr "id:" 5 ";"))
          (pr "}"))
      ((css "#foo .bar"
            (id 5 z-index 2)
            nil)))
  nil
Now, we get to see the problem of using the unquote versus unquote-splicing (,@). The unquote will just shove its argument into the list. Because we're unquoting another list itself, you can see the result of the call to gen-nested-css gets stuck with an extra layer of parentheses. In the expansion, it's like saying

  arc> ((+ 2 2))
  Error: "Function call on inappropriate object 4 ()"
instead of

  arc> (+ 2 2)
  4
However, if you use unquote-splicing, the elements of the list will get "inlined" into the overall list that the quasiquote is constructing. E.g.,

  arc> `(symbols wrapped in parens because of unquote: ,'(a b c))
  (symbols wrapped in parens because of unquote: (a b c))
  arc> `(symbols without parens because of unquote-splicing: ,@'(a b c))
  (symbols without parens because of unquote-splicing: a b c)
So,

  (mac css (sel props . nest)
    `(do ,(pr (string sel "{"))
      ,(gen-css-properties props)
      ,@(map [gen-nested-css _ sel props] nest)))
gives us

  arc> (let macro-expansion (macex1 '(css "#foo" (id 5) (".bar" (z-index 2))))
         (prn)
         (ppr macro-expansion)
         (prn))
  #foo{
  (do "#foo{"
      (do (if 5 (pr "id:" 5 ";"))
          (pr "}"))
      (css "#foo .bar"
           (id 5 z-index 2)
           nil))
  nil
Now, whether this is the desired expansion at the end of the day is a matter of debugging. Good luck! :)

Recommendation: with these points (I hope) cleared up, look closer at html.arc. If you had modeled your code after it more precisely, you probably wouldn't have had these issues. But hey, that's how we learn.

I catch some resistance sometimes when I suggest learning Arc by reading Arc, but the source code is not only a definitive reference for what functionality Arc provides, it also gives you a wealth of idiomatic code. And I can't think of a better way to answer "how does [some function] work?" than to look up its usually-terse definition.

2 points by jsgrahamus 4889 days ago | link | parent | on: What I learned from Icon

Unicon (http://unicon.sourceforge.net/) is the successor to Icon. Both languages had/have good support in terms of experienced people willing to answer questions.
1 point by akkartik 4890 days ago | link | parent | on: What I learned from Icon

I'd read about Icon before (e.g. http://arclanguage.org/item?id=17476), but forgotten the key insight of Icon:

"functions will not be called unless the calls within do not fail, so"

  if y < (x | 5) then write("y=", y)
"can be shortened to"

  write("y=", (x | 5) > y)
---

I'd also somehow missed that it had the infix-chaining idea long before wart:

"the < operator returns its second argument if it succeeds, allowing things like a < b < c" https://en.wikipedia.org/wiki/Icon_%28programming_language%2...

(Compare http://arclanguage.org/item?id=16775)

3 points by zck 4890 days ago | link | parent | on: Nested Macros

At a minimum, gen-nested-css needs to be a macro, not a function.

But also, recursive macros are hard. You need to know whether you'll recurse without looking at the values of variables, because macroexpansion time is before runtime.

I'm not sure your 'each in css works properly.

1 point by forwardslash 4890 days ago | link | parent | on: ! and . operators

Thank you so much!
1 point by thaddeus 4892 days ago | link | parent | on: Realm of Racket

Looks like there's a 40% discount for the next 72 hours using RACKETEERS as a promo code.
1 point by thaddeus 4892 days ago | link | parent | on: Realm of Racket

Just received as email. I can not recommend either way, but it looks interesting.
2 points by dram 4893 days ago | link | parent | on: Insort is not truly destructive?

Good catch.

About the nil problem, maybe we should nil terminate list this way: (1 2 nil), instead of (1 2 . nil). just a joke. :)

Anyway, mutable pair is just a mess, I feel I have some understanding why Racket get rid of set-car! and set-cdr!.


(author of the article here)

Thanks for the analysis!

You're absolutely right in that I've glossed over quite a few details, and clearly I've screwed up in some ways that were immediately obvious to you :P

I guess the first thing I'd have to introduce for the whole issue of basic operations destroying the entire system is to introduce something like:

    def _foo:+(_foo:n, _foo:m):
        ...
i.e. that "underscore something" is a meta-name.

Then we have:

  hello = "hi, "
  world = "planet"

  hello_world = hello + world     # error, since join(hello, world) = top

  # and yet...

  hello_world = "hi, " + "planet" # not an error: join(bottom, bottom) = bottom
yes, this is exactly the point. The idea is that if you're giving it a name, that actually means something pretty significant, much more than just a way to access some specific bit of RAM with a bunch of ascii characters as reference.

I don't know how it'd scale but I imagine that many users of such a theoretical system would find it really annoying to write in the beginning without good tooling (kind of like Java with all those getters and setters :P). I do believe though that this is also the case in Objective-C and that in ObjC this tradeoff is appreciated later when maintaining big code bases. Maybe my rather draconian idea is unfeasible because of this reason alone!

I would like to have the short forms from my examples in ObjC though.. writing "x:x y:y width:width height:height" is actually something that happens quite a bit ^_-

1 point by akkartik 4893 days ago | link | parent | on: Insort is not truly destructive?

"..just alter your expectations of destructive operators.."

Yeah, I agree with this. I weakly think (without evidence) that arc's destructive ops are more functional, and it encourages a better style to not constantly make assumptions about precisely which cons cells go where.

On the other hand, a set of 'alias-friendly' functions could coexist with the existing primitives.

Hmm, I wonder if the notion of boxes would help build alias-friendly operations.

3 points by fallintothis 4893 days ago | link | parent | on: Insort is not truly destructive?

Your version doesn't quite work as-is:

  arc> (= y (= x '(1 3 5)))
  (1 3 5)
  arc> (insort < 2 x)
  (1 2 3 5)
  arc> x
  (1 2 3 5)
  arc> y
  (1 2 3 5)
  arc> (insort < 0 x)
  (0 1 2 3 5)
  arc> x
  (0 1 2 3 5)
  arc> y
  (0 1 2 3 5)
  arc> (insort < 6 x)
  (0 1 2 3 5 6)
  arc> x
  (0 1 2 3 5 6)
  arc> y
  (0 1 2 3 5 6)
  arc> (= y (= x nil))
  nil
  arc> (insort < 1 x)
  nil
  arc> x
  nil
  arc> y
  nil
Here's a version I wrote for my own edification (basically, a more complicated version of yours):

  (def destructive-cons (elt seq)
    ; Note: (push elt seq) won't work, because push is a macro on the "place",
    ; and seq isn't the proper "place" to mutate (push will just mutate the local
    ; binding of seq).  This function will actually mutate the cons cell being
    ; pointed at by seq.
    (let (first . rest) seq
      (scar seq elt)
      (scdr seq (cons first rest)))
    seq)

  (def insorter (test elt seq)
    (if (no seq)
         (list elt)
        (test elt (car seq))
         (destructive-cons elt seq)
        (do1 seq
             ((afn (prev cell)
                (if (or (no cell) (test elt (car cell)))
                    (scdr prev (cons elt cell))
                    (self cell (cdr cell))))
              seq (cdr seq)))))

  (mac insorted (test elt seq)
    `(zap [insorter ,test ,elt _] ,seq))
Writing it made me realize the issues with making a "truly destructive" insort:

  arc> (= y (= x '(1 3 5)))
  (1 3 5)
  arc> (insorted < 2 x)
  (1 2 3 5)
  arc> x
  (1 2 3 5)
  arc> y
  (1 2 3 5)
  arc> (insorted < 0 x)
  (0 1 2 3 5)
  arc> x
  (0 1 2 3 5)
  arc> y
  (0 1 2 3 5)
  arc> (insorted < 6 x)
  (0 1 2 3 5 6)
  arc> x
  (0 1 2 3 5 6)
  arc> y
  (0 1 2 3 5 6)
  arc> (= y (= x nil))
  nil
  arc> (insorted < 1 x)
  (1)
  arc> x
  (1)
  arc> y
  nil
No matter how you try, there's still the edge case with nil, on which you can't scar/scdr.

  arc> (= x nil)
  nil
  arc> (scar x 1)
  Error: "set-car!: expected argument of type <pair>; given nil"
  arc> (scdr x '(2))
  Error: "set-cdr!: expected argument of type <pair>; given nil"
So it appears Arc's model of "places" can't really handle references/aliases that well. Indeed, push, swap, rotate, pop, pushnew, pull, togglemem, and the like are "broken", too, if we expect them to modify the _content_ of their arguments instead of simply where the arguments point. E.g.,

  arc> (= y (= x '(a b c)))
  (a b c)
  arc> (= z (= w '(1 2 3)))
  (1 2 3)
  arc> (swap x w)
  (a b c)
  arc> x
  (1 2 3)
  arc> y
  (a b c)
  arc> z
  (1 2 3)
  arc> w
  (a b c)
In a way, I can see that being the right behavior for swap, but not for insort. I suppose it's at least somewhat consistent: just alter your expectations of "destructive" operators, since they'll all (near as I can tell) just modify where their input "places" are pointing. Not that I'm ready to write off the issue that way, it's just I don't have a solution and think it gets complicated depending on the operators in question. :)

I prefer the verb form: "I think you should github", etc. :)

That was quite a thorough analysis. Perhaps one way to think about this idea: it would encourage a style of programming with minimal temporaries.

2 points by akkartik 4894 days ago | link | parent | on: Insort is not truly destructive?

Ah, I see what you and dram mean. zck pointed this out as well last year: http://arclanguage.org/item?id=17074.
2 points by dram 4894 days ago | link | parent | on: Insort is not truly destructive?

That's the point.

But your code only make first cell of x to be destructive.

  (let insorted (insert-sorted < 2 x)
         (scar x (car insorted))
         (scdr x (cdr insorted)))
I wrote some more destructive code, see: http://www.arclanguage.org/item?id=17838
2 points by dram 4894 days ago | link | parent | on: Insort is not truly destructive?

The first assignment has no confusion.

I think insort should modify the list, not merely assign a new list.

Some demo code:

  (def insort1 (test elt seq)
    (if (no seq)
         (list elt)
        (test elt (car seq))
         (with (first (car seq) rest (cdr seq))
          (scar seq elt)
          (scdr seq (cons first rest)))
        (no (cdr seq))
          (scdr seq (list elt))
        (insort1 test elt (cdr seq))))
  
  (mac insort (test elt seq)
    `(do (insort1 ,test ,elt ,seq) ,seq))
3 points by fallintothis 4894 days ago | link | parent | on: Insort is not truly destructive?

This is a tricky one! Turns out you're absolutely right, actually, this is sort of a bug (if you expect a different behavior of the function insert-sorted).

You're right in thinking that x and y share the same reference, like

  y -.
      \
  x ---+--> [ * | * ]
              |   |
              1   `--> [ * | * ]
                         |   |
                         3   `--> [ * | * ]
                                    |   |
                                    5   `--> nil
The thing is, the only real way to get at pointers in Arc is to use sref, scar and scdr. Those will actually mutate the cons cell sitting in memory. We can verify this, plus the fact that x and y share the same reference, by using (say) scar:

  arc> (= y (= x '(1 3 5)))
  (1 3 5)
  arc> x
  (1 3 5)
  arc> y
  (1 3 5)
  arc> (macex '(= (car x) 2)) ; '= expands into a form that uses 'scar
  ((fn () (atwith (gs1731 x gs1732 2) ((fn (val) (scar gs1731 val)) gs1732))))
  arc> (= (car x) 2)
  2
  arc> x
  (2 3 5)
  arc> y
  (2 3 5)
The problem is that insort doesn't truly mutate the cons cell of its argument. It's a lazy-man's "destructive", because it uses the zap macro to reassign x to a separate, insorted version of the list:

  arc> (= y (= x '(1 3 5)))
  (1 3 5)
  arc> (macex '(insort < 2 x))
  (atomic-invoke
    (fn ()
      (withs (gs1736 x
              gs1735 [insert-sorted < 2 _])
        ((fn (gs1737) (assign x gs1737))
         (gs1735 gs1736)))))
Notice how the expansion calls insert-sorted on x, then uses (assign x ...) to change where x points. insert-sorted is just a normal, FP-style function that doesn't mutate its arguments, instead consing together a new list in memory:

  (def insert-sorted (test elt seq)
    (if (no seq)
         (list elt) 
        (test elt (car seq)) 
         (cons elt seq)
        (cons (car seq) (insert-sorted test elt (cdr seq)))))
So, the pointer diagram will look like

  y -.
      \
  x    `--> [ * | * ]
  |           |   |
  |           1   `--> [ * | * ]
  |                      |   |
  |                      3   `--> [ * | * ]
  |                                 |   |
  |                                 5   `--> nil
  `-------> [ * | * ]
              |   |
              1   `--> [ * | * ]
                         |   |
                         2   `--> [ * | * ]
                                    |   |
                                    3   `--> [ * | * ]
                                               |   |
                                               5   `--> nil
If you want to mutate the cons cell that x points to, you'd instead have to do something like

  arc> (= y (= x '(1 3 5)))
  (1 3 5)
  arc> (let insorted (insert-sorted < 2 x)
         (scar x (car insorted))
         (scdr x (cdr insorted)))
  (2 3 5)
  arc> x
  (1 2 3 5)
  arc> y
  (1 2 3 5)
That way, your pointer diagram would look like

  y -.        1
      \        
  x ---+--> [ * | * ]
             /     \
            /       \        [ * | * ]
           /         \         |   |
          /           \        3   `--> [ * | * ]
         /             \                  |   |
        /               |                 5   `--> nil
        |   [ * | * ]   |
        |     |   |     |
        `---> 1   `-----+---> [ * | * ]
                                |   |
                                2   `--> [ * | * ]
                                           |   |
                                           3   `--> [ * | * ]
                                                      |   |
                                                      5   `--> nil
Then the free-floating cons-cell from the insert-sorted, the old car (the 1), and the old cdr (the (3 5)) would all be garbage-collected eventually. (Modulo implementation details, of course, like the numbers actually sharing the same spots in memory instead of being duplicated. But it makes the diagram cleaner.)

5. minutes = hours_to_minutes(time.hours) + time.minutes

On line 5, he mentions that hours_to_minutes has its return value named minutes. And I can see how time.minutes would be "named" minutes. But he's really baking a lot of tacit semantics into the + operator that aren't actually obvious...

In a Python-like language, there's a distinction between the built-in "plus" statement and a "plus" function. Since + isn't a function, I'm assuming the "return value" doesn't need its own name. However, he's clearly under the impression that the expression "inherits" the names of its arguments somehow. But exactly how is unclear.

Variables obviously have names (name(var) = var). But what are the names of literals? Presumably it's kind of just "no name" (the _ wildcard he mentions?), so no errors arise from something like

  any_way_you_want_it = "that's the way" + " " + "you need it"
Or does he propose to use names for literals to accomplish strong-typing? Like the name of any string is str and the name of any integer is int so that 10 + "1" would somehow be an error? If that were the case, wouldn't you be forced to use the name str instead of the above any_way_you_want_it? As far as I can tell, strong-typing would be relegated to something outside of the strong-naming system. Which might be an acceptable amount of complexity, as long as we're under no illusions about the universality of the strong-naming system.

What about mixing literals with named values in expressions? The result of an expression seems to take on the name of the named operands, as in his example where minutes needs the <- annotation:

  minutes<-hours = hours * 60
These semantics are feasible. Really, the problem the compiler would need to solve would just be a simple dataflow analysis across a "names" lattice, similar to constant propagation (http://ecee.colorado.edu/~waite/Darmstadt/data.html#cprop). The lattice would have top = bad name, bottom = no name, and the "flat" elements in the middle tier (as in constant propagation) would be every possible name. Then the join operator would go

  join(bottom, bottom) = bottom
  join(name, bottom)   = name
  join(bottom, name)   = name
  join(name, name)     = name
  join(name1, name2)   = top     (where name1 != name2)
  join(top, _)         = top
  join(_, top)         = top
OK, so this makes sense. But it's overly restrictive. In particular, I don't know that join(name1, name2) = top is right:

  hello = "hi, "
  world = "planet"

  hello_world = hello + world     # error, since join(hello, world) = top

  # and yet...

  hello_world = "hi, " + "planet" # not an error: join(bottom, bottom) = bottom
hello and world individually did nothing to warrant their names: they've just been assigned to literals. But they are named (right?), and now hello + world is improperly-named, even though doing constant propagation would resolve the problem.

Could we coerce the name of hello_world to allow the mixing of hello and world? The <- notation seems unsuitable, because it only allows the renaming of a single name, as in the minutes<-hours example. Unless maybe you use a wildcard to switch off...well, really the whole strong-naming system, I guess:

  hello_world<-_ = hello + world
This example isn't entirely silly, either. I've had plenty of times in code where I name intermediate values instead of just altering the same variable repeatedly. E.g., total_amount = amount_saved + amount_deleted.

At any rate, traditional copy propagation certainly becomes a nightmare. Of course, this is probably deemed a worthwhile trade-off in such a strong-naming system.

On another note, I think there's an error of omission. While he addresses the input parameters whose names you don't care about, what about functions that have no meaningful name for their return values? I think it would be appropriate to have something like

  def _:reciprocal(_:n):
      return 1.0 / n
(Unless you want to use a Pascal-like system where function return values are named the same thing as the function.)

It's weird though, because it further increases the distance between syntactic expressions and user-defined functions. If + were a function, it'd be one of those declared with a _ return name, because there's no meaningful way to predict the names of what you'll be adding. You'd have something like

  def _:+(_:n, _:m):
      while n > 0:
          n = n - 1
          m = succ(m) # primitive "plus one" function...again, a _ return name
      return m
But if this were the case, you'd no longer have the "name propagation" in +-expressions, so his example

  minutes = hours_to_minutes(time.hours) + time.minutes
could be off-the-wall, like

  ha_the_return_value_is_unnamed = +(time.hours * 60, time.minutes)
You're back to square one. So this strong-naming system encourages the sort of function/statement separation Python gets criticized for!

I dunno. I'm thinking the strong-naming system happens to work well for his example just because his example has variables named after units. I'm skeptical about how well the system would scale to other code bases. Good names are important, yes, but building a type system around them? Plus, it kind of puts names on a pedestal, which I think encourages mutation, which is runs somewhat counter to my normal mode of thinking.

(If I had a github, I'd comment on his gist...)

1 point by akkartik 4894 days ago | link | parent | on: Insort is not truly destructive?

Hmm, I think the confusion has to do with the assignment in the first step rather than the call to insort itself.

  arc> (insort < 2 x)
  (1 2 3 5)
  arc> x
  (1 2 3 5)
Here, x was modified in place. Does that seem as expected?

The real confusion is that y and x are not aliases. They just happen to be bound to the same value (cons cell) at the start. A destructive change to one doesn't affect the other.

Or, put another way, 'destructive' doesn't mean the original list is guaranteed to be destroyed. Just that it _can_ be modified. It would be destroyed if nothing else was using it and it got garbage-collected. The GC is the only entity actually doing destruction.

Am I addressing your question? This is a clarifying process to myself, at any rate :)

More