Arc Forumnew | comments | leaders | submitlogin

Nice article.

Recursion has been one of those things which made my mind bend or expand. I can't imagine what it would normally take for a 12-year old to learn this abstract concept.

What again was your motivation for Mu?

Steve

3 points by jsgrahamus 3369 days ago | link | parent | on: Creating a maze game

Thanks so much, guys.

Steve

3 points by akkartik 3369 days ago | link | parent | on: Creating a maze game

It's possible I'm misunderstanding what y'all mean by "maze game". Does something in text mode like https://github.com/ryanb/ruby-warrior qualify? It's probably at the bottom of a steep hill with Dwarf Fortress at the top..

If text mode is an option, I'll plug my Basic-like http://akkartik.name/post/mu language. My students have made tic-tac-toe and a card game with it. Maybe we should try a maze game next. Here's a text-mode chessboard program, for example: http://akkartik.github.io/mu/html/chessboard.mu.html. With tests for screen and keyboard access (search for 'scenario'). I'm sure it looks like Greek, but take my word for it that 11- and 12-year olds found it pretty easy to work with. Happy to show more over a Hangout or something.

Ack, right after I typed all this out I remembered the Windows constraint. That disqualifies Mu, at least immediately. I knew there was a reason I chose to keep mum when I saw jsgrahamus's post last night.

3 points by rocketnia 3369 days ago | link | parent | on: Creating a maze game

It could take years to figure out all the ins and outs that go into a game engine. Even as a programmer who's tried to make my own engines, I've found it a lot more satisfying to start from an existing basis. And that doesn't make it easy, just easier. :(

