Arc Forumnew | comments | leaders | submit | akkartik's commentslogin
2 points by akkartik 2712 days ago | link | parent | on: Clojure Anaphoric Macros

Thanks for that example. I see now that you mostly only need to worry about multiple evaluation for the `%test` branch; `%then` and `%else` should be exclusive anyway, and I'm not concerned about the growth in macroexpanded code when duplicating a few s-expressions.

You could still have repeated use within a '%then' or '%else' block:

    (aif (test)
      (something)
      (do %then %then))
But it should suffice to perform one evaluation in each branch. Cool! That seems simpler than some of the alternatives I'd been thinking about. I'd try to evaluate everything ahead of time and then realize that I shouldn't run `%else` if the `%test` returns a truthy value.

-----

2 points by adas 2712 days ago | link

Didn't consider a double `%then` or `%else`, thanks. Raises some interesting problems.

Generally the plan is to have a special character control whether it gets bound or inlined, probably `!`. So say `%test!` would get inlined like right now, while using `%test` would bind (and doing both would also be possible). But for a `%then` you'd generally only want to bind if there's 2+ so I could count usages instead.

-----

3 points by akkartik 2712 days ago | link | parent | on: Bugs and failures

I'm not sure I grok the precise boundary you're drawing here.

It seems clear that (car 10) is always a bug, so I'm with you there. However, non-existent files may be bugs in some situations. Perhaps you're just proposing giving programmers two distinct labels to use with discretion? If so I shouldn't get hung up on precise examples.

Are all unhandled failures bugs?

-----

2 points by aw 2712 days ago | link

A failure that is unexpected and unplanned for is a bug. Thus it's a bug if a file doesn't exist and my code doesn't handle that situation.

The boundary is what I want to happen in response to a bug vs. a failure. When I hit a bug, an actual bug, I want to capture the entire state and history of my program, to the fullest extent possible, so that I can find out why the bug occurred. I don't care if this a core dump is GBs in size or might be expensive to generate. If a bug occurs I want all possible information that might help me, everything that the language runtime can produce.

For failures, for expected failures, for failures I handle, I don't need to capture anything. I don't even need a stack trace. I don't need the language runtime to generate a stack trace every time I hit an expected, handled failure.

Existing languages don't allow me to do this. At the point where for example the "file not found" exception is being thrown there isn't enough information to tell whether that's a failure or a bug, so they have to be handled the same.

-----

5 points by akkartik 2714 days ago | link | parent | on: Arc REPL in emacs using cider-mode

Looks wonderful! Thanks for exposing me to some new keywords ("nrepl", "cider-mode")

-----

5 points by i4cu 2714 days ago | link

For anyone else who might be interested:

nRepl https://github.com/nrepl/nrepl

Cider https://docs.cider.mx/en/latest/

Cider-nRepl https://github.com/clojure-emacs/cider-nrepl

-----

3 points by akkartik 2717 days ago | link | parent | on: Try Arc down, will be back up soon

Thanks, Evan!

Remind me, is this running Arc 3.1 or Anarki? Some third option?

-----

3 points by evanrmurphy 2717 days ago | link

Hi Kartik, it's Arc 3.1. Cheers.

-----

5 points by akkartik 2721 days ago | link | parent | on: Clojure Anaphoric Macros

"In `acond`, `aif` and `awhen`, `%test` or `%t` gets replaced with the 'test' form. `%then` gets replaced by the 'then' form, and `%else` by the 'else' form."

    (aif 9 (+ 9 %else) (+ 10 %test))
    ; => (if 9 (+ 9 (+ 10 9)) (+ 10 9))
