Arc Forumnew | comments | leaders | submit | almkglor's commentslogin
2 points by almkglor 6404 days ago | link | parent | on: arc2c update

Some more things:

1. Continuation guards. Should we implement these? The current Arc prevents continuations from a different thread to be executed, as well as protecting continuations from outside 'on-err from being executed within the context of 'on-err.

This is reasonably easy to implement: just have a global "current continuation guard number". Newly created continuations are given that number. If a continuation is called, the current continuation guard number should equal that continuation's guard number. Otherwise we have a call to a continuation outside the guard.

'on-err and 'new-thread would then wrap the function call around something like:

  ; ccgn == current continuation guard number.
  (let foo (%ccgn)
    (%new-ccgn)
    (f)
    (%set-ccgn foo))
(Again, this is an argument for subtyping closures into continuation and non-continuation functions)

Of course this does have the overhead that whenever continuations are called, we have to do the comparison, and call the current error handler.

2. Error handling. Possibly we could just add to lib-ac.scm.arc:

  (set $default-err
       (fn (e)
         (%pr "Error: ")
         (%prn e)
         (%backtrace)
         (%halt)))
  ; TODO: make this a thread-local in the future!
  (set $curr-err
       (%cons $default-err nil))
  (set err
       (fn (e)
         ((%car $curr-err) e)))
  (set on-err
       (fn (ef f)
         ; <insert continuation guard handling code here>
         (set $curr-err (%cons ef $curr-err))
         (f)
         ; <insert continuation guard handling code here>
         (set $curr-err (%cdr $curr-err))))
Of course, there's the slight problem of how to handle errors reported by the primitives themselves, as well as the runtime system (such as continuation guards being violated, or doing (car #\newline)). We should call the current 'err error handler.

Also, in canonical Arc, the value received by an 'on-err error handler is an opaque "exn:fail" structure, which Arc cannot manipulate. Should we also emulate this structure?

-----

1 point by sacado 6402 days ago | link

1- is interesting and important, thus it should eventually be done, but I don't think this is a priority right now.

2- is a good starting point. For exn:fail, that the same as for 1- : not a top priority, particularily as it is not yet implemented in canonical arc.

I'll work on error handling on the next days, as I'll have a little more time than last week(s).

-----


None yet.

We could add that too. Hmm. Suggestions?

On Anarki, libraries are supposed to be in the lib/ directory, so you access them by for example '(require "lib/defpat.arc")

As for file organization: my arc directory is a mess ^^.

-----

1 point by akkartik 6404 days ago | link

:) Thanks A.

Not just your directory, the repo is in a mess. I left arc a vibrant cottage industry a month ago, and have returned to a ghost town, it seems :( Can you answer another question of mine?

For a time you could call apply on macros. That ability seems to have fallen off since the support for the call* table. I noticed you tried to add it after, but nex3 reverted the change. I've been struggling to create an entry for macros in call using just two args, without success. It seems the only way is to add an ugly special case into ar-apply. Have you thought about this at all?

Heh, let me know if I'm making no sense, and I'll include commit ids, etc.

-----

2 points by almkglor 6404 days ago | link

Alternatively, you could do Arc-side:

  (apply (rep the-macro) arguments)
^^

Everyone else has disappeared! T.T

-----

1 point by akkartik 6404 days ago | link

:) We're in a post-apocalyptic tale. Woot! I've always wanted to be in a post-apocalyptic tale.

