Arc Forumnew | comments | leaders | submit | akkartik's commentslogin

Yes you don't need the nil argument. Since make-list returns a list, do the conses on the way out of recursive calls rather than on the way in:

  (def make-list (size val)
    (if (> size 0)
      (cons val
            (make-list (- size 1) val))))
This is identical to your version, except I dropped the now-unnecessary base case (if now generates the initial list at the bottom-most recursive call) and moved the cons outside the call to make-list.

-----

2 points by jsgrahamus 3434 days ago | link

Great. Still wrapping my head around recursion.

Thanks.

-----

1 point by akkartik 3434 days ago | link | parent | on: ASK: Emacs inferior-arc on Windows

I have no experience with Emacs, sadly, but hopefully someone here does and can share a working setup.

-----


I played with Petite Chez Scheme back in '99-'00, long before I knew much about Lisp. Very cool blast from the past.

-----

3 points by akkartik 3438 days ago | link | parent | on: Unit-test.arc 1.0 incoming

Looking forward to playing with this!

Edit 33 minutes later: I'm not much of a Mercurial user. How do I switch branches?

-----

2 points by zck 3438 days ago | link

`hg checkout run-test` will do it.

If you want to view all branches, `hg branches`. There are some old ones I need to finish and merge back in. To view the current branch, `hg branch`.