"If nested you can access the previous level by doubling the first letter of the symbol. For example, '%ttest' would get you the previous [containing?] 'test' form, while '%eeelse' would get you the 'else' form 2 levels up. In the `aand` and `aor` macros you can reference arguments by using a symbol of form `<star><num>` where 'num' is the 1-index of the argument. Previous levels are accessed by doubling the `<star>` character. So the second 'test' form of an `aand` can by accessed with `<star>2` and the third argument of the previous [containing?] `aand` would be `<star><star>3`." [Working around the crappy pseudomarkdown here.]

    (aand (+ 30 20) *1)
    ; => (and (+ 30 20) (+ 30 20))

    (aand 1 2 "third" (aand 33 **3))
    ; => (and 1 2 "third" (and 33 "third"))
Utterly bonkers, and I love the hack :)

-----

3 points by akkartik 2719 days ago | link

Staring at these examples again, another thought occurs to me: do these macros cause multiple evaluation, or are the equivalences above just loose? Can anybody tell? I can't tell just from skimming the implementation.

If they're doing multiple evaluations they're a lot less useful than Paul Graham's original anaphoric macros even if they seem superficially more powerful/expressive.

-----

3 points by i4cu 2719 days ago | link

> do these macros cause multiple evaluation

from the read-me:

"Note that as of right now the symbols are replaced by copying in the expression it references, not by binding to a common variable. Hence not suitable for using with expressions that cause side-effects or involve a lot of computation. That will be changed soon."

I presume, by reading this, these expressions will be evaluated each time they are triggered within the operation, so the answer is - yes they will. But, as the read-me also suggests, this is a "WORK IN PROGRESS" and the author has stated intentions to assign variables, which would solve the issue.

-----

2 points by hjek 2719 days ago | link

If you include * whitespace * between words and stars, they are not parsed as an emphasized section.

-----

2 points by akkartik 2734 days ago | link | parent | on: Show Arc: Debate (very alpha)

Very cool. Can you elaborate on this tantalizing sentence?

> My goal is to make a simple hybrid of HN/Lobsters and Feedly/Google Reader and DropBox/WeTransfer/Google Drive/OneDrive.

What parts/features do you want to take from each, and why do you think they should coexist in a single product?

-----

3 points by hjek 2734 days ago | link

> What parts/features do you want to take from each, and why do you think they should coexist in a single product?

DropBox/WeTransfer/Google Drive/OneDrive is just what people use to send larger files. I'm not a fan of those sites, but I have observed that people I know find Filezilla and FTP very intimidating. There's some other free apps for this, but either they're in PHP or they're too complicated to install or use or hack on. Droopy[0] works good for this, so as a start I just cloned what Droopy does; just to check whether Racket/SQLite is fast enough for this use case. (It is)

I like RSS as an idea and the fact that most sites have RSS feeds, but I think it's underappreciated by non-techies, who think that Facebook or Twitter are the only place you can "follow" stuff. For friends of mine to even consider using RSS for anything, I think it needs to be more accessible. I use Newsbeuter for reading RSS but I would never recommend it to non-techie friends, and it has some annoying bugs: Sometimes text is missing, podcast download is flaky at best so I have to copy the url out at get it with curl, and the interface becomes unresponsive when there's many feed items. So I'm thinking of giving it a try to make a web-based RSS reader that encourages sharing and that at very least I'd enjoy using myself. I think this would fit naturally with a News-style app, basically just having RSS feed items being shown alongside other people's posts, but perhaps tweaking the ranking in a way that external feeds don't overshadow local content (assuming that external feed items are votable). Some non-techies have shown me the Feedly app, which (I think) uses RSS, and (I presume) has some kind of social recommendation system, so I just think it would be nice with a free/libre web app doing something similar.

Then, the HN/Lobsters part is basically about having a minimalist web forum with tree structure replies and well thought out ranking algorithms and spam handling, instead of this thoughtless phpBB-style newest-first unstructured mess. One thing Lobsters does right here is that it notifies you when someone replies to a comment of yours. On HN it's possible for someone to reply and you just won't notice because it's so deeply buried in old threads. But I like all the different ways you can sort/view the content on HN.

Also, I've had some painful episodes trying to back up Wordpress sites. It's not pleasant, because there's little distinction between code and content which is stored in a mish-mash of database entries and folders. WP barfs all over the disk. That's something HN does right, because all the content is in the `www` folder. But, then file upload to HN is not really a realistic thing.

