> I just wanted to throw together a quick demo of the Arc tooling I've been working on.
To what end, might I ask? i.e. are you planning to contribute this to the arc community? Is this something the community can leverage with their own tooling?
I could have other questions, but if it's not something I can use or look at then they become somewhat pointless (the questions, that is).
"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.
Saw this on reddit and given that it's derived from Arc I thought I'd post it over here.
Certainly these are much more involved than my functions. My aif for example is:
(defmacro aif [expr & body]
`(let [~'it ~expr]
(if ~'it
(do ~(first body))
(do ~@(next body)))))
Actually, when I started in Clojure I was using these anaphoric operations a lot, but most of my code has moved away from them (for no particular reason, I just haven't needed them much I guess).
Racket doesn't have anaphoric macros as part of the core language (although there is a module for that), so that's a bit difficult getting used to when coming from Arc, but I find that pattern matching[0] can be used to the same effect:
(match 123
((and (? even?) it) (~a it " is even"))
(it (~a it " is odd")))
Kudos, it looks good. I've only had to time to take quick look, but I'm impressed at how clean the code looks. I see a lot of arcish code so it's made it easy for me to interpret.
I could be wrong, but it actually looks smaller in size than Arc's version, but with more features. Maybe that's due to Racket lending functionality and adding DB code that makes it straight forward.
> not a general purpose cms (link to 'The CMS Trap')
That's me. I'm that guy - lol (well not quite but close).
Between, no JS, Free Software and the above I'm getting that you and I have very different goals :)
> I've only had to time to take quick look, but I'm impressed at how clean the code looks.
Good to know that it's somewhat readable for people who are not me!
> I could be wrong, but it actually looks smaller in size than Arc's version, but with more features.
It's still smaller and it does have file upload, but there's a lot of news.arc features missing before this one is usable.
> > not a general purpose cms (link to 'The CMS Trap')
> That's me. I'm that guy - lol (well not quite but close).
I read a blog post[0] about Wagtail, a Python CMS, and wanted to try it out. Once up and running, it has a user login/registration form, but you literally can't post anything, because it's more like a CMS framework or something (or maybe that is what CMS really means?). To me that embodied 'The CMS trap'.
> Between, no JS, Free Software and the above I'm getting that you and I have very different goals :)
Yea, good to hear different opinions here.
Ok, so I'm not totally against JS, and I'm using it myself for other stuff. I think I'm just really pro progressive enhancement, and it's sometimes the first thing to go. For example, I don't think HN has a reason to not allow voting w/o JS. I think there's a bit of a tendency to use way too large and slow JS frameworks, but I'm interested in ClojureScript now because it seems to be all about eliminating dead code and optimizing.
> I read a blog post[0] about Wagtail,....because it's more like a CMS framework...
Yeah neither Drupal nor Wagtail sound remotely appealing to me. A CMS, in general terms, is just that: a Content Management System. Now that's pretty broad with lots of room for products which are both good and bad.
Personally I'm interested in the Business Intelligence space where people build Apps, only I'm targeting companies who wish to empower their Business Analysts (or specifically the blue to 'purple people') to build apps and streamline inefficient IT processes.
I'm not sure if BI apps are considered a CMS or not, but when I read that 'trap' article I couldn't help but relate.
> I think I'm just really pro progressive enhancement, and it's sometimes the first thing to go.
Well progressive enhancement is not, not using JS.
Progressive Enhancement is pretty much the opposite of Graceful Degradation. In GD you fallback to allow lesser technologies to handle the workload, where PE is starting with limited functionality and build up incrementally to gracefully handle the workload. JS is often what is used to achieve PE.
> I think there's a bit of a tendency to use way too large and slow JS frameworks, but I'm interested in ClojureScript now because it seems to be all about eliminating dead code and optimizing.
Yep.
All I can say here is that automation and the benefits that comes from that will have a cost. Your product/app/page will contain larger files which do some amount of work for you.
Developers have a tendency to see 'frameworks' from a maximal code efficiency perspective, but frameworks really are all about lessening the $ cost, the work burden, time to market and the operational maintenance. The argument is always "If you're building a blog you shouldn't need a framework." And that's true, but most frameworks are not built for making blogs, they're built for greater things - however if said framework makes it easier for person A to do thing X, even if it's just a blog then, really, it's hard not to see it happening (even though developers don't like to see it).
Frameworks, really, are for when large tailor made products become nightmares to manage, that's when they become the go to "solution" and start to shine. Modern day web development has a lot of pitfalls and will require a skillset that not everyone has. Not everyone can make dynamic apps that don't get bogged down by limiting factors like the DOM tree (cluster fuck), rendering/paint (bottle-necks), and single threaded environments (the async data gotcha's). Frameworks can help with all this, but it's at a cost. The file size is bigger, it's not Free Software, it uses JS and you have the potential to be caught-up in a CMS trap.
> Well progressive enhancement is not, not using JS. Progressive Enhancement is pretty much the opposite of Graceful Degradation.
Yea, exactly. I'm OK with JavaScript being used for better UX as long as core functionality works w/o JS. From W3C:
> So, graceful degradation is the practice of building your web functionality so that it provides a certain level of user experience in more modern browsers, but it will also degrade gracefully to a lower level of user in experience in older browsers. This lower level is not as nice to use for your site visitors, but it does still provide them with the basic functionality that they came to your site to use; things do not break for them.[0]
If it is assumed that voting is part of the basic functionality of HN, then HN does PE wrong by breaking it when there's no JS. It's a relatively recent change to HN and Anarki doesn't suffer from that issue.
Yet it's neat that HN uses JS to prevent page reloading on voting. I'm just doing no JS at all on my own project here to make sure I don't accidentally rely on it, because I don't really trust myself to use it in moderation.
> Frameworks can help with all this, but it's at a cost. The file size is bigger,
Yea, they're beneficial, but heavy. HN front page: 62K. HN search: 1.19M. Did they really need Angular for that search input form? That's where ClojureScript looks promising though: Their `helloworld` example is just 20K.[1]
Obviously you're more into graceful degradation than progressive enhancement.
My idea of graceful degradation is falling back to a message that says "sorry, not going to happen!" :)
So, obviously, I'm more into progressive enhancement than graceful degradation.
> ClojureScript looks promising though...
Well to be fair, ClojureScript is not a full fledged framework; It is a framework, but really it's more of a compiler with some library functions. Reagent [1], for example, is a library that bridges the gap between ClojureScript and React. So I'm not sure it's fair to compare ClojureScript to these full fledged frameworks... you're still correct though, in that ClojureScript alone can produce apps with a smaller footprint.
That said & just to compare, last time I checked, React and Elm are appx. 42-45kb, Angular is 90kb. So HN search, obviously need to add their own code to bring it in at 1.19M.
My app, written in pure Clojurescript, is currently at 90kb minified. I hope to bring that down, but chances are the first release will actually be a little larger.
I'm going to ask some seriously basic questions here:
1. So, Mu is a general programming language - right? (in reading the docs I almost wondered if it was a testing language only).
2. It is built on top of Arc - right? (It looks as though it's built with c code, but I see arc in there too so I have to wonder where it fits in).
3. Is the idea that you build your tests for each function inside the actual function? (which the examples seem to illustrate). If that's the case wouldn't the code become really large and hard to navigate? Have you written any substantial programs in it to see how that might look?
4. How does SubX relate to Mu?
I'm probably missing the depth being presented here, but I'm a high-level language guy so I have to start with basic questions :)
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 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.
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 :)
> 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).
> 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?
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 :)
> 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 :)
I mention it because pg makes a fairly good point about using alists.
"There is a tradition in Lisp going back to McCarthy's 1960 paper [2]
of using lists to represent key/value pairs:
arc> (= codes '(("Boston" bos) ("Paris" cdg) ("San Francisco" sfo)))
(("Boston" bos) ("Paris" cdg) ("San Francisco" sfo))
This is called an association list, or alist for short. I once
thought alists were just a hack, but there are many things you can
do with them that you can't do with hash tables, including sort
them, build them up incrementally in recursive functions, have
several that share the same tail, and preserve old values"
Of course none of this negates anything in akkartik's response. I just think there are some helpful tips in the tutorial.
> I have no idea how to do it efficiently, though, since presumably each close/open operation would also mean an http request and possibly a file write.
Which is the same for voting and page generation.
ie. Right now there's a big cost on the servers because all the work is done on the servers. If you start looking at it from the perspective of not adopting that cost then you might as well say the same for voting + all the html creation and just write the whole thing in js where you only fetch data.
This is the slippery slope that lead me to writing apps in clojurescript. For me, the workload may get increased, but much of the operational costs get distributed across the users and on their hardware.
> Which is the same for voting and page generation. [...] Right now there's a big cost on the servers because all the work is done on the servers.
Yea, I don't think something like checking whether a comment is member of the list of hidden items by a user would really add workload of any significance.
> [...] you might as well say the same for voting + all the html creation and just write the whole thing in js where you only fetch data.
Does that not lead to an awful lot of traffic sometimes? (I'm imagining a version of News that would transmit all submitted content to let the client do the sorting and searching instead of doing it on the server.)
> I don't think something like checking whether a comment is member of the list of hidden items by a user would really add workload of any significance.
I don't think so either. My comment was that "all the work is done on the servers". For the whole app. That is looking at all of the cost in aggregate (every interaction requires a http request, and requires throttling, session handling, authentication, html page generation, and so on....).
> Does that not lead to an awful lot of traffic sometimes? (I'm imagining a version of News that would transmit all submitted content to let the client do the sorting and searching instead of doing it on the server.)
Sure if you fetch all data unsorted, but I wasn't suggesting (or at least thinking) anything like that. I was just suggesting the html creation and many interactions that currently represent at least half if not most of the workload the server operations are currently doing.
> Storing state on the server would also allow for it to work w/o JavaScript.
I agree for this case (surprise, surprise...). This app was/is designed to work without js (mostly). If there's much more of a departure from this design and any real dependancy on js begins, well really the whole app should get re-written.