I'm at the head of anarki. Any help with this session appreciated.

  ;; I want to play with parser combinators.
  arc> (load "lib/parsecomb.arc")
  nil
  ;; Example from parsecomb
  arc> (= bin-digit    (one-of "01")
     binary (seq (char "#") (char "b") (many1 bin-digit)))
  #<procedure: binary>
  arc> (binary "#b01" 0)
  Error: "Function call on inappropriate object #3(tagged mac #<procedure>) ((char \"#\" . nil))"

  ;; Ok, mapdelay isn't working. Let's try something simpler.
  arc> (mac foo (a b) `(+ ,a ,b))
  #3(tagged mac #<procedure>)
  arc> (apply foo '(3 4))
  Error: "Function call on inappropriate object #3(tagged mac #<procedure>) (3 4)"
  arc> (apply (rep foo) '(3 4))
  (+ 3 4)
  arc> (eval (apply (rep foo) '(3 4)))
  7
  ;; But that's ugly. Is there a better way?

-----

1 point by almkglor 6404 days ago | link

parsecomb.arc has not been maintained. The current parser combinator library that is maintained is raymyers's lib/treeparse.arc , but you have to coerce strings to parse into 'cons cells.

For that matter the fix in lib/parsecomb.arc should probably be to mapdelay:

  (mac mapdelay (lst)
    `(map [eval:apply (rep delay) (list _)] ',lst))

-----

1 point by akkartik 6404 days ago | link

Yeah, that was the first thing I was going to try, then I got sidetracked :)

Can you elaborate on how I can use treeparse for more generalized parsing? Like if I just have a file with flat text and want to generate a list of strings separated by empty lines?

-----

2 points by almkglor 6403 days ago | link

treeparse is very generalized, so expect to write a lot of code.

As for files: the lib/scanner.arc library allows you to treat input streams (e.g. input files) as if they were a list of characters, which is what treeparse accepts (treeparse can work with "real" lists and scanners).

Treeparse usually returns a list of characters, but this behavior can be modified by using a 'filt parser.

Just off the top of my head (untested!):

  (require "lib/scanner.arc")
  (require "lib/treeparse.arc") ; could use cps_treeparse.arc
  (let (list-str newline line oneline lastline allparse) nil
    ; converts a list of chars to a string
    (= list-str
       [string _])
    ; support \r\n, \n\r, \n, and \r
    (= newline
       ; alt & seq's semantics should be obvious
       (alt
         (seq #\return #\newline)
         (seq #\newline #\return)
         #\newline
         #\return))
    (= line
       ; lots of characters that could be anything...
       ; but *not* newlines!
       (many (anything-but newline)))
    (= oneline
       (seq line
            newline)))
    ; in case the last line doesn't end in newline
    (= lastline
       line)
    (= allparse
       (seq
         ; many is greedy, but will still pass
         ; if there are none
         (many
           ; the filter function has to return a list
           (filt [list (list-str _)]
             oneline))
         ; might not exist; will still pass whether
         ; it exists or not
         (maybe
           (filt [list (list-str _)]
             lastline))))
    (def lines-from-scanner (s)
      (parse allparse s)))
  (def lines-from-file (f)
    (w/infile s f
      (lines-from-scanner (scanner-input s))))
p.s. performance may be slow. cps_treeparse.arc is a more optimized version (maybe 25% faster on average, which most of the speedup in many and seq IIRC, which could be twice the speed), but does not support the "semantics" feature.

-----

1 point by almkglor 6403 days ago | link

More on using scanners and treeparse:

http://arclanguage.com/item?id=5227

As an aside, Arkani (wiki-arc.arc, not to be confused with Anarki, arc-wiki.git) is now called Arki

-----

1 point by akkartik 6403 days ago | link

Thanks a bunch, Alan. I've been reading up. I'll be around :)

-----

3 points by almkglor 6403 days ago | link

If you're curious about the CPS variant of treeparse:

http://arclanguage.com/item?id=5321

Achtung! The CPS variant is written in, of all things, continuation passing style, which is hard on the eyes. It is thus expected to be much harder to read than the straightforward treeparse.

Achtung! In a single Arc session, it is safe to use only treeparse or cps_treeparse. Never load both in the same session.

Achtung! Some of treeparse's features are not supported by cps_treeparse.

-----

1 point by almkglor 6403 days ago | link

Note also that for simple parsing needs, using scanners may be "good enough". For example:

  (require "lib/scanner.arc")
  (def lines-from-scanner (s)
    (drain
      (when s
        (tostring
          ; doesn't handle Mac text files though...
          (while (aand (car s) (if (isnt it #\newline)
                                   (write it)))
            (zap cdr s))))))
  (def lines-from-file (f)
    (w/infile s f
      (lines-from-scanner (scanner-input s))))

-----

2 points by akkartik 6395 days ago | link

It seems like:

  (zap cdr s)
doesn't work when s is a scanner -- s remains unchanged. I looked at the code and convinced myself that macros like zap that call setforms won't work for new types. Does that make sense?

-----

2 points by almkglor 6394 days ago | link

  arc> (require "lib/scanner.arc")
  nil
  arc> (= s (scanner-string "asdf"))
  #3(tagged scanner (#<procedure> . #<procedure>))
  arc> (car s)
  #\a
  arc> (zap cdr s)
  #3(tagged scanner (#<procedure> . #<procedure>))
  arc> (car s)
  #\s

-----

1 point by akkartik 6399 days ago | link

Hmm, just saw this. Thanks for the tip.

I'll work on this some more this weekend, and hopefully have something cogent to say.

-----

6 points by almkglor 6404 days ago | link | parent | on: Programming in a vacuum

We still need libraries, right?

String processing libraries especially, also database libraries, distributed message passing libraries, etc.

Everyone! Come! Let us go forth and write more Arc libraries, document them real good, and push them on Anarki!

-----

4 points by kens 6403 days ago | link

Writing libraries is a Good Thing, but trying to create a large collection of libraries for Arc strikes me as a bit of a hopeless cause. After all, Lisp hasn't been able to reach a "critical mass" of libraries and it has many, many more people involved.

I think Arc would be much better off with a way to leverage existing libraries. Easiest would be to use MzScheme's libraries, but that set of libraries is somewhat limited. (I'm currently experimenting with using MzScheme's OpenGL library from Arc; the object-oriented stuff is a big pain since it doesn't play well with Arc.)

Alternatively, if Arc had a way to access the libraries from, say, Python, .Net, or Java, it would gain access to a huge library base. A couple big problems are how to map the language datatypes between the languages, and how to get the runtimes working together.

-----

3 points by almkglor 6402 days ago | link

Maybe make an arc2jvm? Perhaps even arc2java, I'm sure a subset of Java can be made sufficiently C-like to compile down to (and we even get GC for free).

> A couple big problems are how to map the language datatypes between the languages

Bingo

-----

1 point by etal 6399 days ago | link

Looks like this is the official way to put another language on top of Java:

https://scripting.dev.java.net/

(Note their subtle language that any higher-level language running on the JVM is a "scripting" language.)

I haven't worked with it, but if it handles the mapping of Javascript's type system onto the JVM, then maybe it will do the same for Arc.

-----

1 point by almkglor 6399 days ago | link

Does it allow "script" code to be compiled down to java or jvm bytecode, or is it strictly an interpreter for the scripting language?

-----

1 point by etal 6399 days ago | link

I'm miles out of my league here, but in the interest of science I grabbed the spec, JSR-223. Here's the juice:

Introduction:

  The original goal of JSR-223 was to define a standard, portable way to
  allow programs written in scripting languages to generate web content. In
  order to do this, it is necessary to have a common set of programming
  interfaces that can be used to execute scripts in scripting engines and
  bind application objects into the namespaces of the scripts. Therefore, in
  addition to a framework for web scripting, the specification includes a
  standardized Scripting API similar to the Bean Scripting Framework. It uses
  the Scripting API to define the elements of the Web Scripting Framework.

  [...]

  There are several areas which are intentionally omitted from the
  specification:

  - The specification does not define how scripting languages should enable
    the use of Java objects in scripts, although it is assumed that the
    scripting languages implementing the specification have this
    functionality.

  - The specification does not distinguish between scripting implementations
    that compile script sources to Java bytecode and those that do not.
    Script engines that do can be used to implement the specification, but it
    is not required.

  - The specification makes no requirements of scripting languages or the
    syntax uses to invoke the methods of Java objects in the languages.

Overview:

  In this specification, a scripting engine is a software component that
  executes programs written in some scripting language. The execution is
  generally performed by an interpreter. Conceptually an interpreter consists
  of two parts: a front-end which parses the source code and produces an
  internal representation of the program known as intermediate code, and a
  back-end which uses the intermediate code to execute the program.

  The back-end of the interpreter, also known as the executor, uses symbol
  tables to store the values of variables in the scripts.

  [...]

  Scripting engines which implement the fundamental scripting interface
  defined in this specification are known as Java Script l20 Engines.
  Conceptually, a Java Script Engine can be thought of as an interpreter, but
  this may not actually be the case. For instance scripts executed by a
  single Java Script Engine may be executed internally by different
  interpreters.

Technologies:

  - Java Language Bindings – Mechanisms that allow scripts to load Java
    classes, create instances of them and call methods of the resulting
    objects.

  - General Scripting API – Interfaces and classes that allow script engines
    to be used as components in Java applications.

  The specification does not deal with issues of scripting language design or
  interpreter implementation.

So, it looks like the way you interpret, compile and execute the code is your own business, but if your own ScriptEngine implementation matches the specified API, it will work with existing Java tools and frameworks, particularly for the web. It's modeled after Rhino, so some parts of the Rhino back-end might be directly reusable.

-----

3 points by absz 6402 days ago | link

This is a good point. There is a C interface, and C has innumerable libraries, but working with C can be… icky.

-----

4 points by stefano 6404 days ago | link

What about a TODO list of needed libraries? Everyone could peek one and develop it. I'm currently working on a client side http library.

-----

3 points by almkglor 6402 days ago | link

TODO:

  A reasonably quick substring-matching library
  Database

-----

2 points by absz 6404 days ago | link

Hear hear! Let there be libraries! The school year's almost over, and I'll contribute more then. And I second the idea of a list of necessary libraries that stefano proposed.

Also, has anyone else found themselves accumulating a file of utility functions? I have one with a little fewer than 30 functions which I find generally useful. There's probably some duplication of standard stuff, but there are also things that aren't. If other people have these, we might put the common functions on Anarki.

-----

2 points by almkglor 6404 days ago | link

I suggest that such simple routines be pushed onto Anarki then.

As an aside: it would probably be a good idea to start adding docstrings to everything in Anarki; this would help catch duplication of effort.

-----

3 points by stefano 6403 days ago | link

What about automatically collecting every function/documentation pair and putting everything on the Anarki wiki?

-----

1 point by almkglor 6402 days ago | link

Err, I don't understand exactly what you mean, can you expand on this?

-----

1 point by stefano 6401 days ago | link

I mean scanning Arc files or, better, the help* table and gather information in text files formatted in a wiki-friendly format in order to easily put them on the Anarki wiki.

Edit: have a look at the file lib/help-to-wiki in Anarki (just pushed it).

-----

1 point by almkglor 6400 days ago | link

Arki doesn't support tables yet.

Also, Arki supports <arc></arc> tags, which adds the help* table entries as the mouse hover strings on symbols.

-----

1 point by almkglor 6405 days ago | link | parent | on: arc telnet

Might be better:

  (xdef 'tcp-connect
        (lambda (host port . rest)
          (call-with-values
            (lambda () (apply tcp-connect host port rest))
            (lambda (x y)
              (cons x (cons y 'nil))))))

-----

1 point by absz 6405 days ago | link

Ouch, I hadn't thought of that. But I just tested this, and it appears that ac.scm runs the translation step after the xdefed functions return, so we get a valid Arc list back. Saved by the bell, so to speak :)

-----

1 point by almkglor 6405 days ago | link | parent | on: arc telnet

Why do you assign 'cons to 'inout?

Anyway I suggest you push it on Anarki^^

-----

1 point by bOR_ 6405 days ago | link

Why? because there isn't that much in lisp/scheme/arc that comes naturally to me :).

Not sure about how to go about pushing something on something else. Never been in an environment where we worked with cvs or git or anything. Solitary scientists :)

-----

3 points by almkglor 6405 days ago | link | parent | on: Arc on the command-line

1. This will probably be done in arc2c eventually. Shouldn't be too difficult either.

2. On Anarki the variable 'current-load-file* does this. However the current solution is not thread-safe and having two threads perform 'load at the same time will fail. This means that 'current-load-file* might be changed to a 'thread-local, meaning its contents will be accessed via '(current-load-file* ) - currently it's just 'current-load-file*

3. Someone has to modify infile to do this seamlessly.

-----


For threads: Anarki has thread-local variables:

  (= call-stack* (thread-local))
  (= (call-stack*) (list))

  (let old-fn fn
    (mac fn (args . body)
      (w/uniq (name ret)
        `(,old-fn ,args
            (push (list ,name ,body) (call-stack*))
             (let ,ret (do ,@body)
              (pop  (call-stack*))
               ,ret)))))
Note the extra layer of parenthesization.

-----


1. Yes. This is actually good. You don't want a 1000-iteration loop cluttering a backtrace do you?

Without TCO:

  in function gs42
  in function gs42
  in function gs42
  in function gs42
  in function gs42
  in function gs42
  in function gs42
  in function gs42
  in function gs42
  in function gs42
  in function gs42
  in function gs42
  in function gs42
  in function gs42
  in function gs42
     ....985 more times....
  in function foo
  in function bar
  from top level
With TCO:

  in function gs42
  in function foo
  in function bar
  from top level
Less is more?

That said, if you're implementing a state machine using tail-called functions, you lose the trace of the state and can only get the current state (arguably a good thing too - you don't want to have to hack through 600 state transitions, either). You'll probably have to dump the state on-screen or on-disk somehow instead.

2. Yes, this is difficult. IIRC some Schemes actually internally use an abstract syntax tree (not lists) and make macros work on that tree, not on lists. The abstract syntax tree includes the file and line number information.

The problem here is: what do you end up debugging, the macro or the code that uses the macro? If the macro-expansion is a complicated expression, then the bug might actually be in the macro, with the original code quite correct.

In arc2c the file and line number info are lost pretty early. Maybe we could use an AST direct from the file read-in, and silently fool macros into thinking that the AST is a list.

-----


None yet. You could hack on ac.scm on Anarki to add the function to get the current thread ID.

-----


Nope. After all, pg made Arc for hackers, who never make mistakes, and can write an Arc interpreter on bare metal by flipping switches in time to the processor's 100MHz bus clock.

This sounds like a good thing to add to the arc2c compiler anyway ^^. Certainly we could get a backtrace reasonably easily by actually inspecting the closure structures being passed as continuations, but we also have to determine a decent function-to-symbol mapping.

-----

4 points by bOR_ 6406 days ago | link

it would be appreciated! :P. Functional programming helps because usually it is the last thing you wrote that is wrong, but occasionally I'm completely puzzled by error messages (only to find out that one of my comments started with a : rather than a ;.

-----

4 points by almkglor 6406 days ago | link

Hmm. Anyway it looks like it might be useful to subtype function closures into continuation and non-continuation functions (as an aside it would probably be useful also for optimizations: when a continuation function exits, it can't be called and its closure can be immediately freed or reused, unless we use 'ccc: and even so we could just copy the continuation into a non-continuation closure).

Then when a backtrace is requested we simply scan through the stack for continuation-type functions, and scan through their closures for continuation-types, and so on until we end up on a closure without any continuation-type functions.

-----

2 points by stefano 6406 days ago | link

While scanning the stack you have to pay attention to not include functional arguments as if they were called functions. To give descriptive names to functions I would transform every lambda expression in a named function, e.g. :

  (def f (x) x) --> (set f (fn (x) x)) --> (set f (__named '(f x) (x) x))
and for anonymous functions:

  (fn (a b) (+ a b)) --> (__named '(fn (a b)) (a b) (+ a b))

-----

1 point by almkglor 6406 days ago | link

> While scanning the stack you have to pay attention to not include functional arguments as if they were called functions.

Which is why I was proposing to subtype closures into continuations and non-continuations. Normal functions that are passed around are non-continuations, while continuation closures are created during CPS-conversion. Of course we probably need to add code in 'ccc which would probably copy a continuation closure into a non-continuation version of the closure.

-----

More