Arc Forumnew | comments | leaders | submitlogin

One way to investigate this would be to use Racket's profiler: https://docs.racket-lang.org/profile/index.html

That tool's results are a little tricky to navigate, but there's a kind of indirect way to turn them into flamegraph SVG images via this library and flamegraph.pl: https://docs.racket-lang.org/profile-flame-graph/index.html

Racket also has some support for getting feature-specific profiling data for certain features, although I haven't tried it for anything yet: https://docs.racket-lang.org/feature-profile/index.html

I think Arc's load time is pretty typical of uncompiled Racket code. In Racket, people can speed up their development process by using `raco setup --pkgs my-package` to compile their libraries or `raco make filename.rkt` to compile individual files. That might be harder to do for Arc, especially the Arc REPL, since Arc's macroexpansion is interleaved with run-time side effects.


I'm not sure how to answer that. If you're asking what the easiest way would be to speed up initialization, it would be to load fewer libraries. Since it's an anarchic fork, we've all added lots of ideas that are good in principle but not always needed.

yes.

UPDATE

I got it to work! I believe akkartik was right. Using command (-c) did the trick, which I assume ensured disconnecting from the database.

  (system "psql -U root -d root -c 'SELECT * FROM items'")
returned

  item_id | name | type | description | created | location ---------+--------+------+-------------------------------------+----------------------------+---------------------------------------------------- 1 | marble | tool | A small, translucent orange bauble. | 2022-02-23 17:59:45.480545 | 0101000020E610000012C2A38D239A5EC040683D7C99E44240 (1 row)
Thanks!!!

btw, would this be the correct syntax for table-exists?

  (= dbconn (postgresql-connect "root" "root" password))
  (table-exists? dbconn "items")

>btw I caught a misspelled 'password' in postgresql-secure-connect:

oops. It's fixed, thanks.


Killing me. I don't think it's nginx..

Maybe it isn't disconnecting?

  (tostring (system "psql -V"))
Returned

  psql (PostgreSQL) 12.9 (Ubuntu 12.9-0ubuntu0.20.04.1)
just fine from the app.

Thanks! So far no avail, but I'll try some more!

btw I caught a misspelled 'password' in postgresql-secure-connect:

  (mac postgresql-secure-connect (user db ssl-protocol (o passowrd nil))

Hey Kartik!

It's a new project. However, Hubski has been using postgres for a few years, but it's currently an amalgam of Arc and Racket. I didn't write the db code, and am trying to start fresh. The Hubski racket connection looks like:

  (define db-conn
  (virtual-connection
   (connection-pool
    (lambda () (postgresql-connect
           #:user db-user
           #:password db-pass
           #:database db-database
           #:server db-server
           )))))
with

  (define db-user     "user")
  (define db-pass     "password")
  (define db-database "hubski")
  (define db-server   "localhost")
>Can you show what command you're running to check that you "can connect to the db from the Arc command line"?

Here's my test code that works in the command line, and the response. (I named my db "root" atm and will change that and the user once I get it working. I'm running the app as root.):

  arc> (tostring (pipe-to (system "psql -U root -d root") (prn "SELECT * FROM items WHERE name = 'marble';"))                                     )
  " item_id |  name  | type |             description             |          created           |                                                           
  location                      \n---------+--------+------+-------------------------------------+--                                     ------------ 
  --------------+----------------------------------------------------\n       1 | marble | tool | A small, translucent orange bauble. | 2022-02-23 
  17:59:45.480545 | 0101000020E610000012C2A38D239A5EC040683D7C99E44240\n(1 row)\n\n"
  arc>
But when I have that same code in the app, it times out. I tried to add (prn "\\q") at the end, but same result. Works in command line but hangs up in the app.

I even tried to specify the host and port:

  (pipe-to (system "psql -U root -h 159.203.186.97 -p 5432 -d root") (prn "SELECT * FROM items WHERE name = 'marble';"))
With this in my pg_hba.conf

  host    all             root            159.203.186.97:5432      trust
And the same result; returns in command, app times out. Checked the port:

  root@Xyrth:/home/xapp# netstat -tulnp | grep 5432
  tcp        0      0 159.203.186.97:5432     0.0.0.0:*               LISTEN      124232/postgres
Thanks for the quick reply!

