Arc Forumnew | comments | leaders | submit | i4cu's commentslogin
3 points by i4cu 22 hours ago | link | parent | on: Clojure Anaphoric Macros

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).

reply

2 points by hjek 5 hours ago | link

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")))
[0]: https://docs.racket-lang.org/reference/match.html

reply

3 points by i4cu 13 days ago | link | parent | on: Show Arc: Debate (very alpha)

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 :)

reply

3 points by hjek 13 days ago | link

> 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.

[0] https://torchbox.com/blog/torchbox-has-dropped-drupal/

reply

3 points by i4cu 13 days ago | link

> 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.

reply

3 points by hjek 9 days ago | link

> 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]

[0]: https://www.w3.org/wiki/Graceful_degradation_versus_progress...

[1]: https://clojurescript.org/guides/quick-start

reply

3 points by i4cu 9 days ago | link

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.

1. https://reagent-project.github.io/

reply

2 points by hjek 8 days ago | link

> My idea of graceful degradation is falling back to a message that says "sorry, not going to happen!" :)

Hilarious! A degradation for sure but the graceful part of is questionable.

reply


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 :)

reply

4 points by akkartik 14 days ago | link

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

reply

3 points by i4cu 13 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 :)

reply

3 points by akkartik 13 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).

reply

3 points by i4cu 13 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?

reply

4 points by akkartik 13 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 :)

reply

3 points by i4cu 13 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 :)

reply

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

It's probably worth reading the arc tutorial [1].

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.

1. http://www.arclanguage.org/tut.txt

reply

3 points by i4cu 18 days ago | link | parent | on: Ask: Collapsable Comments

> 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.

reply

3 points by hjek 17 days ago | link

> 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.)

reply

3 points by i4cu 17 days ago | link

> 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.

reply

3 points by i4cu 18 days ago | link | parent | on: Ask: Collapsable Comments

> 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.

reply

4 points by i4cu 23 days ago | link | parent | on: Ask: Why is there no "save-list"?

Presumably save-table was written because the eval'd output of a table is not re-loadable.

While the output of a list can easily be saved to a file via 'writefile' and read back in via 'readfile'.

I touched on this as a problem with tables in an earlier comment: http://arclanguage.org/item?id=20803

reply

3 points by i4cu 34 days ago | link | parent | on: Knark - rewrite in plain Racket?

You could be right, but it's a lot of work effort to do this port so I imagine it's not a trivial limitation where are few lines of dropping into racket code would do the trick. It gives me the sense that he's hit a much bigger or deeper[1] problem.

1. such as as what you suggested.

reply

3 points by krapp 34 days ago | link

The question, and one that might be worth discussing, is what does Arc as a language bring to the table that makes it worth using over Racket?

reply

3 points by i4cu 34 days ago | link

or clojure :)

I've tried to start back into arc coding, a few times, but I immediately hit problems. I've found clojure's hash-maps/tables to be a fundamental need and arc tables to be far too limiting. I've considered forking arc an implementing them, but I can't find time. I'm too busy writing apps.

reply

2 points by hjek 30 days ago | link

Have you done any web development in Clojure?

I like Clojure as a language, but I'm a bit overwhelmed with choice when it comes to web frameworks and the package managers you need to use those web frameworks (somewhat similar to Common Lisp), whereas with Racket and Arc there's just one place to start. Do you have any experience with Clojure web frameworks?

I'm always a bit scared of adding external dependencies to any project, but Rich Hickey has this great rant about semantic versioning where he argues that a new "major version" is essentially just a bad excuse for breaking existing stuff. I wonder if Clojure web libraries take those principles to heart or whether it's another left-pad incident waiting to happen?

reply

3 points by i4cu 29 days ago | link

This is a surprisingly difficult question to answer, but here I go...

FYI cljs = clojurescript.

When Clojure first came out there was a core set of libraries that everyone flocked to:

1. Compojure (a routing library)

  - similar to arc's defop
https://github.com/weavejester/compojure

example:

  (defroutes app
    (GET "/" [] "<h1>Hello World</h1>")
    (route/not-found "<h1>Page not found</h1>"))
1a. Ring (a middleware library)

  - parses the web request and converts it into a hash-map of meaningful values.
  - similar to srv.arc
https://github.com/ring-clojure/ring

2. Hiccup (HTML Soup)

  -  similar to hmtl.arc, but uses a data structure to provide flexibility.
https://github.com/weavejester/hiccup

example:

  [:head
    [:meta {:http-equiv "Content-type"
            :content "text/html; charset=utf-8"}]
    [:title "adder"]
    [:link {:href "/adder.css" :rel "stylesheet" :type "text/css"}]]
Much of this came about when people read example blogs like this:

https://mmcgrana.github.io/2010/07/develop-deploy-clojure-we...