So I'm just trying to mix-and-match things I like from different software. A lot of ideas. Haven't gotten far yet! But I guess it's ok to share work-in-progress here.

If I didn't have dockerphobia and mild JS-intolerance, I'd probably just be running Mastodon or something.

[0]: https://github.com/stackp/Droopy/issues?q=is%3Aissue+is%3Acl...

-----

2 points by akkartik 2734 days ago | link

I'm wondering:

1. Are you trying to build an RSS reader? Or do you want to support RSS versions of pages on your site?

2. Are you building a file transfer service? Or are you imagining that people will want to share files on a HN-like website? I'm not sure why that would be a common use case.

-----

2 points by hjek 2734 days ago | link

> 2. Are you building a file transfer service? Or are you imagining that people will want to share files on a HN-like website? I'm not sure why that would be a common use case.

File upload wouldn't make sense on HN, Arc Forum or Lobsters, and I get that it's entirely on purpose that it's not present on those sites.

But I've been hosting a News site, and those of my friends who use it often submit links to something they've put on Dropbox or Google Drive, so I'm simply observing that this use case is present and that file upload is missing on my site. Perhaps it's because it's a different type of exchanges: a guitarist uploading a recording of a Charlie Parker solo, an artist uploading a drawing, etc.

Well, perhaps I should be using $CRAPPY_PHP_CMS_WITH_FILE_UPLOAD instead of News, but why can't I have well-structured content and file uploads at the same time?

It's also about future extensibility, ways of presenting these file uploads that I haven't thought about yet. I do occasionally write PHP for money. I don't have a very high opinion of that language, and I'd rather make something from scratch in Racket than write it as a Drupal plugin. Given the relative quantities of Drupal plugins and Racket web apps, that might be an unpopular opinion, but it shouldn't seem too strange on this forum. I'd prefer to make custom interactive web sites by extending a code base in Racket rather than PHP.

It's actually also for the use-case of being off-the-grid, yet needing to send/receive files to people in the same room, but not being able to find a USB stick. That situation happens surprisingly often for me. A zero-config web app is useful here because everyone have a browser on their computer (whereas not everyone have FTP / SSH / SMB).

> 1. Are you trying to build an RSS reader? Or do you want to support RSS versions of pages on your site?

I plan to do both. Building a social RSS reader integrated in a News-like app. And this News-like app would provide RSS feeds in the same way Anarki does. I image it wouldn't be too difficult.

-----


Absolutely valid criticism. I'd love to hear more about what you did between seeing the link here and navigating to that example program. I've been trying to build a path to gradually take programmers to an understanding of (this particular unconventional style of) assembly programming. For example, I'm curious how much of the Readme you read, and if you happened to notice that the Readme has an orientation on the x86 processor.

I also made a couple of tweaks to this particular example. I hadn't looked at it in a while. Thank you! https://github.com/akkartik/mu/commit/d6535f3382

-----

2 points by hjek 2734 days ago | link

I read the first section of the readme without really understanding much but also not expecting to as I don't know x86 assembly. Then I decided to at least give the examples a superficial look as I'd noticed the word newcomers in the readme. But I couldn't see the number `42` in a program meant to print `42`, so that's where I gave up.

Is this meant to be a tutorial for assembly noobs?

For comparison I think your readme for Wart[0] is more welcoming: Briefly explaining what it is and how to run it, and then straight onto a simple example that people can actually try out.

[0]: https://github.com/akkartik/wart

-----

2 points by akkartik 2734 days ago | link

That's a good point. I have similar instructions here, but they're in the second section, 3 screens down..

The audience is assembly-curious programmers, but you aren't expected to know any assembly. I just want to try to hook anyone interested in the goal. If you're interested in a stack you can understand from the ground up, I'm willing to try to explain things to you.

-----

2 points by hjek 2734 days ago | link

