Arc Forumnew | comments | leaders | submitlogin

I _think_ it's some weirdness with the nil/empty list thing. I was getting a case where (str x) resulted in the string "nil", but whatever that object was was not treated as nil, for example in conditionals.

Don't worry about it! I haven't even run them in a long time, until this week -- I figured I could get teardown functionality working, then remove the hack around running Anarki tests (https://github.com/arclanguage/anarki/blob/master/tests.arc#...).

Thanks for adding the instructions.


Ouch, have the tests for unit-test.arc been failing for the past year? :( :( Very sorry about that. I see the failure now.

I somehow forgot that unit-test.arc has its own tests. Could you post the instructions for running the tests in the Readme? That would also have the salubrious side effect of showing people a way to run a bunch of existing tests.

Edit: I've added some instructions for running unit-test.arc tests to the Anarki readme: https://github.com/arclanguage/anarki/commit/0913288ec1477f2.... Hopefully that'll help remind me.


As it turns out, it's one test failure (not sure how I got the data into a weird state). It's in the test `tests-are-wrapped-to-create-test-result-template`: https://hg.sr.ht/~zck/unit-test.arc/browse/tests.arc?rev=def...

It's failing because `(type (inst 'foo))` is different in Anarki than Arc. It's a simple change to make it work; I just want to do two things before I stop looking at it:

1. Look deeper into the template inconsistencies. Thanks for the files about this in Anarki. 2. Decide if I want to cut support for Arc, or make this code work in both. This might just involve killing the test, as it's not the _most_ useful test.


Great.

What are the two test failures in?


Yeah, that seems better. I'm still tracking down two test failures, but they're not because of this. I think templates now are of type 'tem, not type 'table.

I tried to make some changes to () instead of nil, and I was not a big fan of how it looked. I found it very unusual that unless quoted, parentheses mean function application. Letting () be the way to write the empty list (and I believe it worked differently quoted from unquoted, but I'm not sure offhand) completely breaks my mental model of how Lisps are parsed.

But with the revert, things look good. Thanks.

3 points by Goladus 1931 days ago | link | parent | on: Why I'm betting on Julia

I think the main reason Julia hasn't taken off is they arrived rather late to the party in terms of their target audience. Using Python and R to script libraries (or binaries) compiled in C and Fortran already had the momentum in the data science space. Julia first appeared in 2012 [1], which is also same year as the initial release of Anaconda [2], essentially a packaging and streamlining of what many in the scientific community were already doing with Python and R. With deep learning and data science really taking off in popularity the last few years, Python was the ecosystem of choice for most.

Julia might have a bright future. It seems to have a small but thriving community and its ambitions match what a lot of people want out of a programming language. The problem is that at this point "Python driving C+Fortran" is the 200 lb behemoth they're competing with, and face nontrivial competition from a host of other languages (R, Matlab, Go).

---

[1] https://julialang.org/blog/2012/02/why-we-created-julia/

[2] https://web.archive.org/web/20181012114953/http://docs.anaco...


Is the problem the equality between nil and the empty list, or did that just expose some unknown flaw with the way Arc deals with macros?
5 points by suzuki 1931 days ago | link | parent | on: Semi-Arc with first-class continuations

It is my pleasure.

As I referred in README.md, the implementation of this arc's continuations is based on https://github.com/nukata/little-scheme-in-java. If you are planning to port this arc to JavaScript, I suggest reading https://github.com/nukata/little-scheme-in-typescript which implements the continuations in the same way in TypeScript.

And in the latter, the display function https://github.com/nukata/little-scheme-in-typescript/blob/v...

  c('display', 1, x => {
      write(stringify(fst(x), false));
      return new Promise(resolve => {
          runOnNextLoop(() => resolve(None));
      });
  },
is applied asynchronously, in a sense, as follows: https://github.com/nukata/little-scheme-in-typescript/blob/v...

  case ContOp.ApplyFun: // exp2 is a function.
      [exp, env] = applyFunction(exp2, args, k, env);
      if (exp instanceof Promise)
          exp = await exp;
      break;
This means the web page is still interactive during the evaluation effectively. Here is an example: https://nukata.github.io/little-scheme-in-typescript/example. Click the "Load" button twice and you will see two "yin-yang puzzle" threads run on the page. Click the "Stop at Writing" button twice to stop them.
2 points by rocketnia 1931 days ago | link | parent | on: Semi-Arc with first-class continuations

It's great to see you again! Sometimes I've wondered about the status of Semi-Arc. Looks like you've been working on a number of other Lisp implementations in the meantime! Pretty exciting. Thanks for sharing this update with us.

As someone who ported Rainbow line by line to JavaScript, I have to say, the size of the codebase can be quite daunting. A smaller implementation sounds a lot easier to work on.

3 points by suzuki 1932 days ago | link | parent | on: Semi-Arc with first-class continuations

This arc seems faster than rainbow in start-up, but slower in execution. On MacBook Air 2019, for N-queens puzzle (N = 9 and 11),

  $ time java -jar arc.jar ~/tmp/arc-in-java-0.2.0/9queens.arc 
  352
  
  real	0m1.634s
  user	0m3.056s
  sys	0m0.159s

  $ time java -jar arc.jar ~/tmp/11queens.arc 
  2680

  real	0m27.865s
  user	0m29.582s
  sys	0m0.242s
and

  $ time java -jar rainbow.jar -q -f ~/tmp/arc-in-java-0.2.0/9queens.arc 
  *** redefining no
  *** redefining map1
  *** redefining pr
  *** redefining list
  352
  
  real	0m2.279s
  user	0m6.492s
  sys	0m0.246s

  $ time java -jar rainbow.jar -q -f ~/tmp/11queens.arc 
  *** redefining no
  *** redefining map1
  *** redefining pr
  *** redefining list
  2680
  
  real	0m9.812s
  user	0m14.462s
  sys	0m0.360s
where

  $ java -version
  openjdk version "11.0.7" 2020-04-14
  OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.7+10)
  OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.7+10, mixed mode)
As for the code size, this arc's arc folder has 4,626 lines of Java 11 while rainbow's src/java/rainbow folder has 14,520 lines (72,190 lines including auto-generated ones) of Java 5.

In short, this arc is a small-scale implementation of Arc.


Then again, who knows how long it will take to fix this problem. I'll roll back for now.

Edit: I feared that rolling back may also be non-trivial, but there were only some minor conflicts. Do a `git pull`! All tests are passing, and the bugs here should be fixed now.

Sorry about all this confusion and back-and-forth spanning a year. I'm going to back off on this change now. I think none of us have the bandwidth for a change this radical.


Let me figure out where in the ecosystem I'm getting the nil value from. I suspect I need to switch a bunch of usages of nil in my codebase to () or '().

Interesting concept. I feel like I'd be ok with it? I want to say we should bind `nil` to `'()`, so existing code would continue to work, but I might be overindexing on compatability and what I'm used to.

I will admit to not being super sure what the real differences between nil and '() are. Presumably it's more than "what is the human-readable representation of the value that terminates a list/is the false value". But I'm not sure what. Also, is there a difference between the quoted and unquoted version? It feels odd to write () in a repl unquoted -- usually, I expect parens to mean a function or macro call.

Prior discussions I've found:

* https://github.com/arclanguage/anarki/pull/145#issuecomment-... * http://arclanguage.org/item?id=11723 (ten years ago Tuesday!) indicates using '() helps with Racket interop * http://arclanguage.org/item?id=21047


At least to me, this is expected. The commit you pointed out above switched the null value to '(). The symbol `nil` still evaluates to (). But the need for evaluation implies that it isn't available in contexts that are not evaluated, such as function arguments or in this particular slot of `withs`.

Like I said, happy to revert it if you don't like it. The whole thing came up because of this conversation: https://github.com/arclanguage/anarki/pull/145#issuecomment-.... The motivation was to simplify the Arc implementation. We already have a nil representation in the underlying Racket; it seems unnecessary to so bend over backwards to switch it to something else.


If I found a way to "roll forward" and lose all trace of nil from the language, would that be acceptable?

(I tried briefly but haven't managed it yet. So it's probably faster to just roll back. I'm just curious about the question.)


Other weird thing I'm seeing:

Arc3.2:

    arc> (withs nil 3)
    3
Anarki:

    arc> (withs nil 3)
    Can't take car of nil
      context...:
       /usr/share/racket/collects/racket/private/kw.rkt:594:14:  withs
       /home/zck/programs/arc/anarki/ac.rkt:647:0: ac-call
       /home/zck/programs/arc/anarki/ac.rkt:1398:4
       eval-one-top12
This is a minimal example from something I found in unit-test.arc. It's some macros related to setup code -- if there's no setup, I currently generate something like `(withs nil 3)`. But that errors in Anarki.

You can explicitly pass no arguments. In Anarki:

    arc> (withs () 3)
    3

Oh nice. Thank you.

Yeah, just to make sure, I `git bisect`ed it down, and the behavior changed at this commit: https://github.com/arclanguage/anarki/commit/b321fb0c0273d1c...

Yeah, it seems pretty clear this is related to `nil` vs `()`. This is pretty neat. Some implications:

    arc> (def f (x) (prn x (if x " truthy" " falsy")))
    arc> (f nil)
    () falsy

    arc> (mac m (x) (f x))
    arc> (m nil)
    nil truthy
What's more, the problem is within macroexpansion, the part that conceptually should be the same between functions and macros:

    arc> (macex1 '(m nil))
    nil truthy
Here's the definition of `ac-macex` in ac.rkt:

    (define (ac-macex e . once)
      (if (pair? e)
          (let ([m (ac-macro? (car e))])
            (if m
                (let ([expansion (apply m (cdr e))])
                  (if (null? once) (ac-macex expansion) expansion))
                e))
          e))
I think the discrepancy lies somewhere between `ac-global-call` for calling functions and using a direct `apply` for macros.

I watched this just enough to reassure my initial suspicion that I'd seen this a few months ago. It's certainly in my neighborhood, but there are a lot of words in the first few minutes that mean different things to different people. It seems possible that what he means by obesity is just "running slower than optimal". I don't think that matters much. He talks about waste and excess, but it's actually nice to be able to ball up a paper and start afresh when you're writing a novel or a paper. Waste isn't always a bad thing. Civilizations are in some ways defined by what they waste (https://www.ribbonfarm.com/2012/08/23/waste-creativity-and-g...). So I wish I had a more concrete motivation for what he's aiming towards, so I could assess if the waste he's concerned about is something I'm concerned about.

Regarding replacing pointers with depth lists, this video has a more detailed explanation, which is what I'm going by: https://dyalog.tv/Dyalog18/?v=hzPd3umu78g.

On one hand I'm glad to see radical ideas like this. As I've struggled to make heap allocations safe and thought about how Rust does it, I've often felt acutely uncomfortable that things have to be as they are. So maybe he's right and pointers are refined sugar that we can thrive without.

But I'm not yet convinced by this particular presentation. Depth lists seem to be basically manually allocated memory that is managed by array indexes rather than addresses. All the benefits derive from them having a consistent lifetime. That gives up a lot of the flexibility of heap pointers! Rather than frame this as, "here's a mechanism that is applicable everywhere," which seems patently false, I'd like to see more of an argument that yes, there are programs you can't write without pointers, but you don't actually need them. From this perspective, Rust's position in https://rust-unofficial.github.io/too-many-lists seems more honest:

"I hate linked lists. With a passion. Linked lists are terrible data structures. Now of course there's several great use cases for a linked list... But all of these cases are super rare... Linked lists are as niche and vague of a data structure as a trie."

(Even this is inadequate. Rust is not just giving up linked lists, it gives up up any data structure that may have two pointers to a single allocation. Doubly linked lists. Trees with a parent pointer. And on and on. Maybe all these data structures are super rare. But it gives me the warm fuzzies to know my language can support them. And I need a stronger argument to give them up.)

The trade-off Mu makes is different: you can have any data structure you want, but refined-sugar will cost you performance to ensure safety, and you'll have to deal with a little additional low-level complexity to juggle two kinds of pointers. I prefer this trade-off to anything else I've seen, but I'm still not quite happy with it. I wish there was something better, or some argument that would persuade me to settle with one of these solutions.


Aaron Hsu discusses the cumulative effects of complexity and value of simplicity; reminded me a lot of our recent discussions here.

One of his main focal points is that "Generalized pointers are the refined sugar of programming"

He points out the size and complexity of an expression tree rendered as linked list nodes using pointers - the nodes are large, and the memory layout is unknown and unpredictable. We have to rely on GC to manage it all.

He proposes an alternative structure based on APL using a "depth list" format. The nodes are in one list, and their location in the tree is in a parallel list. This makes memory usage and layout predictable, and makes the structure much more amenable to generalized memory operations.

Thoughts?


Yeah, when I dug more into the spec, I found out that <p> tags can't be self-closed. Only void and foreign elements can be self-closed (https://html.spec.whatwg.org/multipage/syntax.html#start-tag...).

The closing </p> can be omitted if the next tag is one of 25 different tags (https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p, see "tag omission").

What I ended up coding was that the (para) call will always add a closing tag. This is more consistent with the spec -- as far as I can tell, the closing tag is never required to be omitted.


I support updating html.arc in general, particularly to bring it more in line with HTML 5. I think I added support for data attributes at some point, but some things like an async attribute for script tags are still difficult, because it expects all attributes to have a value.

In a perfect world, though, it would use a structure like Racket's html parser[0], and could be used without namespace pollution.

In a more perfect world (to me), Arc would support XML natively. But I'm probably the only person here who would want to write HTML directly in an Arc script.

[0]https://docs.racket-lang.org/html-parsing/index.html


I like this whole discussion, akkartik and shader. You've both made a lot of excellent points, and I found myself nodding along to one comment only to nod along to the next as well.

Right now I have a lot to say about the math analogy in particular. (The rest of the discussion has been great too.)

Mathematics has a lot in common with even the messy parts of programming.

Mathematics involves a lot of computation, and not necessarily of the digital computer kind or the arithmetic kind. If a human reader has to look up a definition of a word they don't know, then they're practically having to perform a manual lambda calculus substitution step. A lot of popular concepts in math are subject to transitive closure, which gives them indirect consequences hidden away on non-obvious reasoning paths. A lot of topics in math have to do with metareasoning and higher-order reasoning. Altogether, the kind of effort it takes for a human reader to understand a mathematical claim can involve a lot of the same things that on a computer we'd consider program execution.

As much as people might not like to admit it, mathematical theorems don't always take the form of the precise "don't lie" abstractions shader is describing. When a proof has a flaw in it, people still make use of the theorem, either by conjecturing that it's true anyway for some as-yet-undiscovered reason, or by explaining why the flaws in the proof don't matter in this context. In domains where it makes sense to change the mathematical foundations (e.g. deciding to use a different logic or a different set theory), a lot of the bread-and-butter theorems and concepts of mathematics can end up having flaws in them, but instead of coining new names for all those theorems and concepts, it's easier to use the same names and merely describe all the little patches that are necessary for them to work. So I think mathematics makes use of its share of abstraction-breaking techniques, techniques a software engineer might simulate with some combination of dynamic scope, side effects, preprocessing passes, code-walking, aspect-oriented weaving, dependency injection, or something like that. (This is mostly visible to people who are trying to make the math precise enough that a computer can verify or assist with it.)

Of course, math is not quite the same as software engineering. Unlike software, math is written primarily for humans to understand, and it only incidentally has computational aspects. This influences the kind of BS that's possible with math, both for better and for worse.

- For the worse: Once a mathematical argument goes on for a bit too long, humans rarely have the diligence to require that every single part of that reasoning makes sense; they're content to give leeway to some parts that they already feel they understand clearly. Some popular points of leeway end up serving as the foundations for a lot of mathematics, and we might call those the "syscalls" of math. Of course, every paradox of barbers and liars and time travel and infinity and whatnot reveals that humans are stubbornly hospitable to inconsistent ideas, and software engineering shows that humans' leeway leaves room for bugs on an extremely frequent basis.

- For the better: Since humans are in the loop when it comes to reading and sharing mathematical results, the kind of BS that confuses and dismays people has some trouble thriving. If the effort it takes to apply a mathematical concept is too full of hacks and spaghetti, people probably won't find it to be their favorite concept and won't share it with each other. (Of course, there seem to be some concepts which make a lot of sense once people get to know them, but still seem to require a rather circuitous route to learn about. In this way, people can end up being enthusiastic about parts of math that look, from the outside, to be full of nope.)

Considering all that mess, math nevertheless has a reputation for leakless abstractions, and that reputation is well deserved. "The study and development of leakless abstractions" would be a fitting definition of mathematics. The mess comes from the fact that humans are the ones discussing, developing, identifying examples of, and using the abstractions.

Likewise, even if software engineering deals with a big mess of leaky abstractions a lot of the time, the leakless ones are an important part of the design space. Unlike hardware, software code is a mathematically precise chunk of data, and the ways we transform it and compile it are easily a mathematical topic with lots of room for leakless abstractions. The reason (and perhaps the only essential reason) for the mess is that humans are the ones discussing, developing, making hardware for, and using the software.

While it's clear that math and software are two worlds with notable differences -- distinguished at least by the presence of computer hardware that gives a user meaningful value out of using software they don't know how to maintain -- I believe software could very well develop a popular perception as a world of Platonic forms, the same kind of perception math has. It's not that farfetched for people today to say, "obviously, as soon as you put an algorithm on a device and execute it, it's not the same algorithm." What if someday people say nothing we build or do can be a "true" algorithm because an algorithm is a Platonic concept that our world can only approximate?

Is that the right perception for software? Well... is it the right perception for mathematics? I think the perception doesn't matter that much one way or the other. Everyday software can have leakless abstractions of the same kind everyday mathematics is known for, and many of math's abstractions are actually riddled with holes in ways software engineers might find familiar.


"And a self-closed tag lets you know there's no body, which is also a plus."

A <p> tag does have a body. HTML like this:

  <body>
    <p>First paragraph
    <p>Second paragraph
  </body>
Is treated basically like this:

  <body>
    <p>First paragraph
    </p><p>Second paragraph
  </p></body>
If instead you write:

  <body>
    <p />First paragraph
    <p />Second paragraph
  </body>
Then... Well, I should try to be precise....

It looks like the HTML specification defines this as a "non-void-html-element-start-tag-with-trailing-solidus parse error." The spec says that in this case, "The parser behaves as if the U+002F (/) is not present," but also that "[browsers] may abort the parser at the first parse error that they encounter for which they do not wish to apply the rules described in this specification."

I don't know of any browsers that abort the parsing altogether, so it's still reliable to write the HTML that way.

However, the similarity to XML is actively misleading in this case. When you process that document as HTML, you still get structure like this:

  <body>
    <p>First paragraph
    </p><p>Second paragraph
  </p></body>
But when you process it as XML, you get structure like this:

  <body>
    <p></p>First paragraph
    <p></p>Second paragraph
  </body>
So if you're trying to write a polyglot HTML/XML document, self-closing <p /> tags still probably aren't a great option. Closing the paragraphs explicitly, like so, makes it clearer how the structure will end up:

  <body>
    <p>First paragraph</p>
    <p>Second paragraph</p>
  </body>
---

I think modern HTML does have a reliable common subset with XML. Modern HTML treats <br></br> and <p /> as parse errors, but it treats <br /> and <p></p> as valid. To write HTML/XML polyglot content, you just need to pay attention to whether you're dealing with a void element like "br" or a non-void element like "p".

Incidentally, why use an HTML/XML polyglot at all? There are at least a few situations where it can make sense:

- You're serving it as HTML, but (at least someday) you might want to use an XML-processing tool on it or serve it as XHTML.

- You're trying to serve it as XHTML, but you're worried you'll mess up your server configuration and serve it as HTML by mistake.

- You're confident you can serve it as XHTML today, but you have a backup plan to serve it as HTML if needed. In particular, you're afraid someday your XHTML will be invalid due to a bug in your code, a bug in a browser, an intentional spec violation in a browser (e.g. for security or user privacy), or a backwards-incompatible change in the spec. The XHTML spec dictates that an invalid page won't be displayed at all, so if you end up with invalid XHTML for any of those reasons, your site will be rather unusable until you can implement a fix. If that happens at a time you're not ready to drop everything and look at the bug in depth, then you can make a pretty quick switch to serving it as HTML, and most of the page will display again.

Because of the brittle handling of errors, XHTML still hasn't really gotten off the ground. So it seems like the primary value of the HTML/XML polyglot is to serve a document as HTML but use XML-processing tools on it behind the scenes.

---

A side note...

In the very early days of XML and XHTML, when people were trying to make their HTML pages as XML-like as possible, many browsers would interpret something like <br/> as an element with the tag name "br/". That's why people got into the habit of putting in a space like <br />. That way those browsers would instead interpret the / as an attribute named "/", which was mostly harmless. Nowadays, the space is pretty much vestigial and you can just write <br/> if you want to.


I think it is according to the spec, but I don't do enough frontend to really know.

And I find it actually easier to read, because it's properly nested with a closing tag. And a self-closed tag lets you know there's no body, which is also a plus.


I think if your proposal is always strictly better than not closing, then I support it. Programming in Arc tries to take away the need to read raw html, so we can handle a little extra verbosity in the emitted code in some situations.

<3
3 points by zck 1961 days ago | link | parent | on: Writing HTML using Racket and X-expressions

I'm using a static site generator I wrote in arc. My workflow is as such:

1. Write my entries in an org file that contains all the entries.

2. Narrow to the subtree (C-x n s), and export to an HTML buffer (C-x C-e h H)

3. Manually copy the relevant part of the overly-large HTML file into a new file (blog-entry-name.html). This is only the content of a page; it does not include any headers, footers, navbar stuff, or the html wrapper around the body.

4. Insert by hand a serialized arc template, containing three keys: a url slug, the title of the page, and the publication date.

5. Update the frontpage of my site to link to the new page. This file is similarly formatted: an arc template, then html content.

6. Update a file that indicates which pages should go in the sidebar of my site.

7. Run an arc command to generate the entire static site.

8. Check it out locally, then rsync the content to my nearlyfreespeech.net server.

Obvious places for improvement are 3, 5, and 6.

I was thinking about this recently. There's something quite fun in writing extremely personal software. This is not a tool that is designed to be used by millions of people, and I'm ok with that. I'm actually quite happy with storing settings for the page and the html content in the same file! It seems like a neat hack to me.

More