BTW, I am using the domain xyrth.com with a nginx reverse proxy. I just had the thought it may be my nginx configuration. It probably needs to listen explicity on 5432? Here's my current sites-enabled:

  server {
    listen 80;
    server_name xyrth.com;
    return 301 https://xyrth.com$request_uri;
  }

  server {
    	listen              443 ssl;
    	server_name         xyrth.com;
        ssl_certificate "/etc/letsencrypt/live/xyrth.com/fullchain.pem";
        ssl_certificate_key "/etc/letsencrypt/live/xyrth.com/privkey.pem";
        ssl_session_cache shared:SSL:5m;
        ssl_session_timeout  10m;

        proxy_redirect      off;
        proxy_set_header    X-Real-IP $remote_addr;
        proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header    Host $http_host;

        location / {
                proxy_buffers 16 4k;
                proxy_buffer_size 2k;
                proxy_pass http://159.203.186.97:8080;
		proxy_set_header Host xyrth.com;
        }

        location ~* \.(png|jpg|tif|ico|ttf|woff|svg|eot|otf|mp3|ogg|wav)$ {
        root /home/xapp/static;
        expires max;
        }

        location ~ ^/(xyrth.css) {
                root /home/xapp/static;
                expires max;
        }

  }

Hi Mark!

Is this on hubski.com? Have y'all been using postgres there for a long time? Or are you trying it for the first time?

Can you show what command you're running to check that you "can connect to the db from the Arc command line"? I wonder if the app is able to connect but not disconnecting for some reason. That would explain the "thread took too long" timeout message.

If you've written some custom code to connect to postgres, that'd be helpful to look at as well.


There is a database wrapper (database.arc) in /lib that should support postgres.

It isn't well documented and only tested on a SQL connection but it's just a set of wrappers around Racket's DB drivers, so you could try including that into your app.

Unfortunately I seem to have completely lost my test project but the connection should work as follows:

    (= dbconn (postgresql-connect user db password))
Hope that helps.
2 points by shader 81 days ago | link | parent | on: Bel in Clojure

That was a fun and insightful take on the experience of implementing Bel.

The 'lit concept is pretty nice; like tags, but more thoroughly integrated.

I also like the idea of integrating interpreter features more completely, with globe, scope, err, etc.

It raises a question though; how does Bel relate to the Arc community? Is it the full successor? Can they coexist somehow?

3 points by rocketnia 95 days ago | link | parent | on: Latest source?

Yes, it's the fork that's been maintained by the people here at Arc Forum. :) I recommend it.

Whether you want the community additions or not, you might also be interested in the "official" and "stable" branches of that repo. The "official" branch tracks the official .tar releases that you'd find on the front page here, and the "stable" branch tracks the official releases plus a few small bug fixes and quality-of-life improvements.

3 points by capableweb 95 days ago | link | parent | on: Latest source?