I was looking at free engines I could suggest to a non-programmer friend of mine, and I liked Construct 2 a lot (https://www.scirra.com/).

Construct 2 is a tool with free and paid versions, and I tried the free version. One thing I like is that even though it has a rich editor for arranging level designs and animations, it also lets you write plugins that use arbitrary JavaScript code. This means it's probably really easy to put in a JavaScript evaluator. That said, once you're trying to invoke Construct 2's special-purpose functionality from JavaScript code, I have no idea if it's as easy to use as the editor.

Another reason I like Construct 2 is that it saves the game in a pretty readable XML format, so if it really doesn't work out, it's probably not hard to write a tool to parse what you've made and translate it to another system. Knock on wood...

These two features give me the confidence that if things get messy, someone can don a programmer hat and write a chunk of dedicated code to get things back on course.

It turns out I didn't actually get very far in the project, though. After I built a few moving platforms and teleporters, I found some of the platforming physics to be quirky in ways that it seemed like I'd want to dig into the engine to fix. While it seems like I could put in a custom implementation of platforming physics with JavaScript code, I was no longer excited enough to keep up my momentum in the project. But those quirks had to do with the details of what happens when the player gets crushed under a moving platform, and I was also trying to do frame-perfect fine-tuning of the jump physics, so if only I were in a somewhat less picky mood, I could have gotten further in the project.

---

By the way, I hardly know anything about Game Maker, but I want to mention it's in a pay-what-you-want sale for the next five days: https://www.humblebundle.com/gamemaker-bundle

It's a popular system that's been around for a while, I got the bundle just in case I want to see what it's capable of someday.

---

"4. How does one control scrolling in different directions? Do you create the screen in memory and then instruct the system to scroll in the direction of the newly created portion?"

The exact details depend on the game engine, but action games are typically written in an object-oriented style with some "stepper" logic that runs at a regular interval. In each run of the stepper, you process user inputs and determine what graphics will be rendered to the screen. Each step of the stepper may be divided into multiple phases, like a user input interpretation phase, a physics phase, a collision resolution phase, and a graphics rendering phase.

Scrolling is a common need, and there are some traditional optimizations and gameplay conventions that apply to scrolling (such as not updating objects that are too far offscreen), so game engines often offer built-in support for it. A typical interface is that you have a scrollable stage object that you put things in, and you can make a camera object and put it in the stage too. Then you make a camera.setCoordinates(x, y) call to update what part of the stage is showing. You'll tend to make this call at some point in one of the stepper phases, any time after you've calculated the player's new physics position but before the graphics have been drawn.

2 points by akkartik 3370 days ago | link | parent | on: Errors in anarki stable

Thanks for those comments!

Yeah you're right. I'm going to switch to arc.sh everywhere.

Edit 15 minutes later: all done, though the commit histories are a mess. Let me know if I missed renaming the script in any places!

2 points by Oscar-Belletti 3370 days ago | link | parent | on: Errors in anarki stable

Now it works both from the command line and from emacs.

It's ok for me that the the flag is -n.

I think that the arc script was named "arc.sh" because the folder which is used by the news server is "arc", and it would conflict. We can either take back the "arc.sh" name or change the news server's directory to something else, perhaps "www" like in the master branch.

Edit: We should also change the flag of the default program name in inferior-arc.el (line 95):

    (defvar arc-program-name "arc --no-rl"
To:

    (defvar arc-program-name "arc -n"
2 points by akkartik 3370 days ago | link | parent | on: Errors in anarki stable

Ah, I see. Ok, I'll bring that back.

Thanks for explaining the reason for that flag.

Edit 15 minutes later: I've made the flag to disable rlwrap '-n' like in the master branch.

(I didn't pick the original flag, so I'm not attached to that name. I can change it if you want, I just want both branches to be consistent. I also renamed the script to 'arc' like in the master branch, just to make my life easier. I'll update the instructions at https://arclanguage.github.io next.)

2 points by Oscar-Belletti 3370 days ago | link | parent | on: Errors in anarki stable

Now from the command line I don't get any warnings.

However, running arc in emacs (I use arc.el and arc-inferior.el) gives an error:

    rlwrap: error: My terminal reports width=0 (is it emacs?)  I can't handle this, sorry!
rlwrap doesn't work in emacs and this IMO is the reason the previous script had the --no-rl option.
2 points by akkartik 3371 days ago | link | parent | on: Errors in anarki stable

Yes, I wondered what to do about that and who if anyone cared about all those features. Then I forgot :/ I'll create a more bare-bones but working script today.

Edit 14 minutes later: Done: https://github.com/arclanguage/anarki/commit/8764126812. Do let us know if you have any other problems. (I've only tested the script on Mac at the moment.)

2 points by Oscar-Belletti 3371 days ago | link | parent | on: Errors in anarki stable

I checked now, string mutation works.

But what about the other warnings? Is the "main: not defined" warning due to my racket version?

The second warning (./arc.sh: line 40: [: too many arguments) disappears if I change (in arc.sh):

    if [ $repl = '#t' ]
To:

    if [ "$repl" = '#t' ]
3 points by akkartik 3374 days ago | link | parent | on: Errors in anarki stable

Many thanks! I'll merge it to stable.

Edit: Done.

4 points by malisper 3374 days ago | link | parent | on: Errors in anarki stable

That looks like [this](http://arclanguage.org/item?id=17699) which should have been fixed back then.
4 points by akkartik 3376 days ago | link | parent | on: Errors in anarki stable

Well, we do also have 'official' which is exactly what was released.

I think the intent is that 'stable' is guaranteed to be backwards compatible with 'official'. So the real issue is that 'official' is stale.

2 points by zck 3376 days ago | link | parent | on: Errors in anarki stable

Hrm, perhaps it shouldn't be called "stable". That suggests a more-tested release cycle. Maybe something like "original" or "pure", or even "3.1-release"?

I like the idea of having something that's what was originally released, but doesn't really suggest that it's what someone should use if they're new to Arc.

2 points by akkartik 3377 days ago | link | parent | on: Errors in anarki stable

Gosh, I haven't looked at 'stable' in ages and it looks like it's mostly unmodified since 2009, before the Racket days. It's still using 'mzscheme', which isn't even in my Racket 6.3 distribution on Mac OS anymore. And I no longer remember where we hacked Racket to permit mutable strings :/ I didn't see any diffs in ac.scm that might be responsible. Maybe someone else knows?
3 points by Oscar-Belletti 3378 days ago | link | parent | on: A keyword param implementation

The iter macro doesn't work: when it reaches the last iteration it calls next which increases i to (len list) and then tries to get that element, causing an error.

Edit:

The iter macro has another problem: it evaluates list to many times:

    arc>(= counter 0)
    arc>(iter a (map [do (++ counter) _] '(1 2 3 4)) (pr a))
    1234nil
    arc>(/ counter 4)
    11
Note: to make the iter macro not throw any errors I modified the while:

    (while (< ,i (len ,list))
      (= ,var (,list ,i))
      ,@body
      (++ ,i))))))

The actual dispatch happens at runtime, but the cool thing is that it verifies that multimethods cannot overlap, and it does this verification at compile-time (specifically at macro-expand-time)

As complex as Racket is (especially its macro system), this is a good example of how that complexity can sometimes enable very cool things as a library (like compile-time verification of non-overlapping multimethods)

4 points by Pauan 3379 days ago | link | parent | on: Implicit importing

Also, another interesting point: if you move away from our typical text-based ways of editing code, it may be possible to achieve convenience and performance:

http://unisonweb.org/2015-05-07/about.html#post-start

In Unison, you do not need to import code. That's because every variable is actually the hash of its definition, and when you use that variable in your code, it automatically "imports" the hash. That also means that your code contains only the variables that you actually use.

You do still lose some correctness, though. As an example, if you have a function and you change the definition of that function, it will create a new hash (for the new definition), but the old hash is still being used in your code.

Which means that you have both the old version of the function and the new version of the function existing at the same time. So you need to manually update all of your code to use the new hash:

http://unisonweb.org/2015-06-12/editing.html

This problem hasn't been solved yet, but there's been discussions about providing some tools that can automate certain tasks (such as replacing an old hash with a new hash, in your entire code base).

Assuming the above problem is solved, it may actually be possible to have convenience, correctness, and performance with an auto-import system.

But Unison's approach doesn't work with existing languages, so my point that "the language needs to be designed specifically for auto-imports" still stands.

1 point by akkartik 3380 days ago | link | parent | on: Implicit importing

Yes, I love the trend towards micro libraries because they encourage people to pull in precisely the functionality they need. That aspect of Node is not part of the problem.
2 points by Pauan 3380 days ago | link | parent | on: Implicit importing

Actually, because Node.js has a proper module/namespace system, people are instead encouraged to create a lot of micro libraries (e.g. less than 200 lines of code).

So rather than importing a single big library in your application, you would instead import a lot of micro libraries (each of which is versioned separately, and might have their own dependencies).

There are pros and cons to big libraries, and pros and cons to little libraries. I think languages should have good support for both.

4 points by Pauan 3380 days ago | link | parent | on: Implicit importing

I've thought about this problem for several years now.

The following properties are desirable:

- Convenience (auto-imports, imports not needing to specify the variables that are used, etc.)

- Correctness (ensuring that your code uses the variables you expect it to use, and that it doesn't easily break by changing some random unrelated file)

- Performance (only loading the functions/variables that you actually use)

Unfortunately, it is probably impossible to get all three properties at the same time.

By making code more convenient (e.g. implicit imports), you make it harder to reason about code ("I'm using the variable foo, but where does it come from?"), and you make it more likely for there to be variable conflicts (two different files defining a variable with the same name).

You also make it more likely for your program to break when upgrading a library to a new version, or even just when making a change to a seemingly unrelated file.

And if there is a variable conflict (which will happen), then you need some way of disambiguating, which generally requires you to specify which file should take precedence. So in the situation where there are variable conflicts, you lose all of the convenience benefits for that variable.

(And if you choose to not disambiguate, and instead ignore the variable conflicts, then you lose correctness, so that only works for very small projects)

Convenience also conflicts with performance. The language doesn't know which variables are defined in which file, so if you use the "foo" variable, it has to search through all of the files in order to determine which file contains the "foo" variable.

That also means your language needs to define which directories it will automatically import files from (you don't want it searching through your entire harddrive!)

Various languages (including Python[1] and Node.js[2]) have defined their own rules for determining which folders will be searched through for files, so this is a solvable problem.

But unlike Python and Node.js, implicit imports requires it to also search through all of the files inside of those folders... this is obviously an expensive operation.

You could make it work. If your language allows you to pre-compile a binary, then it only needs to lookup the files once (at compile-time). And with tree shaking, the final binary would only contain the variables that you actually use.

You would lose some correctness, and you would lose compile-time performance, but some people would consider the gains in convenience to be worth it.

Arc, however, does not have a separate compile-time, so it would need to do the folder/file lookup every single time you run any Arc program. I think the performance loss would be too great for any medium or large programs.

----

By the way, there's another interesting issue... mutually recursive functions:

  (def my-even (a)
    (if (is a 0)
      t
      (my-odd (- a 1))))

  (def my-odd (a)
    (if (is a 0)
      nil
      (my-even (- a 1))))
When the compiler is compiling the "my-even" function, it encounters the "my-odd" variable, but the "my-odd" variable hasn't been defined yet, so the compiler treats it as a global variable and tries to automatically import it.

If there is a "my-odd" variable defined in another file, then the "my-even" function will call that variable, rather than the "my-odd" variable defined in the current file.

There are various ways to fix this problem, probably the easiest of which is to define the "my-odd" variable before it is used:

  (= my-odd nil)

  (def my-even (a)
    (if (is a 0)
      t
      (my-odd (- a 1))))

  (def my-odd (a)
    (if (is a 0)
      nil
      (my-even (- a 1))))
This means that all variables need to be defined before use, which is contrary to the way Arc works. Also, if you forget to define a variable before use, then your program is buggy. And it is quite easy to forget.

So I think an auto-import system could work, but your language has to be designed around it. You can't just add an auto-import system to an existing language, because of these (and other) issues.

----

* [1]: https://docs.python.org/3/glossary.html#term-import-path

* [2]: https://nodejs.org/api/modules.html#modules_loading_from_nod...

3 points by akkartik 3382 days ago | link | parent | on: A keyword param implementation

Just a quick note before I take a deeper look this weekend: I copied the ':arg' syntax from Common Lisp. Racket tried to follow that as well, though they've added their own tweaks to it. Smalltalk and Objective-C have a trailing colon. Needless to say, you shouldn't feel constrained by any of these approaches.
2 points by mpr 3382 days ago | link | parent | on: A keyword param implementation

So I'm not sure if the argparse function is completely obsoleted by keyword args, but it certainly is in the use case above. With the keyword arguments I've written, you can pass arbitrary code as the default value. So I've rewritten the scrape function above like this

    (ndef scrape (addr --keys (outfile ((tokens addr #\/) -1)) use-ssl)
      (prn outfile)
      (prn use-ssl))
Yes, there is some funny variable capture business going on here. But I think it's interesting to think about anyway :)
3 points by akkartik 3382 days ago | link | parent | on: Keyword args

As it happens, keyword args were one of my earliest bunny trails, and I obsessed over them for probably five years.

Here's my earliest post about keyword args: http://arclanguage.org/item?id=10692. (Follow-up much later: http://arclanguage.org/item?id=13083)

That got me working on a fork of Arc with keyword args support: https://github.com/akkartik/arc. Original discussion about it: http://arclanguage.org/item?id=12657.

That eventually spiraled out into a whole language with easy keyword args at its core: http://akkartik.name/post/wart. Milestones along the way: http://arclanguage.org/item?id=12814; http://arclanguage.org/item?id=12943; http://arclanguage.org/item?id=15180.

Supporting keyword arguments also led me to param aliases: http://arclanguage.org/item?id=15254. Then to Haskell's as-params: http://arclanguage.org/item?id=16970. Then to multi-word functions, kinda inspired by Objective-C's keyword arguments: http://arclanguage.org/item?id=17233.

Finally -- 3.25 years after I started Wart -- I found a foundational bug in it: http://arclanguage.org/item?id=18371. I eventually fixed it, but the fact that it lay unnoticed for so long killed my motivation to continue supporting keyword arguments. At some point along the way it's become a higher priority to make code testable, and if you have thorough tests then how well some tiny slice of code reads seems a lot lower priority given how complex I found it to create a 'closure' of mechanisms that work seamlessly together and don't have any special-cases: http://akkartik.name/post/readable-bad. So these days I try to ignore keyword args lest my madness return :)

3 points by mpr 3382 days ago | link | parent | on: Labels macro in arc

Got the CL version from here http://www.pipeline.com/~hbaker1/MetaCircular.html. Someone on ##lisp IRC linked me when I was asking about how to write labels. Since I'm curious too, I'll do a similar analysis on CL and post the results.
3 points by akkartik 3382 days ago | link | parent | on: Labels macro in arc

Since I'm on a roll here with my speculatin': also don't correlate how a post is received with how long it takes to get comments. I just didn't see your Y-combinator post for the 8 days before I responded. Which is a complete outlier for me on this Forum and hopefully not a harbinger of things to come.. :/
3 points by akkartik 3382 days ago | link | parent | on: Labels macro in arc

That's very interesting analysis. I'm curious to see where you got the original Common Lisp version from. Is there some reason CL requires two withs?
4 points by mpr 3382 days ago | link | parent | on: Labels macro in arc

Hah! Yes, I was a little discouraged. Thanks for letting me know! I have some stuff in mind, expect to see more soon :)
3 points by mpr 3382 days ago | link | parent | on: Labels macro in arc

Yes, this simpler macroexpansion is all that's needed, apparently. When I first ported the code above I didn't have an appreciation for what labels needed to do; I just ported it. Thanks for making me think about it a little harder. Here is an implementation that will macroexpand to only one with. I've included a sample macroexpansion, as well as the results of running two functions. One of them, (collatz-seq), uses only simple recursion. The other, (parity), uses mutual recursion.

    (mac labels (fns . forms)
      (with (fnames (map car fns)
             fbodies (map (fn (f) `(fn ,@(cdr f))) fns))
        `(with ,(mappend (fn (name) `(,name nil)) fnames)
           (= ,@(mappend (fn (f) `(,(car f) ,@(cdr f))) 
                         (zip fnames fbodies)))
           ,@forms)))

    (def collatz-seq (n)
      (labels ((collatz (n)
                 (if (even n)
                   (/ n 2)
                   (+ (* n 3) 1)))
               (worker (n seq)
                 (if (is n 1)
                   (cons n seq)
                   (worker (collatz n) (cons n seq)))))
              (rev (worker n '()))))

    (def parity (n)
      (labels ((even (n)
                 (if (is n 0)
                   'even
                   (odd (- n 1))))
               (odd (n)
                 (if (is n 0)
                   'odd
                   (even (- n 1)))))
              (even n)))

    (prn (macex1 '(labels ((even (n)
                             (if (is n 0)
                               'even
                               (odd (- n 1))))
                           (odd (n)
                             (if (is n 0)
                               'odd
                               (even (- n 1)))))
                          (even n))))

    (prn (parity 17))
    (prn (parity 24))

    (prn (collatz-seq 21))

    ;; ---------------------------------------------

    ;; (with (even nil odd nil)
    ;;   (= even (fn (n)
    ;;             (if (is n 0)
    ;;               (quote even)
    ;;               (odd (- n 1))))
    ;;      odd (fn (n)
    ;;            (if (is n 0)
    ;;              (quote odd)
    ;;              (even (- n 1)))))
    ;;   (even n))
    ;; 
    ;; odd
    ;; even
    ;; (21 64 32 16 8 4 2 1)
4 points by akkartik 3383 days ago | link | parent | on: Labels macro in arc

By the way, I was sad that you stopped submitting Tcl links after the one :) Don't be discouraged that there was no discussion on it. Sometimes I can't think of anything to say at the moment, but I still enjoy the link.
More