Yeah, none of those call sites seem responsible, as far as I can see. The calls in markdown and unmarkdown only trigger if the string starts with certain chars, which it doesn't. Remind me, are you using anarki?
anarki $ git pull # should now have a file called 'arc'
Now an executable file like this (called say x.arc) works:
#!/PATH/TO/arc
(prn argv) ; argv now contains commandline args
Try it out:
anarki $ ./x.arc a b c
(./x.arc a b c)
The repo is a little messy now. I had to create a new asv.scm that looks substantially like as.scm, and the 'arc' script is in addition to the older 'arc.sh' which is for interactive use (and which I don't use, preferring to directly 'racket -f as.scm'). Anybody have suggestions for either? Should we rename the old arc.sh to 'iarc'?
Agreed. Which is why we should pool resources and share the first time we encounter such a bug. Or even a possible such bug that we can then debug together.
For my part, I have had an eye out for it for three years and have yet to encounter such a bug. Here's my hand-wavy reasoning for why I think it can't happen: for it to happen, an outer macro would have to rely on a use of a specific gensym inside a nested macro. I can't imagine how that could happen short of actually trying to use a gensym:
(macro1
(macro1
..x23..)) ; brittlely relying on the gensym turning into *x23*
And if we are to worry about this we can worry about all gensyms anyway.
Okay, so the example with accum actually does work fine, but, only due to lexical scoping. I'm still afraid of the possibility of macros using the same symbols and causing a collision. While it looks like it would be an extremely rare bug to find, it would be one of those bugs that are a real pain to debug.
Anarki doesn't have mailgun support yet. It will reset passwords if your server supports sending email using the local unix sendmail command. So I can think of two options:
a) Does mailgun provide a sendmail command that you can download? If so, just installing that might be all you need.
b) Anarki has rudimentary support for GET and POST on arbitrary urls.
Can you try to figure out the right URL to put into the get-url call or similar post-url call? They're implemented in lib/web.arc, and feel free to ask us questions to help get you on your way.
Yeah, I actually thought about this use case back when I wrote accum for wart. But there's no problem here since the two expansions also create lexical scopes. In unmodified anarki:
arc> (mac foo (accfn . body)
`(withs (acc@ nil ,accfn [push _ acc@])
,@body
(rev acc@)))
#(tagged mac #<procedure: foo>)
arc> (foo a
(each l '(1 2 3)
(a:foo b
(each m (list l (+ l 1) (+ l 2))
b.m))))
((1 2 3) (2 3 4) (3 4 5))
Is there some other scenario that I'm not considering?
(I hadn't realized that w/uniq is superior to implicit gensyms in this regard. Thanks.)
The idea behind autouniqs is that they remove some boilerplate code (ie w/uniq). The current implementation of autouniqs[0] is actually broken. The problem boils down to the fact that with the current implementation, macros will wind up using the same uniqs every time they are expanded. Say accum was implemented using autouniqs.
The intention is acc@ will be replaced with a uniq every time accum is expanded. With the current implementation of autouniqs, acc@ will be replaced with a single uniq in accum's definition. This leads to problems with nested use of accum.
(accum a
(accum b
(...)))
What clojure does to solve the problem is combine "autogensyms" with its implementation of backquote. All the symbols ending in '#' inside of the same backquote are replaced with gensyms when the backquote is evaluated. While clojure's implementation is good for most things, it breaks down with any kind of nested backquotes [including `(... ,(... `(...)))] since the autogensyms will not be the same inside all of the backquotes. I think tying the autogensyms to backquote is a requirement due to the fact that every time the backquote is evaluated new gensyms would have to be put in to replace all of the autouniqs/gensyms (feel free to prove me wrong). This becomes obvious when one starts thinking about macros with nested backquotes and how they would expand. What I want is some version of autouniqs such that they completely eliminate the need for w/uniq.
I may have figured out a possible way to implement backquoting in such a way based off of Steele's implementation[0]. All that is needed is an addition macro that will be put outside of each backquote expansion. When that macro finally is expanded (when all of the other backquotes have been used up), it would go through and replace all of the specified symbols with a uniq symbol up to any further backquotes. This would make it possible to have the uniqs generated every time each backquote is executed.
The only problem I can think of is something like this:
`(x@ `(x@))
How would one specify for the generated symbols for both x@ to be the same. There might already be a way to do it through use of unquoting but I'm not sure how that would interact with the backquote implementation.
If you want to have full tail recursion elimination, I recommend changing everything to work in continuation-passing style. That will overflow the JS stack even sooner, so I recommend using a trampoline.
// before
function plus( a, b ) {
return a + b;
}
// after
function plus( a, b, then ) {
// We call `then()` only after we've unwound the stack.
return trampolineBounce( function () {
return then( a + b );
} );
}
// the trampoline system
function trampolineBounce( func ) {
return { type: "bounce", func: func };
}
function runTrampoline( func ) {
var step = func( function ( result ) {
return { type: "result", result: result };
} );
while ( step.type === "bounce" )
step = step.func();
return step.result;
}
// example usage
var onePlusTwoPlusThree = runTrampoline( function ( then ) {
return plus( 1, 2, function ( onePlusTwo ) {
return plus( onePlusTwo, 3, function ( result ) {
return then( result );
} );
} );
} );
This part is a bit redundant:
function ( result ) {
return then( result );
}
It's equivalent to `then` but uses one more JS stack frame. We can just reuse the `then` continuation directly. In general, the places we can do this are tail calls.
// example usage with a tail call
var onePlusTwoPlusThree = runTrampoline( function ( then ) {
return plus( 1, 2, function ( onePlusTwo ) {
return plus( onePlusTwo, 3, then );
} );
} );
---
"I'm finding a method to transform recursion code to iteration code automatically"
I don't think it's possible to get full tail recursion elimination that way unless you statically analyze the whole program, or you transform the code so it uses the CPS-and-trampolining technique above.
Maybe you can get something somewhat less powerful than full tail recursion, but I don't have advice on that. If you do want to work on that, here's an example of a tail-recursive while loop you can test with:
; Evaluate an expression repeatedly until it returns something falsy.
( (lambda (f body) (f f body))
(lambda (f body)
(if (body)
(f f body)
#f))
(lambda () ___)) ; insert expression here
Thanks to your advices. The runtime is based on Node.js, so there is no tail recursion elimination, I'm finding a method to transform recursion code to iteration code automatically, do you have some advice?
Thx,i have almost done my arc site,but when a user forgot his password and he need to reset by email,but cannot get the email...i wanna know how to add email feature in arc site? by the way,i use mailgun.com to send and they give me HTTP API.like this http://documentation.mailgun.com/quickstart.html#sending-mes...
Yeah, atstrings is a pretty awkward feature. It is possible to opt in and out of it, but only at the top level:
(declare 'atstrings t)
(declare 'atstrings nil)
This affects how strings are processed by 'ac. That is, it only matters for compilation/macroexpansion phases.
I see some of Anarki's tests set 'atstrings to a particular value as they go along, but then they don't reset it. This might be why you see some tests working when yours don't.
(In Anarki, the declarations* global table holds these values, so it's technically possible to save and restore the old value by reading that table.)
---
The REPL behavior you observed is confusing in a different way than you thought. It treated your input as a malformed command, three well-formed commands you didn't know you were entering, and finally an unfinished string literal:
"abc\@
example.com
"
"
abc@@example.com
"
This is where the "\n" response came from, and it's why you received multiple responses per input line.
I'm unable to reproduce it now. I disabled the bug fix, and I believe I am now running the same code as I did when the bug occurred, but I no longer get this exception when I visit that url (it's an ordinary GET request without parameters). The state of the app has also changed and I wonder if this state change has something to do with it.
What's certain is that it's a call that passes 3 arguments to cut. I had logged the exact call to cut to be:
I'm concerned about looping. It looks like you're implementing function calls in terms of JavaScript function calls, so there's no tail call elimination, and a recursive loop will soon exhaust the JS stack. A while loop could be a practical alternative.
"It's the kind of takeover any language could do to any other"
JavaScript's a big language. It comes standard with complex (and I'd say quirky) systems for floating point numbers, date handling, and regexes. ECMAScript 5's strict mode repurposes the same JavaScript syntaxes for slightly different semantics, fracturing the language even within the same spec. That spec also defines at least three different ways of interpreting a JS string as JS code, if we count eval(), strict mode eval(), and the Function() constructor.
The standard is just one small glimpse of the landscape of JavaScript compatibility:
The language is commonly split into intentionally different dialects. The HTML spec willfully violates the ECMAScript spec so that document.all can be falsy[1]. Mozilla's asm.js repurposes the same JavaScript syntaxes to have substantially different performance when they're used a certain way. And of course there are proper extensions and variations of the language, like TypeScript, Harmony, Mozilla's own line of JavaScript versions, the JavaScript-inspired UnityScript and ActionScript, etc.
Nonstandard features are frequently needed. Different JavaScript platforms like the HTML spec, Node.js, Rhino, RingoJS, and Windows Script Host add (or don't add) their own varying tools for concurrency, sandboxing, error handling, debugging, global scope management, package distribution, and module imports, which tempts even general-purpose library writers to stray from standards. Meanwhile, JavaScript libraries sometimes silently take advantage of widespread but underspecified features such as function names, function toString() representations, object.__proto__, and object key iteration order.
Given all this, the good news is that a translator from JavaScript can't be expected to have 100% compatibility. Like Caja, the Clojure Compiler, and Browserify, it may have to put quirky limits on the subset of JS code it intuitively supports. One place to look for a not-so-quirky subset might be lambdaJS[2], which has been used for formal correctness checking.
In case anyone wonders what's wrong with (if ...), I've put together a demonstration of translating Arc-like code into the flavor of generalized arrows[4] David and I use for implementing this stuff:
(if foo
(bar baz)
qux)
( | + | ) | | |
( foo(true) + foo(nil) ) bar baz qux
=======================Disjoin=====================
foo(true) bar baz qux + foo(nil) bar baz qux
| | | | + | | | |
Drop Drop Drop | + Drop =Apply= Drop
| + |
=========Merge========
|
In this graph, sum types (A + B) are edges separated by + and product types (A * B) are simply juxtaposed edges. I've labeled the edges around Disjoin to show which signals go where.
The "Disjoin" operation is the distributive law on product and sum types, taking ((A + B) * C) to ((A * C) + (B * C)). In simple cases, it has to synchronize C with a combination of A and B so it knows which output to send C's packets down.
The "Merge" operation takes (A + A) to A, forgetting which branch it was on. In simple cases, it has to synchronize the original two signals becuase the output should only be inactive when both inputs are inactive. If we're using dynamic typing, we may also need to check the inputs dynamically to make sure their periods of activity don't overlap.
Overall, one call to (if ...) causes multiple synchronization points in the code.
You might wonder what Arc could use here if not (if ...), and my guess is it could use a structured data type with associated special forms that can be implemented with implicit concurrency:
(def left (x) (annotate 'left x))
(def right (x) (annotate 'right x))
; Sum types distribute over product types.
; ((A + B) * C) -> ((A * C) + (B * C))
(disjoin (left a) c) --> (left (list a c))
(disjoin (right b) c) --> (right (list b c))
(disjoin other c) --> (err "Can't disjoin")
; Sum types are commutative.
; (A + B) -> (B + A)
(mirror (left a)) --> (right a)
(mirror (right b)) --> (left b)
(mirror other) --> (err "Can't mirror")
; Sum types are associative.
; ((A + B) + C) -> (A + (B + C))
(assocsl (left (left a))) --> (left a)
(assocsl (left (right b))) --> (right (left b))
(assocsl (right c)) --> (right (right c))
(assocsl other) --> (err "Can't assocsl")
; (A + (B + C)) -> ((A + B) + C)
(assocsr (left a)) --> (left (left a))
(assocsr (right (left b))) --> (left (right b))
(assocsr (right (right c))) --> (right c)
(assocsr other) --> (err "Can't assocsr")
; (A + A) -> A
(merge (left a)) --> a
(merge (right a)) --> a
(merge other) --> (err "Can't merge")
; (A -> C) -> (B -> D) -> ((A + B) -> (C + D))
((lift-sum l r) (left a)) --> (left (l a))
((lift-sum l r) (right b)) --> (right (r b))
((lift-sum l r) other) --> (err "Can't lift-sum")
This is quite a bundle of special forms to add, and I'm probably forgetting a few. I'm not exactly sure how to implement them, especially considering they would have to deal with Arc's arbitrary recursion. ^_^;
Good questions! I keep wanting to tie this stuff back to Arc somehow, so that people here can care, lol, but it's hard to see where to start.
I'll assume we're talking about something like RDP, but not quite RDP. David Barbour's goals for RDP include safe distributed programming, secure mashups, and sometimes even type-directed reduction of boilerplate. Thus RDP isn't necessarily RDP without an appropriate static type system, and we proabably don't need to commit to that part here.
Let's just call what we're talking about "continuous reactive" code, or "continuous" for short. :)
I'll answer out of order a bit.
---
"And what kind of metaprogramming models besides function/macro/fexpr do you see working on top of such a system?"
All of these actually keep making sense in a continuous system. The only things that can't technically be carried over are certain kinds of side effects.
Here's a more complete description of the differences:
- Most imperative side effects don't make sense. If (do (= x 1) (= x 0)) is executed continuously at every instant, then at no time is x just 1 or just 0. Continuous reactive programming needs its own particular side effects to deal with continuously displaying things, continuously maintaining connections to things, continuously monitoring the current state of the mouse, etc. Instead of mutation, continuous reactive code can use demand monitors, which aggregate sets of values. Similarly, we would have to avoid effects related to first-class continuations, threads, etc., unless we can make sense of them.
- Since all the effects at a given instant are simultaneous, the data/control flow doesn't need to be processed in a particular order, and it can naturally be concurrent, which in turn means each concurrent part can be undisturbed for a good period of time without needing to be recomputed. This means certain constructs like (if ...) stick out as expensive operations, since they must synchronize their inputs and outputs rather than letting them pass through concurrently.
- Eval and function calls are pretty weird operations. In a continuous program, the entire data flow graph exists like a constantly marching trail of ants, sometimes switching between different conditional branches. I think a dynamic call causes that trail of ants to sometimes snap between different iterations of a fractal pattern. My current implementation[1] supports "functions" but doesn't support arbitrary recursion, because I keep every possible trail instantiated regardless of whether it's currently in use. It's like all the functions are inlined whether we like it or not. (I actually kinda like it.)
- If we care about secure mashups, passing a big context object around tends to be a bad idea, especially if it's implicit. Fexprs may need a redesign in order to avoid passing authority to code that doesn't need it.
---
"How would you build a declarative continuous state system on top of arc? Or would it be better to start from scratch?"
(Building the execution semantics is probably a bigger challenge than building the state resources, so I'll assume that's what you mean.)
While starting from scratch would keep things simpler, I don't think it's necessary, as long as there's some bridge to manage the continuous side from the procedural side and/or vice versa. A single language could have dedicated syntaxes (e.g. lisp special forms) both for procedural code and for continuous code.
I've actually been thinking about how a single (fn ...) syntax could be used for both. Pure code is a special case of error-free continuous impure code, and that in turn is a special case of error-free procedural code, so a single abstraction might cover it all.
However, dynamic errors make things more interesting. In procedural Arc code, (do (foo) (err "oops 1") (bar) (err "oops 2")) would not execute (bar) because that comes after an error has occurred. In continuous code, all of the code is executed at every instant, so there is no "after" to speak of, and (bar) executes at the same time as both errors occur. (Actually, David's approach would be to terminate the computation as soon as possible and try to roll back any effects that have occurred in the meantime, so in that case (bar) would not usually be seen to execute.)
This is enough of a jarring discrepancy that I've tabled the idea of unifying the (fn ...) syntax in the procedural language I'm working on.[2] I think it's something that can be done, but it's more complex than I first thought.
Anyway, suppose we have two different syntaxes, or suppose (as David prefers) we don't have a continuous (fn ...) syntax at all but instead use concatentive programming techniques.[3]
Either way, it's painful to bridging between one style of computation and another.
Procedural code managing continuous code: Procedural code has to deal with continuous signal input similarly to how it would traditionally deal with keyboard and mouse input, and it has to deal with continuous signal output kind of like drawing to a canvas. This means writing 2-5 imperative code blocks for every bridge, at least until some common bridge patterns can be abstracted away.
Continuous code managing procedural code: One way to use continuous code to trigger an imperative action would be to build a continuously updating collection of the imperative actions we want performed, annotating each one with a timestamp so it's clear when it's supposed to happen exactly. This is cumbersome enough that I haven't tried it yet, but it's basically the kind of FFI layer that a continuous-only language would need in order to play nicely with existing OSes and protocols.
The Web is a good example of a system that already has bridges like what we'd need. An HTML document can have continuously existing tags and attributes that cause JavaScript code to be executed at certain times. In turn, JavaScript code can insert and remove declarative HTML and CSS as it goes along.
Sorry for using so many JS metaphors. :) Aside from Rainbow's UI experiments, I don't know of any Arc applications which continuously monitor or display something.
[3] Either way, I've pretty much made this in JavaScript already,[1] but my (fn ...) syntax is a bit cumbersome because I'm using strings for variable identifiers, and my concatenative syntax is even more cumbersome because it has to pass around explicit first-class type annotations with no type inference. I think there's room to improve on this, especially in something like Arc that has macros.
How would you build a declarative continuous state system on top of arc? Or would it be better to start from scratch?
And what kind of metaprogramming models besides function/macro/fexpr do you see working on top of such a system?
It seems like it could either be a really 'alien' programming model that requires very different approaches, or a simple set of wrappers on top of existing models. I don't know enough about it yet.
It actually surprises me how much Versu development resembles some of my own thoughts about organizing large-scale interactive story plots. I once dreamed of making a language for building interacting FSMs, with support for automatic testing or formal model-checking to find dead code. Apparently this was all in the scope of Praxis and Prompter tools, and I didn't have to lift a finger to see this dream come true. ;)
I'm concerned about some drawbacks this project might face, having to do with the use of instantaneous events for modeling story actions and the use of inline dialogue generation.
Instantaneous events are something I tend to see as a misfeature[1]. The notion of a discrete timeline is baked into the sequence-of-paragraphs UI, the screenplay-like Prompter language, the state machines, and the visualization graphs. Even the logical framework needs it, since the characters instantaneously "remove" past beliefs as they're convinced of incompatible beliefs to replace them.
At least it can be said that in Versu's chosen problem domain, events are essential. They couldn't be "fixed" without changing basically the entire story UI, the tools, and the formalism, leading to a completely different product. At least this project seems to have good tooling to tackle many event-related issues head-on.
As for inline dialogue generation, I've just never been comfortable with the thought of generating story prose using string concatenation; at least a more structured format would be nice. Emily Short and Graham Nelson are two of the people who think about this topic quite a lot, and their context-sensitive text macro approach is impressive, but it uses too much stateful magic for me to want to dive in and use it. (Same with some of the naturalistic syntax...) It's not like I dive into story-writing regardless, but it's one of the basic motivations I use to write programs, which is why I have opinions like this one. :-p
Regardless of any drawbacks--even if Versu had been a closed system under Linden Labs--I was always eager to use it. I may not write full stories, but I do design lots of original characters and settings, and Versu has been designed with the high-level idea that characters can be swapped out for each other to see how they interact in different circumstances. It could be just the right platform to give some life to my ideas.
Versu is an interactive story platform that was developed in a project overseen by Linden Labs, best known for Second Life. The drivers behind this project were Emily Short, a famous author of text-based interactive fiction, and Richard Evans, who had previously worked on the AI for The Sims 3. These are some documents describing the platform they built.
I think one of the docs people here would like is "Prompter: A Domain-Specific Language for Versu":
Graham Nelson made Inform 7, an interactive fiction engine with English-like syntax. Now he's made Prompter, a similarly naturalistic language for Versu. In that introduction to Prompter, he talks about motivations and principles for DSL design.
Prompter compiles to Praxis, which is a logic programming language Richard Evans designed for Versu to handle characters' reasoning, decision-making, emotions, manners, etc. The core of the formal approach is described in "Computer Models of Constitutive Social Practice":
I haven't had a chance to read "The AI Architecture of Versu" yet, but seems to describe the practical details of the development tools for Versu, focusing around Praxis:
I expect almost anything that makes sense for Versu AI authoring will probably be possible to achieve in Praxis thanks to its highly logic-influenced design, and that the most frequently needed things will be ergonomic for writers thanks to Prompter.
I'm not sure how much these tools are public yet, and actual playable Versu stories have only been built for iOS, but it'll be nice to see this improve. Linden Labs had actually shut down the Versu project earlier this year, and this version of the Versu website was just opened up this week, so the project seems to be in a trajectory toward more openness.