I might just be outside the target audience, for now. I don't even know any C. Realistically, I think it would have to be spoon-fed to me in some Bret Victor-esque crocodiles and eggs[0] manner for me to not lose focus.

I went through this absolutely fantastic SQL tutorial this week. Perhaps you might find their list of pedagogical principles[1] useful?

I think one thing that potentially could tempt me into low-level code would be making cool tunes[2][3].

[0]: http://worrydream.com/AlligatorEggs/

[1]: https://selectstarsql.com/frontmatter.html

[2]: https://www.youtube.com/watch?v=GtQdIYUtAHg

[3]: https://www.youtube.com/watch?v=qlrs2Vorw2Y

-----

2 points by akkartik 2734 days ago | link

I just made some tweaks to the Readme. What do you think?

https://github.com/akkartik/mu/commit/4650c8188f

https://github.com/akkartik/mu/blob/master/subx/Readme.md

-----

2 points by hjek 2734 days ago | link

Yea, I think that's more inviting.

I got to try out the point of compiling and trying out your programs now. `ex8`, `ex9` and `ex10` all segfault here.

Some time ago I was at a wedding, and I was terribly bored, until I found out that the guy to my left was writing washing machine software in assembly. In a way it seems awfully primitive, e.g. your `ex11.subx` is 350 lines long and prints out `.......` but I guess in certain systems it's the only option, and what's underneath it all in any system.

-----

2 points by akkartik 2734 days ago | link

Thanks for trying them out!

Those examples expect arguments at the commandline, and I chose not to perform error checking for an example :) The focus lay elsewhere for them. See the comment at the top for each.

ex11.subx is running a test for each of those dots :)

-----


Thanks for the questions! Part of the problem is that the repository isn't for a single 'language'. It's for my experiments for a new way to program that makes codebases easier to understand. As the biggest stress test for the new way, I've trying to create a whole stack that is easy to understand. But it's just a prototype, or rather a series of prototypes. I've tried to make each prototype self-contained, with a Readme and instructions for running it that will continue to work to this day.

Prototype 1 was a statement-oriented Assembly-like language built in Arc. You can still see it at https://github.com/akkartik/mu/tree/master/arc. I worked on it until about 2015. It's 3kLoC of code and 7kLoC of tests. The most substantial program I built in it was a hacky Lisp repl with syntax highlighting: https://github.com/akkartik/mu/blob/master/arc/color-repl.mu

Prototype 2 was a similar (but not compatible) language built in C++. It had the most work on it, and I also used it for teaching for a couple of years. I stopped working on it sometime this year. It's still available on the top level at https://github.com/akkartik/mu. It's 23kLoC of C code, and 5kLoC of Mu libraries. The most substantial program built in it was a 2-pane programming environment I used for teaching programming: https://github.com/akkartik/mu/tree/master/edit#readme. It's 12k lines of Mu code, about half of which is tests. You can see its sources colorized to be easier to read at the bottom of http://akkartik.github.io/mu.

Now I'm at prototype 3, SubX. It's in a very preliminary state. As above it is not intended to be compatible with existing prototypes. The previous prototypes were kinda-sorta designed to be easy to translate to native code, but they were still simple-minded tree-walking interpreters. I had hazy plans of gradually compiling them to native code from the top down, but that turns out to be beyond my ability. SubX starts from the bottom up, building an almost trivial syntax on top of raw native x86 machine code, and I'm gradually learning how to implement a compiler in it. Most people would say it's too hard to build a compiler in assembly these days when we have so many high level languages available. I'd like to see if it's manageable with the right framework for writing automated tests. So SubX starts out not with improvements to syntax, but to error checking and automated testing.

The most substantial program I've built in SubX so far is a port of the very initial version of Crenshaw's "Let's build a compiler" series[0]. All it does is read a number and emit native code to return that number in the exit status: http://akkartik.github.io/mu/html/subx/apps/crenshaw2-1.subx.... So SubX is still in a very early state.