Or if you want to look at it online, the link above (https://bitbucket.org/zck/unit-test.arc/src/8b9dbe91702c3866...) should take you to the right branch. (Edit: you'll know you're on the right branch if the second code block runs tests with (test), not (run-suite))

-----

1 point by akkartik 3438 days ago | link

Ah, thanks. Crazy that https://www.mercurial-scm.org/wiki/GitConcepts doesn't mention hg checkout.

-----

2 points by akkartik 3437 days ago | link

I just tried running the 'run-test' branch with the Anarki tests, but I get an error. Is it expected to be backwards-incompatible?

-----

2 points by zck 3436 days ago | link

It's not backwards compatible. Hence me trying to get feedback before pushing the 1.0.

The specific backwards incompatibility (I assume you got "We can't parse this as a suite body") is moving from this format for the tests:

    (suite suite-name
           test-name (test-body))
to the format you suggested last year:

    (suite suite-name
           (test test-name (test-body)))
I don't think supporting both ways of writing tests is useful. I'm also planning on migrating the anarki tests when I roll out the 1.0. Actually, I could get started on a branch even before. That'd be something to do.

-----

1 point by akkartik 3436 days ago | link

Ah, I'd forgotten :)

Yeah, no reason to be compatible. I was just confused. I'll migrate the tests to the new format, and update Anarki publicly once you release 1.0.

-----

2 points by zck 3435 days ago | link

If you start doing some of the migration work, push the in-progress stuff to a branch and I'll help work on it. I'll do the same if I get to it before you. Thanks.

-----

2 points by akkartik 3434 days ago | link

I spent a few minutes trying to build an automatic script to upgrade our tests -- and found a bug in Arc's serialize/unserialize (my fault) :)

https://github.com/arclanguage/anarki/commit/9efd58992d

What's the equivalent of (run-all-suites) in 1.0? I didn't see this covered in the Readme.

Other than that my script seems to be barfing on nested suites at the moment. I'll continue working on it tomorrow.

-----

2 points by zck 3434 days ago | link

Oh, fancy writing a script to do the upgrade. It'd be cool to see it.

You can run all tests with (test). I should add that to the instructions, thanks.

-----

2 points by akkartik 3434 days ago | link

This must be the most fun thing on my radar right now, because I got to it first thing this morning :)

  ; translate.arc
  (def translate (expr)
    (accum acc
      (translate-2 expr acc)))

  (def translate-2 (expr acc)
    (if (atom expr)
          (acc expr)
        (is car.expr 'suite)
          (do (acc 'suite)
              (let (suite-name . suite-body)  cdr.expr
                (acc suite-name)
                (translate-suite-body suite-body acc)))
        (is car.expr 'suite-w/setup)
          (do (acc 'suite-w/setup)
              (let (suite-name suite-setup . suite-body)  cdr.expr
                (acc suite-name)
                (acc suite-setup)
                (translate-suite-body suite-body acc)))
        'else
          (map acc expr)))

  (def translate-suite-body (suite-body acc)
    (if suite-body
      (if (acons car.suite-body)
        ; nested suite
        (let (nested-suite . rest) suite-body
          (acc (accum acc2
                  (translate-2 nested-suite acc2)))
          (translate-suite-body rest acc))
        ; test name must be atomic
        (let (test-name test-body . rest)  suite-body
          (acc `(test ,test-name ,test-body))
          (translate-suite-body rest acc)))))

  ; bootstrap tests for a test harness :)
  ; suite with tests
  (assert:iso '(suite a (test t1 b1) (test t2 b2))
              (translate '(suite a t1 b1 t2 b2)))

  ; suite with tests and nested suites
  (assert:iso '(suite a (test t1 b1) (suite s2 (test t3 b3)) (test t2 b2))
              (translate '(suite a t1 b1 (suite s2 t3 b3) t2 b2)))

  ; suite with setup and tests
  (assert:iso '(suite-w/setup a (x 1 y 2) (test t1 b1) (test t2 b2))
              (translate '(suite-w/setup a (x 1 y 2) t1 b1 t2 b2)))

  ; suite with setup and tests and nested suites
  (assert:iso '(suite-w/setup a (x 1 y 2) (test t1 b1) (suite s2 (test t3 b3)) (test t2 b2))
              (translate '(suite-w/setup a (x 1 y 2) t1 b1 (suite s2 t3 b3) t2 b2)))

  ; run
  (each f cdr.argv
    (prn f)
    (fromfile string.f
      (tofile (+ string.f ".2")
        (each expr (drain:read)
          (let out translate.expr
            (ppr out))))))

Run it like so:

  $ arc translate.arc *.t lib/*.t lib/tests/*
I haven't committed it anywhere yet because I'm not too happy with the state of Anarki's pretty-printer. Would you mind if I change the indentation style for suites and tests in Anarki? I was thinking something like this:

  (suite atom
    (test includes-int (assert-t (atom 3)))
    (test includes-float
      (assert-t (atom 3.14159)))
    (test includes-exact (assert-t (atom 3/16)))
    (test includes-symbol (assert-t (atom 'a)))
    (test includes-char (assert-t (atom #\a)))
    (test includes-string
      (assert-t (atom "hello")))
    (test includes-nil (assert-t (atom nil)))
    (test excludes-list
      (assert-nil (atom '(1 2 3))))
    (test excludes-table
      (assert-nil (atom (obj a 1 b 2))))
    (test excludes-tagged-types
      (assert-nil (atom (annotate 'foo 34)))))
..rather than this current output:

  (suite atom
         (test includes-int (assert-t (atom 3)))
         (test includes-float
               (assert-t (atom 3.14159)))
         (test includes-exact (assert-t (atom 3/16)))
         (test includes-symbol (assert-t (atom 'a)))
         (test includes-char (assert-t (atom #\a)))
         (test includes-string
               (assert-t (atom "hello")))
         (test includes-nil (assert-t (atom nil)))
         (test excludes-list
               (assert-nil (atom '(1 2 3))))
         (test excludes-table
               (assert-nil (atom (obj a 1 b 2))))
         (test excludes-tagged-types
               (assert-nil (atom (annotate 'foo 34)))))

-----

2 points by akkartik 3434 days ago | link

All done:

  atom:  	10 tests,  	0 nested suites.
  memtable:  	5 tests,  	0 nested suites.
  do:  	2 tests,  	0 nested suites.
  for:  	3 tests,  	0 nested suites.
  ssyntax:  	13 tests,  	0 nested suites.
  ...
  strings:  	0 tests,  	15 nested suites.
      trim:  	4 tests,  	0 nested suites.
      tokens:  	2 tests,  	0 nested suites.
      endmatch:  	4 tests,  	0 nested suites.
      subst:  	3 tests,  	0 nested suites.
  ...
  types:  	0 tests,  	3 nested suites.
      coerce:  	2 tests,  	1 nested suite.
          strings:  	9 tests,  	5 nested suites.
              numbers:  	6 tests,  	3 nested suites.
                  rational-rounds-with-unused-base-arg:  	6 tests,  	0 nested suites.
                  rational-rounds-to-even:  	6 tests,  	0 nested suites.
                  float-rounds-to-even:  	6 tests,  	0 nested suites.
              characters:  	4 tests,  	0 nested suites.
              coercions-to-same-type:  	3 tests,  	0 nested suites.
              string->num:  	18 tests,  	0 nested suites.
              string->int:  	13 tests,  	0 nested suites.
      type:  	15 tests,  	0 nested suites.
      annotation:  	4 tests,  	0 nested suites.
  Suite module: all 3 tests passed!
  Suite special-syntax: all 4 tests passed!
  Suite html: the single test passed!
  There are no tests directly in suite special-forms.
      Suite special-forms.function-definition: all 22 tests passed!
      Suite special-forms.if: all 5 tests passed!
      Suite special-forms.quasiquote: all 15 tests passed!
      Suite special-forms.quote: all 3 tests passed!
      Suite special-forms.assign: all 3 tests passed!
  There are no tests directly in suite types.
      Suite types.coerce: all 2 tests passed!
          Suite types.coerce.strings: all 9 tests passed!
              Suite types.coerce.strings.numbers: all 6 tests passed!
                  Suite types.coerce.strings.numbers.rational-rounds-with-unused-base-arg: all 6 tests passed!
                  Suite types.coerce.strings.numbers.rational-rounds-to-even: all 6 tests passed!
                  Suite types.coerce.strings.numbers.float-rounds-to-even: all 6 tests passed!
              Suite types.coerce.strings.characters: all 4 tests passed!
              Suite types.coerce.strings.coercions-to-same-type: all 3 tests passed!
              Suite types.coerce.strings.string->num: all 18 tests passed!
              Suite types.coerce.strings.string->int: all 13 tests passed!
      Suite types.type: all 15 tests passed!
      Suite types.annotation: all 4 tests passed!
  Suite len: all 3 tests passed!
  Suite bracket-fn: all 3 tests passed!
  ...

  Yay! All 606 tests passed!

-----

4 points by akkartik 3439 days ago | link | parent | on: Language Opinions

I don't have much experience with Tcl, so I'm surprised to hear that it has macros. Can you point me to a description? I want to see what you mean by "macro system built in pure Tcl".

I've heard many criticisms about Tcl over the last 15 years, though, primarily having to do with everything being a string, from what I remember.

---

I certainly don't think Lisp is the end-all. For example, Forth has some cool capabilities much more elegantly provided (http://yosefk.com/blog/my-history-with-forth-stack-machines....). Factor basically has Lisp macros (http://arclanguage.org/item?id=19593). Haskell's pretty nice too. I particularly hanker after its automatically curried functions. Javascript has an eco-system we should all covet.

Past a point, languages don't matter. At some point you have to build stuff with the languages you know. So there's no point optimizing for the absolute best thing possible anywhere. Otherwise you risk becoming just a peanut gallery, bikeshedding because you cannot do.

But that's not an argument to not learn languages. Most people (including myself in the past) are led astray by some misplaced notion of efficiency, of thinking that the right language will make them faster programmers, better at their day jobs. Thinking this way leads to questions like, can I use this in production, will this replace my current language X, what is the value proposition, how many lines does it take to write this program. But learning languages is actually for resilience (or what Nassim Taleb calls anti-fragility). It's for having new ways of thinking about things as insurance against the day you run up against just the right sort of really difficult problem.

You can't prepare for an unknown future by working at it. That gets old quickly. Instead you treat it as play, you find ways to make it entertaining. In fact, that's why play evolved. Play is an evolutionary adaptation for anticipating unknown unknowns. Bear cubs play because it lets them practice skills that they'll never have a chance to fail at (because the one time they need it, they die if they don't have it).

So don't ask if a language is the be all end all. Languages are worth learning as a form of play, to gain breadth in the variety of rube goldberg machines (https://www.youtube.com/watch?v=GXrRC3pfLnE) it is possible to build using computers. Programming isn't about generating characters in the right syntax, following 'best practices'. Those trappings are not the essence. The essence of programming is being able to do things with many different (often unfamiliar) primitives, and about being able to bounce around between abstractions. Learning languages helps with both.

(My big beef with the world is that the way we do software today encourages people to live within rigid abstractions and so stifle their own potential: http://akkartik.name/post/libraries2)

Anyways: Don't learn languages so you can stop learning languages. Learn languages so you can keep learning languages. (http://www.amazon.com/Finite-Infinite-Games-James-Carse/dp/1...)

If after learning a language it turns out to immediately be a good fit in production, in your day job, great! But that's just an unanticipated bonus, not the reason to do the learning.

"A language that doesn't affect the way you think about programming is not worth knowing." http://www.cs.yale.edu/homes/perlis-alan/quotes.html

(I'm not really addressing you anymore. These are just some of the different cognitive traps I've seen myself and others fall into.)

---

'Lisp' isn't even a language, just a way of representing code that you can add to any language. Just see http://lfe.io; https://bitbucket.org/ktg/lfgo; http://docs.hylang.org/en/latest/tutorial.html#hy-is-a-lisp-...; https://github.com/tomhrr/dale; ..

I don't really build stuff in Lisp anymore :) What I'm interested in is teaching programming (http://akkartik.name/post/mu), that's why I continue to hang out here: for the chance to meet newcomers who are trying to learn something new, and to immerse myself in the world of someone unfamiliar with all this.

My Mu project is very far from a dynamic language. It's intended to replace C, so there's no garbage collection. There's not even any nesting expressions. Unlike Lisp where everything is an expression, in Mu everything is a statement. And yet, there are things in there that I picked up from Lisp, and which wouldn't exist if I didn't know about Lisp. Types in Mu are arbitrary s-expressions, because Lisp taught me that (map (address array character) (list number)) handles complexity more gracefully than map<string, list<number>>. That was an unanticipated benefit; I started learning Lisp long before Mu was a twinkle in my eye.

-----

2 points by mpr 3439 days ago | link

Those criticisms of tcl are true in the sense that yes, everything is a string, but unfounded in the sense that so what? Values are stored as strings so that they can be passed around as data and used immediately as code. But if you pass "5" to a command that needs to use it as an int, a conversion will take place, and the int value will be stored along with the string representation. The most recently used type of a variable is kept as the primary representation for efficiency (basically caching).

Here is a link to some docs about the tcl macro system, written by its author: http://wiki.tcl.tk/11155

From a practical perspective, I agree with what you say about "Past a point, languages don't matter. At some point you have to build stuff with the languages you know." Programming is about building stuff that works and is useful, and no matter how much theory you know, at some point you just have to sit down and type.

My motivation for evaluating and comparing the relative merits of languages is that programming, for me, is so much more fun when using certain languages. It's not about finding some holy grail of PL. It's about finding a language that is powerful and will pretty much let me do whatever I want.

I completely agree with your point about learning languages to keep learning languages. Even though I consider tcl to be more fun to program in than forth, my time writing forth code was extremely valuable to the way I think about programming.

I really like that analogy to bears.

Edit: The tclers wiki, to which I linked above, contains a massive amount of information on the language, programming in general, and many other topics, the likes of which I've never seen concentrated as well in another programming community.

-----

2 points by akkartik 3439 days ago | link

"It's not about finding some holy grail of PL. It's about finding a language that is powerful and will pretty much let me do whatever I want."

I don't see the distinction. Isn't "let me do whatever I want" the holy grail of PL? :)

My point is that it's equally important to keep learning new things _to_ want. No single language will consistently do that.

"The tclers wiki contains a massive amount of information on the language, programming in general, and many other topics, the likes of which I've never seen concentrated as well in another programming community."

That is high praise! Feel free to submit your favorite links as new threads.

"..everything is a string, but.. so what? ..if you pass "5" to a command that needs to use it as an int, a conversion will take place.."

Automatic conversion is utterly evil: http://stackoverflow.com/questions/9032856/what-is-the-expla...

Here's another way to think about this. Types are useful but they're also speed-bumps. Dynamic languages push types to runtime so that you can run partially correct programs and gradually make them right. The drawback is that you can never be sure that your program doesn't have a type error; there can always be some code path that you haven't tested that causes everything to come crashing down. Passing just strings around makes this drawback exponentially worse: now you can't be sure your program is right even if you cover all possible code paths and your tests have perfect coverage, because type safety is a function of the lines executed and the data they rely on.

On a hunch I also took a look at Tcl's scope design (http://wiki.tcl.tk/12245) because that's something non-lisps (ahem, Python) mess up in subtle ways. And sure enough, Tcl is brain-damaged in this area. (I have sympathy for this mistake, because I too have spent time trying to "design hybrid approaches that strikes a great balance in maximizing the advantages of both lexical and dynamic scoping while minimizing the disadvantages of each": http://arclanguage.org/item?id=15137. Everyone should play with these things, but there's no known improvement on Lisp's dynamic scope and Scheme's lexical scope -- exactly as they're implemented there. Everyone should play with scope strategies so they can understand why.)

-----

2 points by mpr 3439 days ago | link

It looks to me like the confusion in that SO post is because Javascript does something sneaky in order to support several syntaxes that mean the same thing. Tcl has no syntax. Braces aren't used to delimit blocks and denote dictionaries. They're just used as an escaping mechanism, period. Once you know the language, it is always clear how the interpreter will treat your data.

-----

1 point by akkartik 3439 days ago | link

The brace ambiguity only covers some of the issues they discuss. And there are many more elsewhere on the internet..

-----

2 points by mpr 3439 days ago | link

What do you find brain damaged about tcl scoping?

About the holy grail quote... I should say it's not about __finding__ such a language, more like it's about __searching__ for such a language. Which involves constantly looking at different ones.

-----

2 points by akkartik 3439 days ago | link

upvars is a terrible idea. You don't want capturing variables from the lexical environment to be something you have to explicitly ask for everytime. If you don't tend to use lexical scope all the time you end up with all sorts of action-at-a-distance bugs.

Newlisp is another language that uses dynamic scope by default. All it does is save a little bit of implementation effort. In every other way it's bad for users.

Interestingly, Mu plays this game as well. In Mu you have to explicitly specify what level variable you mean. But Mu explicitly isn't a high-level language. It's a language for building Lisp compilers in. And all Lisp compilers implement lexical scope under the hood using these tricks.

So what I'm saying is that Tcl is trying to avoid some work related to automatically managing lexical scope, and trying to pass this off as good for programmers.

But these comments of mine aren't as concrete as I usually strive for. I doubt you'll be persuaded by them. Just keep them in mind as you continue your search.

"..it's not about __finding__ such a language, more like it's about __searching__ for such a language. Which involves constantly looking at different ones."

Yeah, we're completely in agreement there.

-----

3 points by jsgrahamus 3439 days ago | link

My workday language, MUMPS, also treats everything as a string, unless a number is needed, in which case it tries to coerce the string into a number.

I do find coding easier in MUMPS. Surely that has nothing to do with 30 years of coding in it or my brain having been rewired for it...

-----

4 points by akkartik 3441 days ago | link | parent | on: IDE?

No IDE; I mostly just use Vim and run Arc in a separate window. For highlighting I just tell Vim to treat .arc files as scheme. It works well enough, particularly because I tend to have pretty minimalist highlighting needs. Mostly I just want to see comments and strings distinguished from code (https://news.ycombinator.com/item?id=4478154)

On occasion I find a couple of other tools useful:

a) https://github.com/jpalardy/vim-slime runs over tmux and lets me connect up an Arc session. I also use a script to navigate between Vim windows with the same hotkey as between tmux panes: https://www.reddit.com/r/vim/comments/22ixkq/navigate_around.... Screenshot showing all this: http://i.imgur.com/B8ou7zj.png.

b) Anarki contains a script to generate ctags, so that I can jump to definitions from function calls: https://github.com/arclanguage/anarki/blob/master/extras/exu...

-----

3 points by mpr 3441 days ago | link

What's the benefit of running vim-slime as opposed to just an arc repl from the command line? I've been doing that w/ the same vim/tmux setup you mentioned. I enjoy it.

Edit: I see that you can execute code from the vim window. That is a nice feature. Anything else significant?

-----

3 points by akkartik 3441 days ago | link

No, that's pretty much it. I sometimes find it useful, but yes, mostly I forget it exists. Occasionally it'll bother me when I hit ctrl-c for unrelated reasons :)

-----

2 points by akkartik 3442 days ago | link | parent | on: ASK: How to read user input?

This works for me on Anarki. You don't need the readc anymore, that's fixed thanks to mpr below.

-----

1 point by jsgrahamus 3442 days ago | link

Thanks.

Tried arclite and it, too, had problems with readline. However, its problem is that readline (and parse-format) depend upon readc, which is not defined.

-----

1 point by akkartik 3442 days ago | link

Wow, I had to go lookup what arclite was :)

-----

3 points by jsgrahamus 3442 days ago | link

Found it on the arc wiki.

-----


I'm on the move at the moment, but since you brought up defvar yesterday I wonder if that would be a simpler way to go..

Also, it's not clear why you're using the top-level eval. I think it might be unnecessary. Double unquoting is hard to get right so worth avoiding if at all possible. Edit 2 hours later: oh, I see the reason for the eval.

-----

2 points by mpr 3442 days ago | link

I read the link you suggested in my previous post about defvar, and I see that it can be used to set dynamic behavior when a variable is referenced (??). Is this the correct interpretation, and what might be some uses of that?

-----

3 points by akkartik 3442 days ago | link

Yeah it lets you decide what to do when getting or setting a variable. The original link has an example at the bottom, but here's another one kinda related to what you seem to be trying to do:

  arc> (= h (obj a 1 b 2))
  #hash((a . 1) (b . 2))
  arc> (defvar a
               (fn args
                 (if args
                   ; write
                   (= h!a car.args)
                   ; read
                   h!a)))
  arc> a
  1
  arc> (= a 3)
  3
  arc> a
  3
  arc> h
  #hash((a . 3) (b . 2))

-----

2 points by mpr 3442 days ago | link

Oh I see now. That is pretty cool

-----

2 points by akkartik 3443 days ago | link | parent | on: ASK: How to read user input?

I like it! I don't think it'll break anything; can you send a pull request? Then we'll be able to run such code reliably at the repl! That would be sweet.

Edit 38 minutes later: hmm, there's one issue. Right now you can type multiple expressions in on a single line, but this change would drop everything after the end of the first expression. A better approach would be to drop only whitespace and stop at the very first non-whitespace character.

-----

2 points by mpr 3443 days ago | link

Thanks! Yeah, I am aware of that bug, and was kind of ignoring it ;) I'll implement your suggested fix then send a pull request.

-----

3 points by mpr 3443 days ago | link

Pull request submitted

-----

3 points by akkartik 3443 days ago | link | parent | on: ASK: How to read user input?

I think you're misusing defvar. You can read about defvar at https://awwx.ws/defvar2. In short, to invoke your prompt function everytime you refer to x, say this:

  arc> (defvar x (fn() (prompt "> ")))
For example:

  arc> (len x)
  > abc
  3
BTW, I tend to avoid using the interactive repl when I'm performing raw I/O because of the need for the (readc) hack you mentioned. I'd rather just write my code in a script and run it in batch mode.

-----

2 points by mpr 3443 days ago | link

I was using defvar where I should have been using =. But the idea in my post was just to store the result of (prompt) in the variable x, which is meant to be a string.

And yes, now that I am moving past the experimental phase of my script, I will be running things in batch mode.

Thanks for the advice.

-----

More