note: it's a 2010 article so some of it's outdated, but the idea would be the same.

At that time everyone was racing to make more robust web frameworks. Many of them were from people doing the above stuff only adding features. However, shortly afterwards cljs was released and another slew of web frameworks came out as people embraced writing web apps client side. Then again, shortly afterwards, Facebooks React became the new thing and advanced the idea of further separating out the data content from the UI composing for rendering. At this point data models (i.e. big hash-maps) and syncing that data to the UI became the new norm. And even since then more advanced frameworks came out, such as Fulcro, that further extend the data modelling & syncing features (https://github.com/fulcrologic/fulcro).

Through out all this many web frameworks became abandon-ware and now it's really hard for a newbie to make sense of which one to use. In my opinion:

1. If you want to do what Arc does (server side page generation) then use Compojure + Hiccup.

2. If you want to write basic client side cljs code there are dom libraries like:

- Dommy https://github.com/plumatic/dommy

- Domina https://github.com/levand/domina

These are fairly simple to use.

2a. If you want to write client side cljs code that takes advantage of React then use Reagent. https://github.com/reagent-project/reagent (Much better more interesting that Domina/Dommy)

If you have a desire to enter the more advanced data-model-UI-syncing arena where I would probably use Fulcro (but haven't). Note that these advanced frameworks like Fulcro expect you to know much more about state management / data modelling and it could be a steep learning curve for some.

I've been developing cljs web apps for over 4 years. Over these years I've tried some of the frameworks, but I ended up writing my own as none of them could do what I needed.

Is that helpful?

All of that may seem like too much, but remember you really only need Compojure + Hiccup to be where Arc is at.

Edit: I just noted that you know Datalog, so I think Fulcro is a good fit for you (see http://book.fulcrologic.com/#GraphDB).

reply

2 points by hjek 29 days ago | link

> Is that helpful?

Yes! Thanks a lot for the write-up.

That Compojure example does look quite familiar and Arc-like, and looks like it can handle multipart post requests too[0].

I'd never heard of Fulcro before. Given what is often emphasised about Clojure, at first glance at the Fulcro docs I'm a bit surprised how often they mention state and mutations:

> The other very common case is this: You’ve loaded something from the server, and you’d like to use it as the basis for form fields. In this case the data is already normalized in your state database, and you’ll need to work on it via a mutation.[1]

Also, I'm too much into graceful degredation to ever go all out Cljs, unless it was for a phone app. But I find that it's often interesting to see how people do things in Clojure, even when not using that language, so I'll be giving those Fulcro videos a look.

[0]: https://github.com/whostolebenfrog/compojure-multipart

[1]: http://book.fulcrologic.com/#_initializing_in_a_mutation

reply

3 points by i4cu 29 days ago | link

> I'd never heard of Fulcro before. Given what is often emphasised about Clojure, at first glance at the Fulcro docs I'm a bit surprised how often they mention state and mutations:

Well things on the client side can be sometimes be mutable. No one gets around the fact the DOM is a mutable only object. But besides that, the Fulcro library has labelled one of their feature's a 'Mutation'. Which was probably a bad choice, but it has nothing to do with the immutability of the underlying cljs object that it uses for that "Mutation". You'll notice the example is using 'swap!'. That means it's modifying an atom; Where an atom is an interface to make changes to the immutable object it holds. So really 'swap!' takes the change request, constructs a new version the original thing held in the atom, with changes, then 'swap's it with the original item inside the atom. The original thing was never changed (no changes to existing slots in memory). Hence clojure's things are immutable, and they are in Fulcro too, accept when changing the DOM tree.

As for state that's mentioned all the time in Clojure :)

reply

2 points by akkartik 30 days ago | link

This is the Rich Hickey talk from 2016: https://www.youtube.com/watch?v=oyLBGkS5ICk. If you listen to it, it was made in an effort to adjust the community's trajectory. I too would like to hear if adjustments did happen.

Basically the way I interpreted it[1] is that "major version" is a meaningless concept. If you're making incompatible changes, rename the package. If that's a disincentive to making incompatible changes -- great!

[1] http://akkartik.name/post/versioning

reply

2 points by hjek 30 days ago | link

Nice blog post.

> Rich Hickey pointed out last year that the convention of bumping the major version of a library to indicate incompatibility conveys no more actionable information than just changing the name of the library.

Yes, I guess Hickey is applying this idea of immutability not only to data structures but also to APIs and even databases[0] with the proprietary database service Datomic. Interestingly Datomic uses Datalog as query language, so it's straight forward to apply at least some of those ideas with the Racket Datalog package[1].

As of now I have a basic web forum working with all data storage done in Datalog (except for file uploads!). I'll post some code once the design is a bit more settled. It's still in the breaking-things-all-the-time phase.

What I find a bit tricky about Datalog is that relations are stored together with other facts, which in my mind feels a bit like storing code in a database, but maybe I just haven't wrapped my head around it yet.

Does anyone here have favourite articles or talks about logic programming?

[0]: https://www.youtube.com/watch?v=EKdV1IgAaFc

[1]: https://docs.racket-lang.org/datalog/interop.html

reply

3 points by i4cu 29 days ago | link

> What I find a bit tricky about Datalog is that relations are stored together with other facts, which in my mind feels a bit like storing code in a database, but maybe I just haven't wrapped my head around it yet.

I can't speak for Datalog, but I've used Datomic.

If it's the same, then a 'fact' is comprised of an entity (the id), + an attribute, + a value.

The relationships are made by storing an entity id into the value slot of another fact. Thus the model is both flat (being a list of facts) and hierarchical (they can point to each other). It pretty much becomes a graph database. Is that what you mean?

reply

2 points by hjek 29 days ago | link

> I can't speak for Datalog, but I've used Datomic.

Cool!

> The relationships are made by storing an entity id into the value slot of another fact. Thus the model is both flat (being a list of facts) and hierarchical (they can point to each other). It pretty much becomes a graph database. Is that what you mean?

Yes, exactly! What I worry about (because I'm fairly new to logic programming) is whether it could potentially be difficult to update a program where storage of business logic and storage of data aren't separated?

If we assume we have a Hacker News web app where we have a fact: One day Alice submits a story with the title "How to peel onions"; and we are thinking "Why on earth did she post that here?!?" So we add this relation to our code: A story is `irrelevant` if it has the word "onion" in the title. Then, the next day we get another fact: Now Bob has submitted a story called "How onion routing works". This new story by Bob then makes us reconsider our definition of `irrelevant`.

In a typical imperative program we'd just edit the code and redefine the `irrelevant` predicate, and it would take effect next time we run the program (or instantly if we enter it at a repl). But here in our logic program we store this `irrelevant` relation in our graph database, so even though we have removed it from our code, it is still sitting there in the database along with all the facts, outside the reach and responsibility of git, or whichever VCS we're using.

Yes, so my question is: How do you practically deal with changes to business logic in logic programming where data storage and relation storage is one and the same? Perhaps Datomic just avoids this issue somehow? I may also be missing or misunderstanding something.

reply

3 points by i4cu 29 days ago | link

Can't say I know what the options are since I don't know Datalog or the DB you're using, but is this reasonable?:

  -----------------------------------------------------------
  Entity              | Attribute | Value
  -----------------------------------------------------------
  person-id-001       | name      | Alice
  person-id-001       | stories   | [story-id-001, story-id-002...]   

  story-id-001        | headline  | "How to peel onions"
  irrelevant-word-001 | stories   | [story-id-001, ...]
  irrelevant-word-001 | word      | onion
  -----------------------------------------------------------
So if you decide that onion is no longer irrelevant then delete the entity 'irrelevant-word-001'. Which seems, at least to me, better than making code pushes.

So all of this assumes a few things:

- Your DB supports a cardinality of 'many' items in the value slot.

- Your query language can perform joins.

Of course none of this helps when someone changes a headline, but only full-text search DB's will help you do that.

Edit: made edits.

reply

3 points by i4cu 29 days ago | link

To be complete (and somehow I edited this out):

  -----------------------------------------------------------
  Entity        | Attribute          | Value
  -----------------------------------------------------------
  globals       | irrelevant-words   | [irrelevant-word-001, ...]   
  -----------------------------------------------------------
So you would also need to remove the value 'irrelevant-word-001' from the above. At least this is how I would do it in Datomic anyway.

What's interesting (at least to me) is that Datomic has a function called 'retractEntity' [1] which auto-magically removes all references of an entity in any value slot when you retract the entity. Man I love Datomic :)

[1] https://docs.datomic.com/on-prem/transactions.html#dbfn-retr...

reply

2 points by hjek 29 days ago | link

> So if you decide that onion is no longer irrelevant then delete the entity 'irrelevant-word-001'. Which seems, at least to me, better than making code pushes.

I can make sense of that when there's just one instance of this app running. Yet imagine the scenario where the web app has been published, and suddenly other people are running this web app. If the business logic is then changed, somehow I'd have to tell those people: "Oh btw, when you're running `git pull` next time, then you just also gotta run this query to retract some of the old relations from the database."

Definitely not a problem for me yet, but I can just smell it coming. I could add those retractions to the code, but they would have to stay there indefinitely, because it's not possible to tell if those retractions have taken place on everyone's databases yet.

Maybe I'm over-thinking this.

> What's interesting (at least to me) is that Datomic has a function called 'retractEntity'

Looks like the one called `~` in Racket's Datalog[0].

[0]: https://docs.racket-lang.org/datalog/interop.html#%28form._%...

reply

2 points by i4cu 29 days ago | link

Quick questions before going further.

Is this irrelevant-word example a real feature you're building into the app or a contrived example to understand Racket DataLog DB use?

If it's a real feature, and I'm assuming it is. Then I'm also assuming that when a story is submitted you're parsing the title and adding the relationship to the current set of irrelevant-words that are stored.

So the question's are:

1. How are you going to remove the past relationships between stories and an irrelevant word that's getting removed? (looks like we've answered this).

2. How are you going to make sure past stories gain the relationship to newly added irrelevant words?

3. How are you going to handle title changes.

After you get handle on these then what you really need to do provide an interface, from within the apps admin tools, to trigger the noted functionality. This way the business logic is in the app and it's modifying the data.

reply

2 points by hjek 28 days ago | link

> Is this irrelevant-word example a real feature you're building into the app or a contrived example to understand Racket DataLog DB use?

It's a simplified and slightly contrived illustration of an issue in this pre-alpha code I haven't published yet, perhaps just because I haven't thought of a name for the project yet. But yes, let's assume it's a real feature for now.

> 3. How are you going to handle title changes.

I like Hickey's idea of accretion of data - with a timestamp! - and not forgetting previous facts. I think he's talking about it in The Database as a Value[0]. So, a story could have a few different titles in the database, and the newest one is the one you get to see.

The thing is, it's easy to add a timestamp to facts as a way of not considering old facts without forgetting them, but not to relations. For example in Racket Datalog[1]:

    (! (voted "i4cu" 'up 134))
can easily be get a timestamp:

    (! (voted "i4cu" 'up 134 1542151773))
but that would not really make sense in a relation like

    (! (root A B) (ancestor A B) (parent null A))
So, I don't feel there's a need for ever retracting facts, because timestamps solve that. (Even when deleting something, you could just add the fact that is has been deleted.) But with relations (a.k.a. business logic) I think I will need to retract things, which is tricky because this logic is not only present in the code but also in the database. This was the problem I was asking about.

> 2. How are you going to make sure past stories gain the relationship to newly added irrelevant words?

Datalog queries reflect the current set of facts and relations, so the possible irrelevance of a story would not be stored anywhere, so it wouldn't need to be updated.

> 1. How are you going to remove the past relationships between stories and an irrelevant word that's getting removed?

Some as above. For example, in a place oriented program a story object could have a boolean attribute `irrelevant?`, whereas I'm sending a query every time this value is needed, so no stale `irrelevance` attributes are stored anywhere.

> After you get handle on these then what you really need to do provide an interface, from within the apps admin tools, to trigger the noted functionality. This way the business logic is in the app and it's modifying the data.

Yes, that is kind of there already, as in having functionality for changing titles. The `irrelevant` functionality is not there now.

Ok, I think I just need to work on getting this code publishable, because it might be easier to discuss tangible examples.

[0]: https://www.infoq.com/presentations/Datomic-Database-Value

[1]: https://docs.racket-lang.org/datalog/interop.html

reply

2 points by i4cu 28 days ago | link

Yeah, Datomic doesn't expect the relationships to be stored in the DB. It stores a bunch of indexes for you and it has a great query language, but that's it.

So where will you're data be? In a local data structure? I read your racket Datalog link, but it doesn't show any details for the database side (i.e. durability etc.) even though it's labelled a database.

Also, I'm curious what made you choose a graph db. It seems like you're inheriting a lot of complexity and I'm wondering what the benefit is over a more traditional sql or nosql db.

reply

2 points by hjek 28 days ago | link

> So where will you're data be? In a local data structure?

Yes, I think. The database just stored in memory but it can be serialized and saved to the disk using `write-theory`[0] and loaded `read-theory`. That is what I'm doing for now, and it's a very naive and inefficient to do a full database dump rather than just appending new data, and I presume it's particularly in this area where Datomic is way more optimised and well thought out.

> Also, I'm curious what made you choose a graph db. It seems like you're inheriting a lot of complexity and I'm wondering what the benefit is over a more traditional sql or nosql db.

Well, I did the initial work on the web app: creating user accounts, adding posts and replies, and then I got to data storage. Initially I did a News-style flat-file database, just saving data as lists in files, that are then loaded into memory when the program starts. It mostly worked but also felt a bit complicated, and I thought that perhaps I should just use a proper database?

What I like about news.arc is that you can just launch it without any configuration, so MySQL and PostGreSQL were out of the question, and I started reading a bit about SQLite. But I've also had this fascination with logic programming, from what people are posting here[1][2], and from reading a bit of The Reasoned Schemer, and I watched some of those Rich Hickey talks again, where he talks about Datalog, which happens to be available for Racket.

There are just some things that are incredibly simple in declarative/logic programming. For example, if you have facts about stories being `parent` of their replies, then it's simple to just define the `ancestor` relation, and when you have the `ancestor` relation, you automagically get `descendants` without having to write any code, because it's just the inverse of `ancestor`:

    (! (:- (ancestor A B)
           (parent A B)))
    (! (:- (ancestor A B)
           (parent A C)
           (ancestor C B)))
But, I've also bumped into some questions - more practical than theorical - and that is why it's interested hearing about your experience with Datomic, and why I'm asking here.

So, SQLite is still on the table. I'm not too familiar with NoSQL, but my impression is that they are all about speed and scalability of data storage. I haven't used MongoDB but isn't it essentially just like storing JSON in a file, except faster? It would be interesting if any of those could be used in conjunction with Datalog though, if don't add too complexity for the sake of increased speed.

[0]: https://docs.racket-lang.org/datalog/interop.html#%28def._%2...

[1]: http://arclanguage.org/item?id=20650

[2]: http://arclanguage.org/item?id=20519

reply

2 points by i4cu 28 days ago | link

So a few things I wanted to point out:

Datomic vs. DataLog

Datomic uses DataLog as part of its query language, but that's pretty much where the comparison should end. Things like "treating the database as a value", and features such as data accretion that Rich talks about have nothing to do with DataLog. They're features of Datomic. So for example when you mention never retracting data, well your data size is going to continuously grow unless you write your own data management layer on top. Datomic, on the other hand, does this for you. When you want to query the database over time, then you're going to need to store time intervals for all of your data and incorporate that into each query. Where as in Datomic (which has a time log) you can pass in the DB itself as a value (with an associated time interval) and Datomic will make sure your queries are working against the dataset that accounts for the time interval.

I'm pointing this out because it seems to me that you're doing (or are going to be doing) a lot of work that may not be worth it for what you're trying to accomplish.

Nosql

> I'm not too familiar with NoSQL, but my impression is that they are all about speed and scalability of data storage.

Yes and No. Often speed can be a feature Nosql dbs advertise, but really, for me anyway, it's about flexibility and ease of use. Traditional RDBMS, for example, require creating schemas. Many Nosql databases don't require a schema at all which makes it easier to use and more flexible to change. Nosql's are often a key-value store so it can be really easy to take a hash-map or table of data from your code and just dump it into an nosql datastore and be able to query it.

My personal favourite is Redis and it might be worth considering for your app.

You can:

- store a value under a key [1]

- store table data [2]

- store values in a set [3] (which allows intersection/difference queries)

- store values in a sorted-set [4] (which allows you query by some numerical value like timestamp)

- use it to manage relationships [5]

The reasons I mention Redis is that the HN app is very well suited to it. HN only keeps 'x' amount of data in memory. And in Redis the data lives in memory. Also Redis allows you to set expiry times on data for auto eviction [6]. And Redis also supports ordered lists [7] which can make it useful for lisp based languages.

However it's not embedded. And if that's a requirement I'd almost suggest you move away from Racket and adopt a language that has more options for embeddable databases. I guess if you're willing to roll your own (and it looks like you may be) then that's awesome too.

But in case you decide otherwise... The library I use is Redis Carmine [8], but there are Racket clients [9].

1. https://redis.io/commands/set

2. https://redis.io/commands/mset

2a. https://redis.io/commands/mget

3. https://redis.io/commands/sadd

4. https://redis.io/commands/zadd

5. search: "Representing and querying graphs using an hexastore" https://redis.io/topics/indexes

6. https://redis.io/commands/expire

7. https://redis.io/commands/lset

8. https://github.com/ptaoussanis/carmine

9. https://redis.io/clients#racket

reply

2 points by hjek 27 days ago | link

> Datomic uses DataLog as part of its query language, but that's pretty much where the comparison should end. Things like "treating the database as a value", and features such as data accretion that Rich talks about have nothing to do with DataLog.

I'm not sure I totally agree with this. I think that apart from talking about the design of Datomic, he also has a more general point against what he calls PLOP (PLace Oriented Programming), which Datalog does address.

For example in plain Racket a value is lost if something else is put in its place:

    > (define foo 'bar)
    > (define foo 'baz)
    > foo
    'baz
In Datalog you just accrete facts:

    > (! (is foo bar))
    > (! (is foo baz))
    > (? (is foo X))
    is(foo, bar).
    is(foo, baz).
Hickey is also mentioning how git doesn't do PLOP in that it doesn't throw out your commit history (without you asking it to do so).

> The reasons I mention Redis is that the HN app is very well suited to it. HN only keeps 'x' amount of data in memory. And in Redis the data lives in memory. Also Redis allows you to set expiry times on data for auto eviction [6].

Interesting. Just checked news.arc, and yes `initload*` is set to 15000. Interesting idea from Redis with expiry times. I'll check it out. I hadn't considered the scenario of storing text enough to max out on memory, because it would probably be premature optimisation, but good to keep in mind. I'd like to give Redis/Rackdis a try; thanks for the suggestion. I've been hosting an Etherpad Lite instance, and Redis was painless to setup.

> I'm pointing this out because it seems to me that you're doing (or are going to be doing) a lot of work that may not be worth it for what you're trying to accomplish.

Yes, my priorities here are definitely to make the code as brief and simple as possible, and to not have to do to much work. With plain Datalog it's very little work to timestamp a fact, and it's also kind of necessary, e.g. to figure out which fact is most recent, when previous facts are not removed. I'm just trying to get the gist of Hickey's ideas here.

reply

2 points by i4cu 27 days ago | link

> PLOP (PLace Oriented Programming), which Datalog does address.

Yeah, I was thinking more along the lines that Datomic has built-in functionality to address the caching, cache eviction, and indexing that goes along with all that data accumulation. But you're correct, DataLog does accumulate facts.

> Interesting. Just checked news.arc, and yes `initload*` is set to 15000.

I did the same thing, about 6 or 7 years ago, that you're doing now. I ported HN to Clojure (which is actually how I learned Clojure). If memory serves me correctly when I was doing the work I realized I needed a real DB if I wanted to support load balancing. i.e. I needed to centralize the data for the authentication and fnid session info. I think Arc calls them fnids... You probably know better than I do now, but Arc has all this code to expire these session fnids and so, for me, Redis was just a good fit for that task.

Anyways, I'll be sure to take a look at the final result of your work.

Cheers.

reply

2 points by hjek 27 days ago | link

> I did the same thing, about 6 or 7 years ago, that you're doing now. I ported HN to Clojure (which is actually how I learned Clojure).

Cool!

> I needed to centralize the data for the authentication and fnid session info. I think Arc calls them fnids... You probably know better than I do now, but Arc has all this code to expire these session fnids and so, for me, Redis was just a good fit for that task.

The Racket web server is quite "batteries included" and comes with these different managers[0] for dealing with expiration of sessions/continuations, such as the LRU manager:

> The memory limit is set to `memory-threshold` bytes. Continuations start with 24 life points. Life points are deducted at the rate of one every 10 minutes, or one every 5 seconds when the memory limit is exceeded. Hence the maximum life time for a continuation is 4 hours, and the minimum is 2 minutes.

> If the load on the server spikes—as indicated by memory usage—the server will quickly expire continuations, until the memory is back under control. If the load stays low, it will still efficiently expire old continuations.

[0]: https://docs.racket-lang.org/web-server/servlet.html?q=respo...

reply

2 points by i4cu 27 days ago | link

> If the load on the server spikes...

When I was referring to load balancing and centralizing the data I was referring to many web servers sharing a centralized/external source for auth/session data.

I'm unfamiliar with racket's web server 'servlets'. The docs are little unclear (at least to me). Can these servlets live on a separate server so that the data can be shared between web servers? I'm guessing that was/is not a requirement for you, but I'm just interested in knowing if that's how it can work.

Uh oh, you're getting me interested in Racket now. I can't have that... I have too many projects :)

edit: I guess at the end of the day these servlets are web-servers right, so you can, even if you have to do it over http and build an api.

reply

2 points by hjek 26 days ago | link

> Can these servlets live on a separate server so that the data can be shared between web servers?

Probably. I assume that serializable continuations[0] from stateless servlets can just be stored wherever, like in Redis or something, instead of in the memory of one server.

> I ported HN to Clojure

If that is something you have published, it'd be fun to see, whether it's finished or not.

> Uh oh, you're getting me interested in Racket now.

My impression is that Clojure is faster, less verbose partly due to clever syntax and provides more immutable data structures than Racket. But when it comes to documentation and error messages, I find Racket more coherent and comprehensible.

Say, if I wanted to connect to a SQL databse, with Racket I'd use the DB module[1], end of discussion. But with Clojure there's Korma, ClojureQL, Persist, HoneySQL, Yesql, a JDBC wrapper from Clojure contrib, SQLingvo, oj, Suricatta, aggregate, Hyperion, HugSQL, and probably a few more[2][3]. That multitude of libraries with similar purpose may be useful in some cases, sure, but also potentially a bit overwhelming for beginners, so I guess that's why I found it easier to get started with Racket.

[0]: https://docs.racket-lang.org/web-server/stateless.html#%28pa...

[1]: https://docs.racket-lang.org/db/

[2]: https://stackoverflow.com/questions/294802/use-a-database-wi...

[3]: https://adambard.com/blog/clojure-sql-libs-compared/

reply

2 points by i4cu 26 days ago | link

> If that is something you have published, it'd be fun to see, whether it's finished or not.

I actually tried to look it out the other day during this conv, but it's buried somewhere unavailable right now. If I find/get to it I'll post.

> That multitude of libraries with similar purpose may be useful in some cases, sure, but also potentially a bit overwhelming for beginners, so I guess that's why I found it easier to get started with Racket.

Agreed. Navigating the volume libraries and the options available is a real pain in the beginning, but once you get past that, then it's not bad at all. At the same time, take a look at the quality of Clojure's Redis Carmine Library vs. Racket's Redis Libraries. Miles apart.

To each their own, right :)

reply

3 points by hjek 17 days ago | link

> take a look at the quality of Clojure's Redis Carmine Library vs. Racket's Redis Libraries.

Good point.

Anyways, I'm going with SQLite. It's really fast[0] and I just found out about recursive selects[1].

[0]: https://www.sqlite.org/fasterthanfs.html

[1]: https://sqlite.org/lang_with.html

reply

2 points by akkartik 33 days ago | link

Can you elaborate on what extra features Clojure's tables provide?

reply

4 points by i4cu 33 days ago | link

Where to start...

1. The ability to read them. Especially on output. It may seem small, but when you have a larger deeply nested map it really matters.

  {:user "akkartik"}
2. Keywords are ifns that work on hash-maps.

  (map :user list-of-maps)
3. All the built in library fns that work on them. Basics + Assoc-in, Get-in, merge, deep-merge, contains?..

Here's a good function to separate a collection of maps by any fn/ifn.

  (def separate (juxt filter remove))
 
  (separate :user data)

4. Iteration just works, ie they automatically become association lists for pretty much every composing fn (reduce, loop, ect)

5. Destructuring bind. Eg:

  (defn myfn [{:keys [user] :as record}]
    (str user " rocks"))
6. Nil values are supported.

It goes on, but I think that's enough... Besides my thumbs are getting sore typing via phone.

reply

3 points by akkartik 33 days ago | link

You'll need to elaborate more, apologies in advance to your thumbs. Some of the features you describe I don't understand (ability to read, maybe others). Others seem to already be in Arc (library support, iteration, destructuring bind; [_ 'user] doesn't seem much worse than :user). Nil values feels like the only definitely missing feature, and it feels more like a fork in the road with different trade-offs rather than one alternative being always superior to the other.

reply

7 points by i4cu 32 days ago | link

Ok so let's use this data

Clojure:

  > (def players
       {:joe {:class "Ranger" :score 100}
        :jane {:class "Knight" :weapon "Greatsword" :score 140}
        :ryan {:class "Wizard" :weapon "Mystic Staff" :score 150}})
response:

{:joe {:class "Ranger" :score 100} :jane {:class "Knight" :weapon "Greatsword" :score 140} :ryan {:class "Wizard" :weapon "Mystic Staff" :score 150}})

Arc:

  (= players
    (obj 'joe (obj 'class "Ranger" 'score 100)
         'jane  (obj 'class "Knight" 'weapon "Greatsword" 'score 140)
         'ryan  (obj 'class "Wizard" 'weapon "Mystic Staff" 'score 150))) 
response:

#hash(((quote jane) . #hash(((quote weapon) . "Greatsword") ((quote class) . "Knight") ((quote score) . 140))) ((quote ryan) . #hash(((quote weapon) . "Mystic Staff") ((quote class) . "Wizard") ((quote score) . 150))) ((quote joe) . #hash(((quote class) . "Ranger") ((quote score) . 100))))

-- Readability --

Reading that output is beyond a headache. It should be against the law! But more importantly, when writing code/troubleshooting you can't copy the evaluated output make a modification and then call the function again. I know you can bind it and apply functions to modify it, but often that's a real hassle compared to copy/paste.

-- Destructuring bind --

See links [a,b]

I think simple cases don't show the difference.

  > (def book {:name "SICP" :details {:pages 657 :isbn-10 "0262011530"}})
  
  > (let [{name :name {pages :pages isbn-10 :isbn-10} :details} book]
      (println "name:" name "pages:" pages "isbn-10:" isbn-10))
	  
response:

    name: SICP pages: 657 isbn-10: 0262011530
	
-- Library functions --

Clojure has DOZEN's of helper functions (and important traversal functions) that don't exist in Arc. Yeah I could write them, but I still have to. I was highlighting the ease with 'separate', but here's even 'deep-merge'....

  (defn deep-merge
    "Recursively merges maps. If keys are not maps, the last value wins."
    [& vals]
     (if (every? map? vals)
         (apply merge-with deep-merge vals)
         (last vals)))
		 
  > (deep-merge players {:joe {:weapon "LongBow"} :jane {:weapon "Lance"}})
  
It's not even these sample cases that expose the issues. It's about working with REAL data. I have many files containing hash-maps that are 15 levels deep. If I were to attempt pretty much anything, but the most simple cases, in Arc I would hit problem after problem after problem. Which I did. So fine... really I should use a DB, right? ok let me just grab that DB library in Arc....

-- Nil values --

I'd even be fine with any falsey values like boolean false. But Arc will not even store an #f value. To suggest I can't pass in REAL data with falsey values is really limiting. I can't pass in an options table argument. For every operation I would have to add more data or nuanced data or nuanced operations to support "this value was set, but it's set to 'NO' I don't want that option.".

[a] https://clojure.org/guides/destructuring#_associative_destru...

[b] https://clojure.org/guides/destructuring#_keyword_arguments

reply

3 points by akkartik 32 days ago | link

Thanks! That's very helpful.

reply


I'm sorry, but I'm not with you.

Firstly, Warren Buffet's take on Bitcoin is the same take that he has on Gold. And do you believe Warren Buffet doesn't understand Gold? Don't you think Gold investors write articles on how Warren doesn't understand Gold? Just because someone doesn't buy into something doesn't mean they don't understand it. Which brings me to my second point: Blockchain is not Bitcoin.

From a technology perspective Blockchain will increasingly become more important. From an investment perspective I'm with Warren Buffet - Bitcoin is shit.

It's important to understand that Blockchain as a tool can create technology which will have utility (and thus value), but Bitcoin has limited utility and no intrinsic value, in just the same way Gold has no utility nor any intrinsic value. Just because Bitcoin utilizes Blockchain doesn't mean Bitcoin inherits that value as a product (or in this case: currency).

Edit: BTW - the rest of the article was great... I just couldn't get over the Buffet/Investment part (which to be fair is pretty much the opening statement for the article).

reply

1 point by kinnard 35 days ago | link

Thank you! I don't think there's any example of a systems which fulfills the desired properties of a Blockchain without a native cryptocurrency. I'm skeptical of the notion of intrinsic worth. I think things have value because humans value them. Gold has a lot of utility, you probably use it frequently without knowing it. https://www.popularmechanics.com/science/a19670/refining-gol... I think it's more accurate to say that Blockchain uses Bitcoin, since Blockchain was the solution necessary to create Bitcoin(speaking of course before the advent of derivative cryptocurrencies+blockchains), but really they're codependent. You need bitcoin to write to Bitcoin's blockchain. That's very useful.

reply

1 point by i4cu 35 days ago | link

> You need bitcoin to write to Bitcoin's blockchain.

Right so, Blockchain is a methodology for a cryptographic system of record with transaction capability, and doesn't have to be used for currency, even though that's how everyone is using it. When you simplify it down to a 'system of record' then other use cases become new opportunities. I don't currently have useful ideas outside of currency, but my point was that Buffet doesn't need to understand the details of the methodology (bottom up) to establish an understanding of Bitcoin for investment purposes.

Even as a currency it is valuable provided that it gets tied to something tangible/meaningful. Personally I'm interested in ethereum because of its smart contracts and dapps.

> Gold has a lot of utility,

I'm aware that gold has some utility, so you are correct that I shouldn't haven't stated 'no utility', but gold's utility is not capable of justifying the price on the market. If gold was not being held as asset for the sake of being held as an asset, based on its rarity, then its market value would plunge to the near bottom.

> I'm skeptical of the notion of intrinsic worth. I think things have value because humans value them.

Right so, Gold has value because humans value it, but its value on the market is not tied to its intrinsic worth it's tied to its rarity alone. And that's the point of highlighting intrinsic worth over a human valuation. The value stability/durability is tied to its intrinsic worth.

The overall point is that Buffet is a value investor that's known to use intrinsic worth as the value measure. This is why Buffet hates both gold and Bitcoin. So let's not assume that he doesn't understand. Bitcoin's value is not stable because it's not tied to anything tangible. Even $ are just notes, but they are backed by a gov't which is why they have intrinsic value and therefore much more stability (or as much stability as the gov't/country provides).

note: made some edits to support correct word use.

reply

3 points by i4cu 36 days ago | link | parent | on: Hook

I've found the 'hook' mechanism to be really useful. I use the same thing in an app I'm writing (in cljs) which auto creates web applications via data schematics.

When my app compiles a new app there's a little routine that adds/removes pre-defined function calls from within a table. So then when cljs compiles the client side code (which performs dead code elimination) the functions that are hooked into the table get added, but the others are eliminated. The cljs dead code elimination process couldn't do this alone; it required being able to hook/unhook code depending upon the app schematic.

Anyways, just wanted add to your point on how useful this feature can be.

reply

More