SubX tests aren't inside the functions they test, they're just interleaved in the same file. Any label that doesn't start with '$' is the start of a new function. Functions that start with 'test-' are tests, and they all run when you run with a 'test' argument on the commandline.

The hope is to one day build a robust, hackable stack culminating in a high-level Lisp atop this infrastructure, a stack that leans into the Lisp tendency to fragment dialects by encouraging people to create incompatible forks -- while also making it easy (but not automatic!) to share code between the incompatible forks. It would still be some amount of work to copy the tests over and then make them pass, copying over bits of code at a time and modifying it as necessary. That's the sort of workflow I want to encourage rather than blindly upgrading software by running a package manager command. But before I can recommend it to others I have to see if I can get it to work. This repo is a test bed for eventually building tools to help people collaborate across incompatible forks.

Thanks for asking these questions! They're very helpful in understanding how others see the mess my repo has turned into. I'm going to try cleaning it up.

[0] https://compilers.iecc.com/crenshaw

-----

3 points by i4cu 2734 days ago | link

Ah... OK.

See I always move up to the top level directory and work my way down. So as I did this my reference point for understanding got completely mixed up.

I'd make a top-level dir called 'prototypes' with details like [1] you provided in this comment and then branch down.

* Also, I'd probably limit referencing Mu, specifically, in SubX. I don't think its helpful. Just remove this line:

  "We'll gradually port ideas for other syscalls from the old Mu VM in the parent 
  directory."
  
It doesn't add much value and takes people's attention away as they start trying and understand the relationship. If you need to just add notes at the bottom.

[1] "The hope is to one day build a robust, hackable stack culminating in a high-level Lisp atop this infrastructure, a stack that leans into the Lisp tendency to fragment dialects by encouraging people to create incompatible forks -- while also making it easy (but not automatic!) to share code between the incompatible forks."

Re: [1]

Yeah, if you can build a language that allows someone to build arc and even a clojure version of arc using the same base code, that would be cool. I'd immediately try spinning up a new version of arc with clojure's tables and table functions :)

-----

3 points by akkartik 2734 days ago | link

> I'd make a top-level dir called 'prototypes'... and then branch down.

Yeah, I've been planning a reorganization like that. Unfortunately the reorg is going to break all the links I shared before I thought of this :/ So I'm going to wait a bit before I make the switch.

> if you can build a language that allows someone to build arc and even a clojure version of arc using the same base code, that would be cool.

I'm not aiming quite there. That would be really hard to do, and then it would be impossible to keep in sync with existing language upstreams over time, given the underlying platform will be very different. The way I imagine providing something similar is this: there would be multiple forks of the Mu stack for providing a Clojure-like or Arc-like high-level language. But these languages wouldn't be drop-in replacements for real Arc or real Clojure. Also, each fork would try to minimize the number of languages it relies on to do its work, so you wouldn't immediately be able to run both Clojure and Arc on a single stack. Because every new language used in a codebase multiplies the comprehension load for readers. (I ranted about this before at https://lobste.rs/s/mdmcdi/little_languages_by_jon_bentley_1...)

Basically, I want to commoditize the equivalent of a Lisp Machine for any language. My goal is to help people collaborate across incompatible forks, _but_ the forks have to all have certain characteristics that no existing software has (because it all assumes rigid compatibility requirements).

-----

3 points by i4cu 2734 days ago | link

> Basically, I want to commoditize the equivalent of a Lisp Machine for any language. My goal is to help people collaborate across incompatible forks...

I'm struggling to understand so forgive me, but the reasons why and what you're doing seem to change or at least are many fold and thus hard to unpack. Or maybe the different prototypes are messing me up.

So let me see if I can unpack it (at least for myself :)

Your goals are:

1. To build prototype 'x' compiler/language that's more robust and easier to maintain because it has been built with testing capabilities in mind (I'm imagining a model with convenience features or set requirements to accomplish this).

2. Build prototype 'x' to permit developers to build their own compiler/language(s) that inherit the benefits from prototype 'x', thus making that process more enjoyable and more likely to succeed.