Read around a bit and found a community maintained fork which seems to be a bit more updated (https://github.com/arclanguage/anarki), is that what people tend to use?

Tonight I did the same thing for the last of my Arc repos, Penknife.

  $ npm install --global rainbow-js-arc framewarc penknife-lang
  $ rainbow-js-arc init-arc my-arc-host-dir/
  $ framewarc copy-into my-arc-host-dir/lib/framewarc/
  $ penknife-lang copy-into my-arc-host-dir/lib/penknife/
  $ cd my-arc-host-dir/
  $ rainbow-js-arc run-compat -e \
      '(= fwarc-dir* "lib/framewarc/")' \
      '(load:+ fwarc-dir* "loadfirst.arc")' \
      '(= penknife-dir* "lib/penknife/")' \
      '(load:+ penknife-dir* "penknife.arc")' \
      '(pkrepl)' \
      -q
  pk> [+ arc["Hello, "] arc["world!"]]
  "Hello, world!"
Penknife really was just a pile of code before now. I originally wrote it on my commute using an Android phone. Now I've finally gotten around to giving it a readme, a .gitignore, and all that. Whew. :)

There was another language I called Penknife a couple of years later because I thought of it as having the same set of design goals, so technically this one's more like Penknife Mk. I. That's what I think I'll call it.

Something about Penknife Mk. I that I often think back to is its approach to macro hygiene.

Its syntax is primarily string-based, but with the ability for other values to be embedded inside the strings. It has a kind of quasiquotaton that surrounds the quoted section with an object wrapper that the macroexpander recognizes. When the macroexpander expands that expression, it switches over to using the scope at the macro's definition site. Interpolations in the quasiquotation are surrounded with another wrapper that causes the macroexpander to switch back to the caller's scope.

When the macro is defined, its lexical scope is captured and carried on the macro's binding. That way, it's the namespace that holds other namespaces inside it. The syntax trees don't have to hold namespaces; they can just hold paths to traverse the namespace hierarchy.

I still think of this as a nice sweet spot; the code has a context-independent enough identity to be compiled, while the namespaces are mutable enough to allow REPL interaction.

Racket's sets-of-scopes approach to hygiene is mature and supports advanced features like local macros and local definition blocks, but I think of it as kind of sloppy. It spray paints one piece of information over the whole syntax tree just to mark that a variable is in scope. This approach could be handy in cases where variable scopes overlap with each other in non-hierarchical ways -- where sometimes one variable is in scope, sometimes the other, and sometimes both -- but I feel like Penknife's wrapper objects are a much tidier model of the typical hierarchical structure. There've been many moments in my Racket programming when I would have liked Racket to have Penknife Mk. I's approach to hygiene instead.

- Penknife Mk. I on GitHub: https://github.com/rocketnia/penknife

- Penknife Mk. I on GitHub (current snapshot link): https://github.com/rocketnia/penknife/commit/509a7c21d750a24...

- Penknife Mk. I on npm (currently version 0.1.0): https://www.npmjs.com/package/penknife-lang

- The Era repo on GitHub, home of Penknife Mk. II and a few other things: https://github.com/era-platform/era

- The Era repo (current snapshot link): https://github.com/era-platform/era/commit/7f6751cb4d15f8c8c...


Links:

- Rainbow.js on GitHub: https://github.com/arclanguage/rainbow-js

- Rainbow.js on GitHub (current snapshot link): https://github.com/arclanguage/rainbow-js/tree/655c4e493c417...

- Rainbow.js on npm (currently version 0.2.1): https://www.npmjs.com/package/rainbow-js-arc

- Framewarc on GitHub: https://github.com/rocketnia/framewarc

- Framewarc on GitHub (current snapshot link): https://github.com/rocketnia/framewarc/tree/12b6962fc3b7fb93...

- Framewarc on npm (currently version 0.1.2): https://www.npmjs.com/package/framewarc

- The original Lathe repo (old link): https://github.com/rocketnia/lathe

- The original Lathe repo (new link): https://github.com/lathe/lathe

- The original Lathe repo (current snapshot link): https://github.com/lathe/lathe/tree/79302086a5a6876d8e282d8f...

Another repo that I just forked off of the original Lathe repo is Lathe Comforts for JS:

- Lathe Comforts for JS: https://github.com/lathe/lathe-comforts-for-js

- Lathe Comforts for JS (current snapshot link): https://github.com/lathe/lathe-comforts-for-js/tree/165d3844...


Yes it does.

    $ git clone git@github.com:arclanguage/anarki.git
    # install Racket 7.7
    # create x.arc
    $ cat x.arc
    (prn "hi")
    $ anarki/arc.sh
    arc> (load "x.arc")
    hi
But like I hinted, anarki is the community-managed fork and has 3 incompatibilities with Arc 3.2. Be especially careful if you're already managing a HN-like site using Arc 3.2.

I don't recall the details atm, but I believe https://github.com/arclanguage/anarki does this correctly. Can you check? Read the readme for caveats first, though.

Getting back after arc is not the problem; even if I didn't have cd history set up a simple `cd -` would handle that.

Simple problem: I have a file containing a program written in Arc. I want to run it from the shell. How do I do that? I can fire up arc and `(load)` the file, but then the file needs to be in the arc2.3 dir or else I have to load it by absolute path, even if I was just in the same directory it's in.

For now I've just switched to using Anarki; I can run its `arc.sh` start script from anywhere in the filesystem and feed it a locally-relative pathname and it will run. Although it does seem to take quite a lot longer to start up than arc.

2 points by zck 221 days ago | link | parent | on: Running arc from outside the arc2.3 dir?

What OS are you on? If you're on Linux or MacOS, you can use `pushd` and `popd`.

On bash, I have some aliases set up. they will move you to the right directory, run arc or anarki, then when it exits, move you back to the original folder.

alias arc="pushd ~/programs/arc/arc3.2/; rlwrap racket -f as.scm; popd" alias anarki="pushd ~/programs/arc/anarki/; ./arc.sh; popd"

I normally use fish shell, but functions there are simple enough to make based on the bash aliases.

3 points by akkartik 244 days ago | link | parent | on: Arc Forum installation Step 3

Is this step 3 at http://www.arclanguage.org/install? You need to open a terminal. What OS does your computer use?
2 points by jsgrahamus 244 days ago | link | parent | on: Arc Forum installation Step 3

What is the link you are using?

There's not much use I have for fexprs. I just had certain ideas about how they fit in with other concepts and how they could be made compatible with a compilation-favoring workflow.

I consider it basically a mistake to use the same syntax for macro calls and function calls... but not because they're different things. When we want to invoke a function like a macro, we can just conceptualize it as a macro that expands into a function call.

I think it's the same for fexprs. Macro calls can more or less expand into fexpr calls by simply preserving all the code they received under a `quote` in their expansion result. This doesn't quite get us a lexical-scope-respecting version of fexprs unless we also capture the local lexical environment, and that's not necessarily possible depending on the macro system, but it's not much of a stretch:

- Racket's macroexpander keeps track of the set of local variables in scope, but it doesn't quite expose it to user-defined macros.

- Arc's macroexpander `ac` keeps track of the set `env` of Arc local variables in scope, but it doesn't expose it to user-defined macros.

- In Guile, the local environment can be captured using (procedure-environment (lambda () '())).

- Fexpress currently lets compilation-friendly fexprs do this using the `depends-on-env?` field of a `compilation-result?` data structure. If a subexpression depends on the lexical environment this way, (fexpress-clambda ...) forms surrounding that subexpression expand differently so that they create a run-time representation of the lexical scope. This way, no one step in the code has to traverse or build up the whole environment, so the cost is spread out. Since Fexpress's variables are immutable, variable accesses don't even have to go through this run-time environment; they can be Racket variable accesses.

Anyhow, I basically consider fexprs to be one of the things a macro-capable language is theoretically capable of, even if people don't commonly prefer to use that functionality and macro systems don't always quite offer it.

I've been interested in exploring the concept of typed macros in general for the purposes of designing module systems which have both macros and typed API signatures. And I've had typed fexprs on my mind as an optimization approach since the Eight thread way back when, and I expect these two trains of thought to be on their way to the same destination.

With Fexpress, I explored the typed fexpr side, and I kept a really narrow focus on types for optimization rather than API boundary delineation so that I wouldn't make it any more complicated than it had to be. I don't want to be like "oh, by the way, in this complex type system for macros, you can find fexprs if you look for them," because some people might recoil at that. :-p And if I said something like that, but I didn't actually have an fexprs-by-default language ready to show, that would be quite a tease.

The actual languages I build with these ideas will probably not have fexpr calls as the default behavior for calling a local variable. Personally, I prefer not to have any default; it's not too much work to write out `funcall` unless I have a whole DSL's worth of local variables I'm invoking. But if it's a typed language, a lot of the local variables will probably have Lisp-1-style behavior anyway, because a variable of function type could be known to have function-call-like macro behavior and so on. And if some of the types people use turn out to declare that their variables have fexpr-like call behavior, I'll consider that to be an interesting outcome. The types, if they do any kind of soundness enforcement (unlike Fexpress's unsound hints), can keep those fexprs from sneaking into other people's code unless they're ready to receive them.

I know API enforcement wouldn't necessarily be your favorite feature in those designs. :-p But I think that's basically the picture of where Fexpress-style fexprs slot into my long-term goals, basically as one part of the possibility space that macros have more room to explore with the help of a type system.


To be clear I still use macros. I'm skeptical of the benefit of fexprs, but it totally makes sense to support them as a sharp-edged, high-power capability intended to be used rarely.

I think macros can get pretty complicated fast. I think in some ways it's at least as hard to write well polished macros (with good error messages, editor support, debugging steppers, pretty printers, and so on) as it is to build a well polished language without macros. I think the reason it feels easy to write macros in many languages is because many languages don't set a high bar for a polished experience.

I don't think taking macros out is an answer I'm very interested in, though. I want people to be able to use my stuff without having to write code the same way I do, and vice versa. Some kind of syntactic extensibility is important to me.

And I don't think taking macros out even really eliminates the "tied to the front end" non-compositionality issues. Every abstraction that can be used has a user experience, and macros just take more control of that experience than functions do. Every time a programmer tries to report a friendly error message from a function, they're trying to take some control over that experience, but the capabilities of a function only give them so much information to work with.

A language without macros can supply a one-size-fits-all kind of polished experience, which may indeed be a much better experience than what most macros provide in practice. But a language like that also imposes a practical limit on how much more polished it can get, especially for domain-specific applications that don't have the attention of the language developers.


Thank you, that is very helpful! And this also matches some of my growing ambivalence with macros.

When I was building Wart[1], I repeatedly ran into situations that seemed very difficult to debug. Looking back, I tend to debug by simulating computation in my head, and macros made it easy to create code that was more complicated than I could visualize[2]. When I built Mu[3] I hoped that backing away from first-class macros and pervasive support for traces[4] would help. That's still in early days, but the evidence is quite ambivalent so far.

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

[2] https://www.goodreads.com/quotes/273375-everyone-knows-that-...

[3] https://github.com/akkartik/mu

[4] https://archive.org/details/akkartik-mu-2021-06-23


"Were you thinking you'd have something called `map/f` that can take non-functions as its first argument?"

No, I was thinking `map` should only take a procedure.

I don't think there's a particular way to "map" an fexpr that stands out as a compelling abstraction. I propped up a certain ambitious "evaluate a list-valued expression without evaluating the list elements" vision for `map` above, but this is really more of a lazy function map than an fexpr map.

The `map` operation is basically about the list data structure. Since it's a data structure that holds its elements eagerly, we can transform it with an eager function. If it held its elements lazily, we could use a lazy function. If it held its arguments unevaluated-ly, and if the elements didn't even have to be expressions, then the abstractions we could map over these lists might be fexpr-like, but I don't know of a way to make that make sense.

If you're thinking "but then what do you do with an fexpr once you have one?" I don't know. I've only found so many compelling uses for fexprs, which have to do with places where the code of the call site is needed at run time, possibly because run time and compile time are tangled up:

- Defining custom syntaxes in mutually recursive ways that involve invoking some of the syntaxes before some of the others have finished being defined.

- Making macro systems where redefinitions of the macros automatically propagate to change the behavior of previously defined things that call them.

- Writing abstractions that take care of their own JIT behavior. In order to strategically recompile their use sites according to usage statistics collected at run time, they need access to both the source code and the ability to observe run-time conditions.

In each case, fexprs would be serving as a language front-end layer, not as internal building blocks for general-purpose computation. I think source code is second-class in an essential way; although we can write macro abstraction layers that fabricate source code as they go along, this makes it harder to report informative errors in terms of the user's original code. Macros and fexprs take source code as input, so they're tied to the language front-end in a way that procedures aren't.

And when the source code isn't something we're abstracting over much, then neither is the environment of macros or fexprs that are in scope there. (Like, the environment is more or less an annotation on the source code, and if we focus on having only one original source codebase to annotate this way, then we only have one set of annotations to produce.) So a lot of the best techniques for macros or fexprs are likely to be self-contained abstractions that keep their first-class manipulations of macros and fexprs behind the scenes, rather than whole programming styles which make pervasive use of macros or fexprs as first-class values.


Sorry, I do know that fexpr lisps can combine `map` and `and`. It sounds like you're proposing a different approach in Fexpress, so I was wondering what sort of vocabulary you are imagining. Were you thinking you'd have something called `map/f` that can take non-functions as its first argument? And so there'd be fexpr-aware variants for all higher-order functions? (Thinking harder, an `and/f` doesn't really make sense here.)

By the way, if you'd be interested in a version of `and` that can be used as both a Racket macro with short-circuiting behavior and a first-class procedure without it, you can do that in Racket (using essentially a symbol macro):

  (define (my-and-procedure . args)
    (andmap identity args))
  
  (define-syntax (my-and stx)
    (syntax-parse stx
      [_:id #'my-and-procedure]
      [(_ args:expr ...) #'(and args ...)]))
This basically reproduces the same halfheartedly fexpr-aware behavior as PicoLisp, but without any fexprs:

  > (map my-and (list #t #f) (list #f (displayln "oops")))
  oops
  '(#f #f)
  > (list (my-and #t #f) (my-and #f (displayln "oops")))
  '(#f #f)
In the context of Fexpress, this punning can be taken to another level, letting the first-class version of `my-and` be both a procedure and an Fexpress fexpr at the same time, while the second-class calls to it have Racket macro behavior. I've put together a test to demonstrate it:

- Code link: https://github.com/rocketnia/fexpress/blob/main/fexpress-tes...

- Code link pinned to the current commit as of this writing: https://github.com/rocketnia/fexpress/blob/e33c07a4e8252793a...

In the Fexpress proof of concept, there's no way to convert between Fexpress fexprs and Racket macros in either direction. That makes this punning pretty pointless, except perhaps as a technique for publishing libraries that can be used roughly the same way by both Racket and Fexpress programmers.


The point I'm making in that paragraph is that there are reasons for `map` and `and` not to work together even in an fexpr-capable language. So I'm not sure what you're asking for; I'd say Racket's `map` and `and` already are "fexpr-aware."

Are you asking for clarification on how "this interaction succeeds" in a language where fexprs are more pervasive?

Let's see... I found an online PicoLisp runner.

As an initial test, `prin` prints and returns its string argument, and anything non-NIL counts as truthy:

  : (and (prin "hello^J") NIL (prin "world^J"))
  hello
  -> "hello^J"
So here's `mapcar` mapping a function over two lists:

  : (mapcar '((a b) (+ a b)) (list 1 2 3) (list 4 5 6))
  -> (5 7 9)
Here it is mapping `and` over two lists, where "the interaction succeeds" in the sense I was referring to:

  : (mapcar and (list T NIL) (list NIL (prin "oops^J")))
  oops
  -> (NIL NIL)
Here it is mapping a custom fexpr over two lists so we can see what unevaluated arguments it observes:

  : (mapcar
      '(args
        (println args)
        (and (eval (car args)) (eval (cadr args))))
      (list T NIL)
      (list NIL (prin "oops^J")))
  oops
  ($177775247560141 $177775247560143)
  ($177775247560141 $177775247560143)
  -> (NIL NIL)
(PicoLisp doesn't evaluate rest arguments like `args` here, even though it does evaluate every other argument in the argument list, and that's by design. This is how we can write custom fexprs.)

It appears that what our fexpr observes is a list of what PicoLisp calls "anonymous symbols" (probably like gensyms), which apparently are bound to the already-evaluated argument values.

Incidentally, notice how this `mapcar` call fully evaluates the list arguments, including the part that prints "oops." That makes these two programs arguably inconsistent:

  : (mapcar and (list T NIL) (list NIL (prin "oops^J")))
  oops
  -> (NIL NIL)
  : (list (and T NIL) (and NIL (prin "oops^J")))
  -> (NIL NIL)
When the transformation passed to `mapcar` is a pure function, refactoring a `mapcar` call this way makes no difference. When the transformation is a side-effecting procedure, it does make a slight difference because one transformation call might perform its effects before the next transformation's arguments have started to be evaluated. And here, when the transformation is `and`, this refactoring makes a difference because `and` usually changes how often its arguments' effects are performed, but `mapcar` doesn't let it have that control.

That more or less makes sense operationally, but it means that in PicoLisp, I could imagine `mapcar` being more "fexpr-aware" than it is now. A more fexpr-aware design for `mapcar` would either:

- Allow `and` to affect the evaluation of `mapcar`'s own arguments. Now we get to implement a way to evaluate a list-valued expression without evaluating the list elements, so that we can pass the elements along to `and`. And once we're done with that, what should happen when we map fexprs that aren't even procedure-like, such as (mapcar let ...) or (mapcar setq ...)? Is there even a point to doing any of this?

- With full awareness of fexprs, make the decision to explicitly disallow non-procedure fexprs from being used with `mapcar`. That way, we don't even open up that can of worms.

Racket's `map` essentially accomplishes the latter.

More