Huh. Probably will also require some hackery on the git-side too, because it would be probably revertible and thus the modified versions can still be accessed. LOL.
Much of the base system of Arc expects some Unixisms. In fact, much of the base system of PG's original ArcN expects a mostly BSDisms, and it's really the Anarki fork which supports mostly Linux.
As an example, much of the code assumes that the path separator character is "/", but I gather from mzscheme documentation that Mac OS uses ":". Also, bits and pieces of the Arc system use /dev/urandom.
So mostly I suspect the problems you encounter(ed) are from Arc's assumption of Unix-likeness which might not be available on OS X (not sure however; I don't have access to a real Mac)
Edit: Oh and yes: the first thing you want to do is switch to Anarki, which is more likely to work on a random, non-PG system.
OS X is based on BSD. As for the path thing, wow, that threw me. Not sure what that might apply to. Here's are my PATH entries on a SuSE box and my Macbook, respectively. They're both POSIX compliant.
I guess, for starters, I need to figure out how to set up BIND to route news.localhost to a local directory, and figure out which directories the various bits of MzScheme and Arc should be applied to. My biggest frustation right now is figuring out the syntax for BIND.
Windows internally implicitly supports / as an alias of \.
Note that Arc provides its own http-server which is not very directory-based (rather, something like http://example.com/login would be defined with a (defop login ....) form in the running Arc process), so I'm not sure why you're routing to a directory.
The deal with path separators has to do with the difference between the classic Mac OS (9 and lower) and Mac OS X. Classic Macs used their own path system, which used :s as the path separator (e.g., ":Macintosh HD:Documents:Arc:tut.txt"). When the switch to OS X came, suddenly everything was based on Unix, so file paths became sane (e.g., "/Users/username/Documents/Arc/tut.txt" or even "~/Documents/Arc/tut.txt"). Nevertheless, in the name of backwards compatibility, many things still display paths in the old format, the GUI pretends that :s in file names are actually /s, and AppleScript deals natively with old-style filenames.
Re Anarki: yes, I've found arc2 doesn't work out of the box on CentOS 5, and my repos are botched (something to do with installing PHP 5 during initial install, I think, was what forced me to change something in the repos), so I need to fix the repos to install Git so then I can install Anarki.
,(or default-method
`(err "no matching method found for multimethod:" ',name)))
Is this deliberate? It seems to me that the optional default-method argument is a default expression that will be evaluated, but without access to the arguments to the function.
I'm not sure exactly what you're getting at. Admittedly, what I did there is somewhat hackish (although my original method was worse). The default-method argument is just that: a default method which will be called (with the arguments to the multimethod) if no other method is found. But the expression that makes it up is only evaluated if necessary (lazily, in an 'or expression). So if it produces an error, then when that expression is evaluated, that is, if and only if we have failed to find a matching method, it produces an error; and since, in the macro itself, I know the name of the multimethod, I can substitute this into the default for 'default-method, and have nicer error messages.
Hmm. Would you mind giving a more overviewish summary of the library-packing system? You seem to have stuff like "defproject" in pack.arc ; what are the relationships between projects, libraries, and packages in your scheme?
Currently the (using ...) scheme in Arc-F assumes a package is a file is a library (and if the library is too large to fit in a single file, to have various "support packages" which are related to the main library package: i.e. lib.arc source contains '(using <lib/part1>v1), and there's a file in lib/part1.arc which contains the parts of the library.). I'm interested in your take on multi-file libraries.
Also, my scheme assumes one context object per source file. A context object is equivalent to the "current-package" variable ( s/"/* ) in CL, except you can arbitrarily create such objects, and they work monadically. What is your expectation on how the packaged libraries work?
> if the library is too large to fit in a single file, to have various "support packages" which are related to the main library package
No more. For simplicity now everything (even single files) are packaged in directories. A "library" (or "package") is a way to distribute some piece of code: everything is placed within a directory named libname.pack together with informations to load dependencies. You can load such a package and all its dependencies using a single command ('use, 'require or 'using). A "project" is a way to manage the development of such a library and is roughly a "make" for Arc. To define a project you would use the macro 'defproject, specifying a list of dependencies (single files or libraries) and a list of files composing the project. When loading a project through 'proj-load only modified files are loaded. When you have loaded a project you can build a packaged library using 'deliver-library. A proper directory will be created and populated with the relevant files. Project and libraries are independent of the namespace system. I've modified 'using to load a library using 'use if nothing else can be done, with the assumption that if such a library exists, it also defines its own namespace (e.g. (using <http-get>v1) loads the library named 'http-get, hoping that that library defines a namespace <http-get> with an interface v1.
In the Arc-F packages/namespaces, dependencies are specified by the (using ...) metaform. Each package is conceptually a point where a library could be; if a library needs several files, each file is a package and there is a unifying file which depends on the other packages and specifies the interface.
I suppose my view of namespace-as-package is from the point of view that namespaces are the be-all and end-all of organizing libraries though. Hmm. I'll try a look-see at your pack.arc, although I think it would be nice if you could provide a few simple examples.
> dependencies are specified by the (using ...) metaform
Mmmm... I've been thinking about this. There seems to be some overlapping between your system and mine. I think pack.arc is better suited to extend a pure namespace system with a packaging/dependency system, whereas arc-f namespace system is meant to manage also dependencies.
To see a real usage of pack.arc you can look at my ftp library: http://github.com/stefano/ftp-client/tree/master It's not updated to work with arc-f (yet) but it shows how to use pack.arc: a proj.arc file for development and a automatically generated directory ftp-client.pack intended to be downloaded and copied within the search path by the end user.
Yes. In fact I kind of rushed Arc-F a bit because I suspected that your pack.arc would overlap with the packages system in Arc-F, so I wanted to see what we could work out in order to reduce overlapping in this case.
Hmm. Project management? The Arc-F namespace system doesn't handle thinking in terms of "projects". And how about versioning? The "version" that is used in the Arc-F namespaces is more about the interface version, not necessarily the version of the actual library.
Also, I was also thinking that potentially a particular library may have multiple implementations, while those implementations share the same interface. For example, there is a reference implementation for vectors in lib/vector.arc, and (using <vector>v1) will acquire that vector interface. However, a different implementation of Arc-F - for example, arc2c - might provide a different version - in the case of arc2c, it might be a thin wrapper around a C array.
I will probably add it to pack.arc in the future. For the moment it wouldn't be very useful, since there are so few libraries and the ones that do exist are in early development stage. I'll put in pack.arc what I need now to help me develop and distribute libraries. Suggestions are always welcome of course.
The main overlapping between the two system seems to be the fact that 'using tries to load a file before importing its interface. I don't think this will create any conflict with pack.arc. I should also change its name: the term "package" is used both for "collection of files" (pack.arc) and "namespace" (arc-f).
Your example about arc2c arises a problem that Arc, in its original conception, wanted to solve: multiple implementations. Small little differences between implementations always end up hurting: for example ftp-client works with Anarki and doesn't work for Arc2 or Arc-F. There are too much implementation of Arc right now. I have nothing against arc-f, snap, arc2c, rainbow,... I like their existence and what they added (in particular arc-f), but a canonical implementation used by 99% of Arc developers should exist, and it should be "fast enough" (2x slower than python is my personal limit).
How about project-based development then? Basically to keep related files together.
Personally I prefer a variety of smaller libraries whose components would then be composed by other libraries (which would end up being small too, because the functionality exists in other libraries).
My main design goal in Arc-F is to make the use of libraries - and in particular, the use of different libraries from different people with different design philosophies - as smooth as possible. Many of the additions in Arc-F (the ones that aren't packages) are actually subtly biased towards that main goal.
> (2x slower than python is my personal limit).
Hehehe. Looks like I'll need to start doing some teh lutimate leet hackage in the function dispatching code... Or alternatively start considering how to write an interpreter from scratch (which is a subgoal of SNAP, too) ^^
I don't quite understand that. With pack.arc development is project-based. Maybe we have different opinions of what a "project" is. To me, it is a directory with a file proj.arc describing the structure of the project (a 'defproject declaration).
> write an interpreter from scratch
Really difficult but really needed. The mzscheme dependency is quite big compared to how small as a language Arc is. The main efficency problem, as you said, is function dispatch, because we have to check if it is a function, a list, etc. One thing I don't like very much about SNAP is the dependency on the boost libraries: it is a huge dependency. Is it really needed?
Another problem with an interpreter from scratch is the GC: it is very difficult and time consuming to write an efficient, concurrent and stable GC. A good solution would be to use the Boehm-Weiser GC: it is easy to integrate in any interpreter (I don't know if it works with SNAP's process' model, though) and it is a really good GC. Even the mono project and gcj use it.
Ah, right. Of course, that's why there's 'defproject, right?
> because we have to check if it is a function, a list, etc
As an idea: generally writes to global variables are much rarer than reads from global variables; in fact, practically speaking nearly every global variable is going to be a constant. We could move the cost of checking if a call is a function, a fake arc-f function, or a data structure to the writing of global variables rather than the read.
Basically calls where the expression in function position is a reference to a global variable are transformed to callsites which monitor that global. The callsite initially determines the type of the value in the global (or creates an error-throwing lambda if the global is still unbound) and determines the proper function to perform for that call (normal function call, or a list lookup, or a table lookup, etc). The callsite also registers its presence to the global.
If the global is written, the global also notifies all living callsites (we thus need weak references for this), which will then update themselves with the new value.
This is actually "for-free" in SNAP, because there's an overhead in reading globals (copying from the global memory-space to the process memory-space), and SNAP thus needs to monitor writes to globals so it can cache reads.
> One thing I don't like very much about SNAP is the dependency on the boost libraries: it is a huge dependency. Is it really needed?
The bits of boost I've used so far are mostly the really, really good smart pointers; while I've built toy smart pointer classes I'm not sure I'd want those toys in a serious project. Also, I intend to use boost for portable mutexes. Now if only boost had decent portable asynchronous I/O...
Alternatively we could wait a bit for C++0x, which will have decent smart pointers which I believe are based on boost.
> Boehm-Weiser GC: it is easy to integrate in any interpreter (I don't know if it works with SNAP's process' model, though)
Well, one advantage of the process-local model is that process-local memory allocations won't get any additional overhead when the interpreter is multithreaded; AFAIK any malloc() drop-in replacement either needs to be protected by locks in a multithreaded environment, or will do some sort of internal synchronization anyway. In effect we have one memory pool per process, allocating large amounts of memory from the system and splitting it up according to then needs of the process.
Since processes aren't supposed to refer to other process's memory, the Boehm-Weiser GC won't have anything to actually trace across allocated memory areas anyway.
And I probably should start using tagged pointers instead of ordinary pointers ^^. They're even implementable as a C++ class wrapping a union.
In any case a copying algorithm already exists because we need to copy messages across processes anyway: minor changes are necessary to extend it to a copying GC.
Arc-f actually has clojure-style multimethods hidden somewhere (I invite you all to search for them!). However, there are very deep reasons why I did not encourage their use.
The most important is that the dispatcher function is a limiting factor. To be specific, the dispatcher function means that extending the function is limited to what the dispatcher expects.
If the dispatcher function expects exactly one argument, you cannot create a method with an optional argument. For instance, consider a shape rectangular-prism
(= shape
(obj shape 'rectangular-prism
height 2
width 3
depth 4))
(method area 'rectangular-prism (p (o face 'front))
(case face
front (* p!width p!height)
back (* p!width p!height)
left (* p!height p!depth)
right (* p!height p!depth)
top (* p!width p!depth)
bottom (* p!width p!depth)))
Unfortunately, because the dispatcher for 'area expects exactly one argument, you cannot extend 'area with a method that supports optional arguments.
I'll be putting up a more extensive rationale for why I decided not to use Clojure-style multimethods in Arc-F (despite already having implemented them), and would very much rather prefer to bash my head trying to implement CLOS-style multimethods.
It's true that if the dispatcher expects exactly one argument, you can't add optional arguments. The solution is very simple: don't make dispatchers which expect exactly n arguments. Make all your dispatchers take rest parameters which they ignore. In fact, 'multi-keyed already does exactly that, so if you used (multi-keyed area 'shape), your example would work exactly as expected. It's not a hard fix.
Regarding reasons for not implementing Clojure-style multimethods in Arc-F, how about this one: because you can implement them in plain old arc! Don't put stuff in Arc-F if it can be done in Arc or Anarki already. Interoperability is good - we don't need to fork the community as well as the language.
It's not just the number of arguments. One advantage of CLOS-style multimethods is this:
(def bam (a b)
(err "collision type unknown!"))
(defm bam ((t a ship) b)
(destroy ship)
(destroy b))
(defm bam ((t a ship) (t b missile))
(destroy ship)
(destroy missile)
(add-points missile!source))
Because of the computation of keys, you can't exactly implement the above using clojure-style multimethods without tricks like method bouncing.
Like I said: already implemented clojure-style multimethods. And tried it. So yes: I'll continue bashing my head implementing CLOS-style multimethods, because while clojure-style multimethods are cute, they're not good enough for all cases. Arguably neither are CLOS-style multimethods, but at least we have an alternative choice.
Edit:
Also, there's a good reason for implementing this in the scheme-side: efficiency. Your implementation of clojure-multimethods allocates a cons cell for each argument to the multimethod. The scheme-side implementation does not, because on the scheme-side I have access to the ar-funcall* functions.
Efficiency of course is not a concern, except when it is.
As an aside, there are several bits of Arc-F that look like they're implemented in Arc, but are actually implemented in the scheme-side. There's an Arc implementation of them in arc.arc, which is labeled as "IDEAL", while the actual binding to the scheme side is labeled as "REAL". For example, the basic compose function is actually implemented in the scheme-side, but there's a reference Arc implementation in arc.arc marked "IDEAL". The only reason they're on the scheme-side is due to efficiency. Ideally, they would be in Arc: realistically, they are better implemented in the base system.
This is true, and indeed I mentioned it in the OP; you can't do type-based dispatch with clojure-style multimethods except on exact matches. You also can't get method chaining. But clojure-style has the advantage of being damn simple, and easily permits dispatch based on non-type-based conditions. CLOS style has the advantage of being more flexible about dispatch and integrating well with OO methodologies.
I'm not trying to convince you that CLOS multimethods are bad or not to implement them; a full implementation for Arc or Arc-F would be _very cool_. CLOS is without a doubt my favorite thing about Common Lisp. But Clojure-style multimethods are not "cute" or useless. They're just not a universal panacea. Very little is.
For me, cute means something really really nice, not necessarily useless. Like cute mature women, for example. Or better: cute girls, with guns. LOL.
Method chaining may require us to rethink PG's type system, at least if we want to handle a drop-down to a more generic type (which arguably is the more interesting bit about chaining). It's reasonably easy to drop from the "argument 2 matched type T" to "argument 2 matched no types", but how about when we want to drop from "argument 2 matched derived type D" to "argument 2 matched base type B"?
Waa.
We would have to have an operator which determines if D is derived from some random type B, and forcibly standardizing on it. This is going to make my head explode.
Specifically, arc.sh sets the environment variable "arc_dir" to the installation directory.
I would suggest making a batch file which sets the environment variable arc_dir properly before launching mzscheme -af as.scm ^^
Should probably be done correctly by providing an arc.bat I suppose, although the problem is always figuring out the installation directory... haven't hacked MSDOS batch files in a long time ^^
C:\User\Programming\Arc\arc3f\arc-f>set arc_dir=C:\User\Programming\Arc\arc3f\arc-f
C:\User\Programming\Arc\arc3f\arc-f>mzscheme -mf as.scm
Compiling arc.arc...
Use (quit) to quit, (tl) to return here after an interrupt.
<User>tl:
Unfortunately, (having learned Unix shell scripting), I never bothered to learn Windows batch files.
Also of note, arc.sh doesn't work in Cygwin:
$ sh arc.sh
> default-load-handler: cannot open input file: "c:/cygdrive/c/User/Programming/Arc/arc3f/arc-f/as.scm" (The system cannot find the path specified.; errno=3)
I pushed an untested arc.bat launcher on the anarki arc-f recently, although now that I've reviewed it it seems I used the wrong flag in the mzscheme invocation (-af instead of -mf). Could I ask you to check it out, and if so, could you check it out?
As for the cygwin stuff.... hmm. Maybe I should fix my broken WinXP machine...
@echo off
set arc_dir=C:\Program Files\ARCF
mzscheme -mf "%arc_dir%\as.scm"
This one worked for me. I'm not sure if removing the [][]'s from the file and making it windows line breaks mattered, but at least I had to move some ""'s around to get it to work.
Now just trying to get the launch-an-arc-script script working (from outside the repl). The script that is elsewhere on the website works on linux, but not windows ;). Damn pipes.
Out of curiousity: does it work even if you're on a different hard drive/directory as the installation drive/directory? The intent of that batch file is to allow you to launch Arc from anywhere, while still (1) able to access the current directory and (2) able to load library files from the arc installation directory
E:\> copy con tmp.arc
(prn "hello world!")
^Z <---- that's a control-Z
E:\> "C:\User\Programming\Arc\arc3f\arc-f\arc.bat"
Use (quit) to quit, (tl) to return here after an interrupt.
<User>tl: (using <files>v1)
t
<User>tl: (ls)
("tmp.arc") <--- you should get a list of files and stuff in the current directory, including tmp.arc
<User>tl: (load "tmp.arc")
hello world!
nil
<User>tl:
Note that this is currently bugged in Arc-F though. Will fix ^^.
The auto-gensyms thing looks cute. It might be possible to hack something like that, although it would require modifying the axiomatic quasiquote operator.
It seems partly a problem with srv.arc, it seems it doesn't correctly use \r\n, only \n ~.~;
See in srv.arc:
(def header ((o type textmime*) (o code 200))
(string "HTTP/1.0 " code " " (statuscodes* code) "
" serverheader* "
Content-Type: " type "
Connection: close"))
Inspecting the file, they seem to be \n's only, not \r\n
A second problem lies in 'readline:
(def readline ((o str (stdin)))
" Reads a string terminated by a newline from the stream `str'. "
(awhen (readc str)
(tostring
(writec it)
(whiler c (readc str) #\newline
(writec c)))))
It has to do with the fact that it reads in a character, BUT DOESN'T CHECK THAT THAT FIRST CHARACTER IS A NEWLINE. The only check done is with the second character read.