3. Permit/Encourage greater collaboration amongst developers, on separate prototype 'x' projects, because prototype 'x' is robust and developers are working under a shared model that has core concepts/features that act as a bridge for that collaboration to happen.

Does this seem right?

So is this a project that you're doing because you're passionate about it and you think it can change things (i.e. improve the lives of other people)? Or is this a product idea where you have assessed there's a need and you're going to fill it?

-----

4 points by akkartik 2734 days ago | link

Thanks for the probing questions! Yes, I don't mean to move the goalposts on my reasons. I feel like I'm reaching for something fundamental that could end up having lots of different benefits, mostly things I can't anticipate.

I don't actually care that much what the high level language is at the top. I'm biased toward Lisp :) so that's what I'm going to build towards. But if others want a different language I want to make it easy to switch the superficial syntax to suit their taste -- and convert all existing code on the stack so everything is consistent and easy to read. If some others want to add new runtime features, I want to make that easy too. Finally, I want it to be tractable to mix and match syntax and runtime features from different people and still end up with something consistent. Using tests at the bottom-most layers, and building more rigorous type systems and formalisms as necessary higher up.

The key that would make this (and much else) possible is making the global structure of the codebase easier to comprehend so that others can take it in new directions and add expertise I won't ever gain by myself, in a way that I and others can learn from.

Ignore the other prototypes in this repo; they're just details. The goal I'm working toward is a single coherent stack that is easy for others to comprehend and modify.

This isn't a product, in the sense that I can't/won't charge money for it. I'm not really making something others want right now. I'm trying to make something I think the world needs, and I'm trying to make the case for something the world hasn't considered to be a good idea yet. I'm sure I don't have all the details nailed down yet :)

-----

3 points by i4cu 2734 days ago | link

> I'm trying to make something I think the world needs, and I'm trying to make the case for something the world hasn't considered to be a good idea yet. I'm sure I don't have all the details nailed down yet :)

"To boldy go where no man has gone before"

"Second star on the Left"

I can get behind that :)

-----

4 points by akkartik 2737 days ago | link | parent | on: Ask: When to use lists vs tables?

Lists are easy to split and merge, insert and delete nodes from, but searching them is slow. Tables are fast for searching and insertion and deletion, but they don't naturally encode ordering relationships.

Check out this chapter: http://www.gigamonkeys.com/book/they-called-it-lisp-for-a-re... Tables are often great for things Lisp historically used lists for. But the one thing they can't do is represent code and trees in general. That's where cons cells come in.

-----

3 points by krapp 2737 days ago | link

>But the one thing they can't do is represent code and trees in general. That's where cons cells come in.

They can if you can nest them, or include references to other keys (like foreign keys in SQL or something) It's inefficient but it's possible. You can even represent trees in a flat array if you work hard enough.

Although maybe I should be pedantic and clarify that you can represent trees with tables but not in tables natively, of course.

-----

2 points by akkartik 2736 days ago | link

You got me, I was careful to use the word 'natural' in the first paragraph but I forgot to do so in the second ^_^

-----

2 points by kinnard 2680 days ago | link

I realized belatedly that ordering matters for my application. . .

Even looked at Assocative Containers[1] before I thought, "alist!"

[1] https://en.wikipedia.org/wiki/Associative_containers

-----

3 points by akkartik 2743 days ago | link | parent | on: Ask: Why is there no "save-list"?

As i4cu pointed out, it's because write can't handle tables. There _is_ a save-list, but it's called writefile, and it works not just on lists but most primitive datatypes. However, it doesn't work on tables.

The naming isn't very consistent here. Arguably writefile should be called save-file. We should preserve the write prefix for operations that write to opened Racket file ports (https://docs.racket-lang.org/reference/file-ports.html) and save for operations that write to provided filenames. That convention is followed by save-table and write-table.

And of course we could simplify names still further if we took i4cu's suggestion and had write work with tables.

-----

3 points by kinnard 2743 days ago | link

Is it straightforward to change them to be consistent?

-----

2 points by akkartik 2743 days ago | link

Yup, just rename the macro and search-and-replace all calls. Then try running all tests! (Updating incompatibilities at the top of arc.arc is also appreciated.)

-----

2 points by hjek 2743 days ago | link

Yep. Or change `save-table` to be an extension of `writefile` using `defextend`:

   (defextend writefile (val name) (is (type val) 'table)
     [the body of the save-table function here]

-----

2 points by rocketnia 2743 days ago | link

Even if you merge `writefile` and `save-table` like this, but not `readfile1` and `read-table`, then people still need to know, at development time, what type of data is in the file in order to read it, so they might as well use a type-specific way to write it as well. Unfortunately, merging `readfile1` and `read-table` isn't really possible, since their serialized representations overlap; they can't reconstitute information that was never written to the file to begin with.

From a bigger-picture point of view, this seems like it would become a non-issue once Arc had its own reader. I assume the problem with reading tables using `read` is that Racket's reader constructs immutable hashes. An Arc-specific reader would naturally construct Arc's mutable tables instead.

Doesn't Racket's reader give us similar problems in that it reads immutable strings and cons cells, too? So these problems could all be approached as a single project.

In the short term, it's not a project that would need a whole new reader. It could just be an adaptation of Racket's existing reader... something like this:

  (define (correcting-arc-read in)
    (let loop ([result (read in)])
      (match result
        
        ; TODO: See if this should construct a Racket mutable cons cell
        ; instead (`mcons`). Right now this just creates an immutable
        ; one, which should be fine since Arc uses an unsafe technique
        ; to mutate those.
        [(cons a b) (cons (loop a) (loop b))]
        
        [ (? hash?)
          (make-hash
            (map
              (match-lambda [(cons k v) (cons (loop k) (loop v))])
              (hash->list result)))]
        [ (? string?)
          ; We construct a new mutable string with the same content as
          ; `result`.
          (substring result 0)]
        ; We handle tagged values, which are represented as mutable
        ; Racket vectors.
        [(? vector?) (list->vector (map loop (vector->list result)))]
        
        ; We handle various atomic values. (TODO: Add more of these
        ; cases until we've accounted for every writable type Arc
        ; supports. Alternatively, just make this a catch-all
        ; `[_ result]`.)
        [(? number?) result]
        [(? symbol?) result])))
Writing Arc's `queue` type might be tricky, since that representation relies on sharing. It's possible queues (and other tagged values in general) should have a customized read and write behavior.

-----

3 points by hjek 2743 days ago | link

> I assume the problem with reading tables using `read` is that Racket's reader constructs immutable hashes.

Racket also has mutable hashes created using `make-hash`[0] rather than `hash`. It could just be that the tables are not serialised as something Racket reads as mutable hashes when deserialising it back again?

[0]: https://docs.racket-lang.org/reference/hashtables.html#%28de...

-----

3 points by rocketnia 2743 days ago | link

"It could just be that the tables are not serialised as something Racket reads as mutable hashes when deserialising it back again?"

I'm pretty sure the Racket reader never reads a mutable hash, but that it's possible for a custom reader extension to do it.

Some of Racket's approach to module encapsulation depends on syntax objects being deeply immutable. In particular, a module can export a macro that expands to (set! my-private-module-binding 20) but which uses `syntax-protect` so that the client of that macro can't use the `my-private-module-binding` identifier for any other purpose. If the lists constituting a program's syntax were usually mutable, then it would be hard to stop the client from just mutating that expansion to make something like (list my-private-module-binding 20), giving it access to bindings that were meant to be private.

I think this is why Racket's `read-syntax` creates immutable data. As for why `read` does it too, I think it's just a case of `read` being a relatively unused feature in Racket. They don't have many use cases for `read` that they wouldn't rather use `read-syntax` for, so they don't usually have reasons for the behavior of `read` to diverge from the behavior of `read-syntax`.

All this being said, they could pretty easily add built-in syntaxes for mutable hashes, but I think it just hasn't come up. Who ever really wants to read a mutable value? In Racket, where immutable values are well-supported by the core libraries, you aren't gonna need it. In the rare case you want it, it's easy enough to do a deep traversal to build the mutable structure you're looking for (like my example `correcting-arc-read` does).

It only comes up as a particular problem in Arc. Arc's language design doesn't account for the existence of immutable values at all, so working around them when they appear can be a bit quirky.

-----

3 points by hjek 2742 days ago | link

> I'm pretty sure the Racket reader never reads a mutable hash, but that it's possible for a custom reader extension to do it.

No..?

    > (require racket/serialize)
    > (define basket (make-hash))
    > (hash-set! basket 'fruit 'apple)
    > (write-to-file (serialize basket) "basket.txt")
    > (define bucket (deserialize (file->value "basket.txt")))
    > (hash-set! bucket 'fruit 'banana)
    > bucket
    '#hash((fruit . banana))

-----

4 points by rocketnia 2742 days ago | link

The Racket reader reads a seven-element list there, not a mutable hash.

Looks like you're proposing to use a two-stage serialization format. One stage is `read` and `write`, and the other is `serialize` and `deserialize`. At the level of language design, what's the point of designing it this way? (Why does Racket design it this way, anyway?)

I can see not wanting to have cyclic syntax or syntax-with-sharing in the core language just because it's a pain in the neck to parse and another pain in the neck to interpret. Maybe that's reason enough to have a separate `racket/serialize` library.

But isn't the main issue here that Arc's `read` creates immutable tables when the rest of the language only deals with mutable ones? The mutable tables go through `write` just fine, but `read` doesn't read the same kind of value that was written out. If and when this situation is improved, I don't see where `racket/serialize` would come into play.

-----

2 points by hjek 2741 days ago | link

> Looks like you're proposing to use a two-stage serialization format.

I wasn't really proposing anything, only pointing out that it's not the case that Racket never can read mutable hashes, and then illustrating that with a code example.

-----

3 points by rocketnia 2741 days ago | link

Hmm, all right. I didn't want to believe that was the point you were trying to make. In that case, I think I must not have conveyed anything very clearly in the `correcting-arc-read` comment.

I've been trying to respond to this, which was your response to that one:

"Racket also has mutable hashes created using `make-hash` rather than `hash`. It could just be that the tables are not serialised as something Racket reads as mutable hashes when deserialising it back again?"

In the `correcting-arc-read` comment, I used `make-hash` in the implementation of `correcting-arc-read`, so I assumed your first sentence was for the edification of others. I found something to respond to in the second, which kinda pattern-matched to a question I had on my mind already ("Can't the Racket reader just construct a mutable table since what was written was a mutable table?").

As for my response to your "No..?"...

One of the purposes of `correcting-arc-read` is that (when it's used as a drop-in replacement for Arc's `read`) it makes `readfile1` return mutable tables. So if anyone had to be convinced that a reader that returned mutable hashes could be implemented at all in Racket, I thought I had shown that already. When it looked like you might be trying to convince me of something I had already shown, I dismissed that idea and thought you were instead trying to clarify what your proposed alternative to `correcting-arc-read` was.

Seems like I've been making bad assumptions and that as a result I've been mostly talking to myself. Sorry about that. :)

Maybe I oughta clarify some more of the content of that `correcting-arc-read` comment, but I'm not sure what parts. And do you figure there are any points you were making that I could still respond to? I'd better not try to assume what those are again. :-p

-----

3 points by kinnard 2741 days ago | link

Sounds like I opened a can of . . . lists . . .

-----

3 points by akkartik 2743 days ago | link

I have an old fork (https://github.com/akkartik/arc) that has an extensible generic pair of functions called serialize and unserialize which emit not just the value but also tagged with its type. read and write are built atop them.

-----

1 point by kinnard 2740 days ago | link

I don't think I can handle this while I'm building the app I'm working on . . .

-----

More