notes-computer-jasper-jasperNotes3

http://bartoszmilewski.com/2009/05/21/unique_ptr-how-unique-is-it/ :

some type properties:

i believe the previous is a complete description of milewsky's system, (except for owner polymorphism, which i think can be done in a simple flexible way in Jasper with annotations anyway), but should ask milewsky to be sure. according to http://fpcomplete.com/the-downfall-of-imperative-programming/ , he's since embraced functional programming to avoid data races, but i'm guessing he still thinks the above methodology would do the trick.

in jasper:

additional operation: ':=', called "move": x := y == x = y; y = null;. The idea is that x gets the pointer and y doesn't have it anymore; this can be applied even to unique pointers, whereas normal =, which means copy, cannot.

you can declare a pointer with the type propery (other examples of type properties are , meaning "compiler, i demand that this cannot be mutated" and const, meaning)

his type system apparently provably avoids race condititions (both 'low level' and 'high level' race conditions, whatever that means). it does not prevent deadlocks.

he also says "Such system would be incomplete without some support for lock-free programming. There are very good reasons for the language to enforce sequential consistency. For comparison, C++ committee, after a long struggle, decided to allow non-SC primitives (weak atomics). Java only enforces SC for volatile variables. " then goes on to introduce the lockfree qualifier, which means "A lockfree variable is guaranteed to be always atomically accessed in a sequentially consistent way." He has noted elsewhere that 'low level' race conditions defeat even lockfree algorithms.

not quite sure what this means. i guess sequential consistency is a guarantee that sequential code will always execute sequentially? is he saying this is good and that C++ has gone too far? or is he saying that you only need it when you are writing lockfree algorithms, which is why it seems his language has SC off by default but on when the lockfree qualifier is used? i think the latter. he notes this example which i don't entirely understand:

"

Speaking of lock-free programming, here’s the implementation of the infamous Double-Checked Locking Pattern:

class Singleton<T> { private: lockfree T _value; public: T get() lockfree { if (_value is null) { synchronized(this) { if (_value is null) _value = new T; } return _value; } } }

A lockfree variable is guaranteed to be always atomically accessed in a sequentially consistent way. A lockfree method is not synchronized, but it can only operate on lockfree variables (or use a synchronized section). Even though the use of lockfree doesn’t introduce low-level races, it may create high-level races when accessing more that one lockfree variable at a time. But we all know that lock free programming is only for desperadoes. "

"

Lessons learned from previous work

There are many commonalities in various approaches to race freedom.

    Ownership should be built into the type system. In OO languages each object must have an owner. In C-like languages each piece of data must have an associated lock.
    There should be efficient mechanisms for passing data between threads
        By value
        By reference. (The object being passed must be a monitor.)
        As immutable data, by const reference
        By unique reference using move semantics

Most of those ideas are expressible through type qualifiers. "

other sources: http://bartoszmilewski.com/2009/11/30/unique-objects/

http://bartoszmilewski.com/2009/09/22/ownership-systems-against-data-races/ http://bartoszmilewski.com/2009/06/15/race-free-multithreading-owner-polymorphism/ http://bartoszmilewski.com/2009/06/02/race-free-multithreading-ownership/ http://bartoszmilewski.com/2009/09/22/ownership-systems-against-data-races/

---

 "Andrei insists that "shared" should guarantee the absence of low level data races. Whenever I give examples of how this makes performance unpredictable, I hear the answer: "cast shared away". Currently nobody complains about it because this feature hasn't even been implemented. "

" D1 to be discontinued on December 31, 2012 (after the end of the world) by andralexin programming

[–]DrBartosz? 7 points 8 months ago

I'd look at existing solutions (Chapel, C++AMP, and even Haskell) as a starting point for task-based parallelism and GPU. I like the ownership system and it would be nice to have it in the language, but that's not the highest priority. What I'd like to see is better support for move semantics/uniqueness, which is essential for thread/task safety. I'm also not sure what to think about the "shared" experiment. The idea seemed good at the time, but we could never agree (or I could never agree with the rest of the team) about the details. So now it seems like a half baked idea (essentially, in order to do anything interesting, you have to cast "shared" away). "

much earlier (i am writing this in aug 2012) he talks about http://bartoszmilewski.com/2008/07/30/sharing-in-d/ and http://bartoszmilewski.com/2008/08/11/sharing-and-unsharing-of-data-between-threads/ . if his objections are unchanged since then, the performance objections are:

" The first is performance–not for shared objects, mind you, but for the non-shared ones. Walter tells us that accessing a thread-local static variable adds between 1 to 3 instructions of overhead. That seems quite reasonable. Especially considering that in multi-threaded environment the use of global non-shared variables is rarely correct.

There is also a performance penalty when starting a new thread–all static variables it has access to have to be default-initialized, plus all module constructors have to be called. This might amount to quite a bit. We will recommend not to overuse global variables and module constructors. The way to amortize this cost is to create thread pools. "

another objection seems to be the difficulty of 'unsharing' objections, and of resharing them after unsharing them. this sort of thing is necessary if you want to get a shared object from another thread and then pass it into an unknown library.

also " Note to D programmers: The current semantics of D “shared” is slightly different from my proposal. For instance, it forces all embedded objects to be monitors (their methods must be synchronized by their own lock), requires explicit use of the synchronized keyword, and forces all access in non-synchronized methods to be sequentially consistent. (And it doesn’t guarantee freedom from races.)"

dunno if this is still his latest.


btw the 'promise' vs. 'demand' thing w/r/t const vs. immutable and lent vs. unique (lent should be called noalias or somesuch) seems pretty common (wasn't this also the deal with Haskell's existentially quantified types or maybe with Haskell typeclasses vs. Java interfaces? or were those other things?). should have syntax for it, so you can just say 'i promise it will be immutable' and 'i demand it be immutable' without learning two keywords for each of these cases and remembering which one is which (or at least, have a keyword naming convention like promiseImmutable demandImmutable)

---

toread: http://bartoszmilewski.com/2011/01/05/using-f-sequences-and-pipelines/ http://bartoszmilewski.com/2010/09/11/beyond-locks-software-transactional-memory/

--- http://bartoszmilewski.com/2010/05/11/parallel-programming-with-hints/

---

" The standard concern is that new type qualifiers introduce code duplication. The classic example is the duplication of getters required by the introduction of the const modifier:

class Foo { private Bar _bar; public: Bar get() { return _bar; } const Bar get() const { return _bar; } } "

in Jasper, Jasper should be able to infer that the const-ness holds iff certain of the args passed in are const

---

STM

" Back to shared memory, this time without locks, but with transactional support. STM, or Software Transactional Memory, is a relatively new paradigm that’s been successfully implemented in Haskell, where the type system makes it virtually fool-proof. So far, implementations of STM in other languages haven’t been as successful, mostly because of problems with performance, isolation, and I/O. But that might change in the future–at least that’s the hope. "

" In Chapel, you use the forall statement for loops or begin to start a task. "

" All three HPCS languages chose fine-grained tasks (activities in X10, implicit threads in Fortress), not threads, as their unit of parallel execution. A task may be mapped to a thread by the runtime system but, in general, this is not a strict requirement. Bunches of small tasks may be bundled for execution within a single system thread. "

" To make things more concrete, let’s say you want to distribute a (not so big) 4×8 matrix among currently available locales by splitting it into blocks and mapping each block to a locale. First you want to define a distribution–the prescription of how to distribute a block of data between locales. Here’s the relevant code in Chapel:

const Dist = new dmap(new Block(boundingBox=[1..4, 1..8]));

A Block distribution is created with a bounding rectangle of dimension 4×8. This block is passed as an argument to the constructor of a domain map. If, for instance, the program is run on 8 locales, the block will be mapped into 8 2×2 regions, each assigned to a different locale. Libraries are supposed to provide many different distributions–block distribution being the simplest and the most useful of them.

When you apply the above map to a domain, you get a mapped domain:

var Dom: domain(2) dmapped Dist = [1..4, 1..8];

Here the variable Dom is a 2-D domain (a set of indices) mapped using the distribution Dist that was defined above. Compare this with a regular local domain–a set of indices (i, j), where i is between 1 and 4 (inclusive) and j is between 1 and 8.

var Dom: domain(2) = [1..4, 1..8];

Domains are used, for instance, for iteration. When you iterate over an unmapped domain, all calculations are done within the current locale (possibly in parallel, depending on the type of iteration). But if you do the same over a mapped domain, the calculations will automatically migrate to different locales.

This model of programming is characterized by a very important property: separation of algorithm from implementation. You separately describe implementation details, such as the distribution of your data structure between threads and machines; but the actual calculation is coded as if it were local and performed over monolithic data structures. That’s a tremendous simplification and a clear productivity gain. "

" The combination of STM and PGAS in Chapel necessitates the use of distributed STM, an area of active research (see, for instance, Software Transactional Memory for Large Scale Clusters).

In Chapel, you not only have access to atomic statements (which are still in the implementation phase) and barriers, but also to low level synchronization primitives such as sync and single variables–somewhat similar to locks and condition variables. The reasoning is that Chapel wants to provide multi-resolution concurrency support. The low level primitives let you implement concurrency in the traditional style, which might come in handy, for instance, in MPMD situations. The high level primitives enable global view programming that boosts programmer productivity.

However, no matter what synchronization mechanism are used (including STM), if the language doesn’t enforce their use, programmers end up with data races–the bane of concurrent programming. The time spent debugging racy programs may significantly cut into, or even nullify, potential productivity gains. Fortress is the only language that attempted to keep track of which data is shared (and, therefore, requires synchronization), and which is local. None of the HPCS languages tried to tie sharing and synchronization to the type system in the way it is done, for instance, in the D programming language (see also my posts about race-free multithreading). "

" Here’s the tongue-in-cheek summary of the trends which, if you believe that the HPCS effort provides a glimpse of the future, will soon be entering the mainstream:

    Threads are out (demoted to latency controlling status), tasks (and semi-implicit parallelism) are in.
    Message passing is out (demoted to implementation detail), shared address space is in.
    Locks are out (demoted to low-level status), transactional memory is in."

" Message passing’s major flaw is the inversion of control–it is a moral equivalent of gotos in un-structured programming (it’s about time somebody said that message passing is considered harmful). MP still has its applications and, used in moderation, can be quite handy; but PGAS offers a much more straightforward programming model–its essence being the separation of implementation from algorithm. The Platonic ideal would be for the language to figure out the best parallel implementation for a particular algorithm. Since this is still a dream, the next best thing is getting rid of the interleaving of the two in the same piece of code. "

" But if you send a message to one thread and set up a handler for the result in another, or have one big receive or select statement at the top to process heterogeneous messages from different sources, you are heading towards the land of spaghetti. If you’re not careful, your program turns into a collection of handlers that keep firing at random times. You are no longer controlling the flow of execution; it’s the flow of messages that’s controlling you. Again, programmer productivity suffers. (Some research shows that the total effort to write an MPI application is significantly higher than that required to write a shared-memory version of it.)"

STM:

" (Transactions unfortunately do not address one other issue, which turns out to be the most fundamental of all: sharing. Indeed, TM is insufficient – indeed, even dangerous – on its own is because it makes it very easy to share data and access it from multiple threads; first class isolation is far more important to achieve than faux isolation. This is perhaps one major difference between client and server transactions. Most server sessions are naturally isolated, and so transactions only interfere around the edges. I’m showing my cards too early, and will return to this point much, much later in this essay.) '" -- http://www.bluebytesoftware.com/blog/2010/01/03/ABriefRetrospectiveOnTransactionalMemory.aspx

---

" Everything I described so far is common to CSP (Communicating Sequential Processes) and the Actor model. Here’s what makes actors more general:

Connections between actors are dynamic. Unlike processes in CSP, actors may establish communication channels dynamically. They may pass messages containing references to actors (or mailboxes). They can then send messages to those actors. Here’s a Scala example:

receive { case (name: String, actor: Actor) => actor ! lookup(name) }

The original message is a tuple combining a string and an actor object. The receiver sends the result of lookup(name) to the actor it has just learned about. Thus a new communication channel between the receiver and the unknown actor can be established at runtime. (In Kilim the same is possible by passing mailboxes via messages.) " -- http://bartoszmilewski.com/2009/07/16/on-actors-and-casting/


" The D programming language with my proposed race-free type system could dramatically improve the safety of message passing. Race-free type system distinguishes between various types of sharing and enforces synchronization when necessary. For instance, since an Actor would be shared between threads, it would have to be declared shared. All objects inside a shared actor, including the mailbox, would automatically inherit the shared property. A shared message queue inside the mailbox could only store value types, unique types with move semantics, or reference types that are either immutable or are monitors (provide their own synchronization). These are exactly the types of messages that may be safely passed between actors. Notice that this is more than is allowed in Erlang (value types only) or Kilim (unique types only), but doesn’t include “dangerous” types that even Scala accepts (not to mention Java or C++). "


" The two topics that stood out were: functional programming and DSLs. It seems like every language tries to assimilate some functional elements in order to deal with parallelism. Still, the problem is that in most languages the added safety features for concurrency are not enforceable and don’t compose well. A compiler can potentially check whether a lambda passed to a parallel loop doesn’t modify shared state, but it can’t extend this check to functions called from that lambda. The solution would be an effect system, which includes side effects of a function in its signature, but effect systems are extremely hard to program with. Compare this with Haskell monads, which serve as a simple but safe effect system. Unfortunately many imperative programmers have fits when they hear the M word. (I remember C programmers having the same reaction to the word “object.”) "

---

" The especially interesting Microsoft talk was about support for asynchronous programming in C# and VB. The speaker was Mads Torgersen, the organizer of the conference. Mads described a coroutine-style programming model where you can block in any part of your code using await and, when an event is triggered, resume execution right where you stopped. In the meanwhile, your program can continue executing other code — on the same thread. (You might recognize here a continuation monad in disguise.) This is a perfect model for GUI applications that call asynchronous operations. I talked to Herb Sutter about it later and he said that a very similar thing is being proposed for C++ (I’m going to be on the C++ sub-committee for parallelism with him, and I’ll report more details). " --- " Herb also had a talk about C++11 and how different it feels from the previous versions. For me the really big deal is move semantics that tips the scales from passing by reference to passing by value — where passing by value is really passing by reference behind the scenes."

---

" The newest addition to C# is support for asynchronous interfaces (somewhat similar to Boost::ASIO). This is a hot topic at Microsoft because the new Windows 8 runtime is based on asynchronous API — any call that might take more than 50ms is implemented as an asynchronous API. Of course you can program to asynchronous API by writing completion handlers, but it’s a very tedious and error-prone method. Microsoft’s Mads Torgersen described how C# offers several layers of support for asynchronous programming.

But what caught my interest was how C# deals with composition of futures (they call them task objects). They have the analog of an aggregate join called WhenAll? and an equivalent of “select” called WhenAny?. However these combinators do not block; instead they return new futures. There is another important combinator, ContinueWith?. You give it a function (usually a lambda) that will be called when the task completes. And again, ContinueWith? doesn’t block — it returns another future, which may be composed with other futures, and so on. This is exactly what makes C# futures composable and, hopefully, C++ will adopt a similar approach. "

" I noticed that there seems to be a problem with C++’s aversion to generalizations (I might be slightly biased having studied Haskell with its love for generalizations). Problems are often treated in separation, and specific solutions are provided for each, sometimes without a serious attempt at generalization. Case in point: cancellation of tasks. A very specialized solution involving cancellation tokens was proposed. You get opaque tokens from a factory, you pass them to tasks (either explicitly or by lambda capture), and the tasks are responsible for polling the tokens and performing appropriate cancellation actions. But this is an example of an asynchronous Boolean channel. Instead of defining channels, C++ is considering a special-purpose one-shot solution (unless there is a volunteer willing who will write a channels proposal). By the way, futures can be also viewed as channels, so this generalization might go a long way. "


http://bartoszmilewski.com/2009/03/10/futures-done-right/

http://fpcomplete.com/asynchronous-api-in-c-and-the-continuation-monad/

--- need *expandlist, expanddict syntax from Python (mb in our case expanddict is good for both, so just call it *)

---

need notation for requesting O() notation in time and space of algorithms -- but also want notation for requesting approximate algorithms (even though this notation, unlike the other, changes the choice of what function is being requested)

---

in general, we have a lot of matching and handler stacks (exception trigger matching, structural type matching, handling a __set which might be overridden, searching through active lexically scoped namespaces for a name

general construct needed

---

can __get s be overridden like __set s ?

do we need to allow __set s to be overidden if we allow tuples to be passed to fns?

yes and yes (the second yes: b/c it's not necessary, it never was, but it makes it much more convenient for objects that want to define a whole virtual graph of objects inside themselves; o/w they'd have to create anonymous functions for each thing inside of themselves to hold its location within the network, and wrap the real methods that deal with __get and __set for everything in the network. it's much more convenient if the parent object can just demand to be directly passed the path to the child virtual object for which a get or set is being requested.

actually after thinking about it for a few more minutes i don't see what passing the path to the parent is good for that can't be done just in the manner described above (namely, produce a child which delegates to the parent but with a 'path' keyword term). if there is a 'delegate' fn in the std lib then this is ez (note: delegation should be per interface). note: to make this work you have to have an analog of tail recursion where a chain of delegations is automatically collapsed by the compiler and interpreter; o/w a very deep node would have to go thru a zillion delegate calls whenever you call '__get' on the deepest node. Similarly, the 'path' needs to be optimized somehow so that you don't end up with a very big thunk when you get very deep -- again, though, a general solution is probably better here -- i think just strictly (e.g. not lazily) appending the last path element will solve the problem here.

---

need token syntax for:

execute this function, but if it throws a 'null' exception, turn that into a 'null' value. mb ~f.

make sure it interacts well with the maybe tower notation

should this syntax recursively apply itself within the function, so that inside the function, null exceptions are also caught and turned into nulls? eg. what if the function is just a cache actually getting the thing from somewhere else -- if the exception is uncaught then the cache wont work on null values. but you dont want to just willy-nilly mess up the calls inside a function someone else wrote. mb this is more of a convention than a macro -- mb by default its not recursive but implementors can override the pattern (e.g. say 'if i am called like ~f then do this, but the definition of plain f is this other thing'. seems to me like the maybe tower idea may have some clever way to take care of this automatically though, maybe in concert with dataflow tracing of the source of the value which is returned.

---

if the language has guards then by default this permits the defintion of partial fns; what if no guard triggers? does the function return null (undefined) or throw an exception (or throw an exception by default, which may be replaced by a null somewhere up the call chain, see above)

--

mb my idea for exception handling makes exception raising kind of like coroutines.

mb coroutines are a specialization of concurrency.

--

we want to be able to replace any variable holding a value with a probability distribution, define how primitives affect probability distributions, and then run a naive function that thinks it's dealing with ordinary numbers, and get the resulting probability distribution. and we want to do other similar things.

i think typeclasses do this.

remember to check out ML modules.

---

! could be the assertion character -- which is also possibly the type annotation character. !! could mean a compile-time assertion.

f ^annotation1 ^annotation2 could annotate f

$ could be interpolation

[] could be normal G-constructor, but something else like [" could be G-constructor where keys are implicitly quoted (inside that, $ could be used if a variable is needed instead)

---

bind: identify two nodes

(um, mb we should reserve the '=' sign in graph constructors for this...)

also, we need an 'unbind' or 'fork' or 'dup' or 'free' or 'decouple' or something.. (so do bound nodes secretly maintain their separate identity? mb there are two kinds of binds.. also, does bind also bind meta attrs? probably not, probably its per view..)


something like Python's % and Pythons's repr (used to be `blah`, which was more handy) would be helpful


Subject: Three defaults for parsing symbols

"symbol" $symbol ?symbol

$symbol is the 'typical' default, in which a symbol is replaced by its value during evaluation; e.g. if you say: "d = e", then $ is the default for the right hand side: the symbol "e" will be evaluated and the result assigned to symbol d.

e.g. inside a Javascript-style graph constructor, given [" ], e.g. [" dog = 3 ], the left hand sides are shortcuts for quoted strings, e.g. [" dog = 3] is short for [ "dog" = 3 ].

?var is formal parameters, so if "f ?a = $b", that's a fn defn of function 'f' with one formal parameter, 'a'. This is the default on the left hand side of fn defns, e.g. if you say 'f a = b', that's the same as "f ?a = $b"

A more unusual function could be given by:

a = 3 f $a = ?b

which means "function f(3) = symbol 'b'"

can apply ", ?, $ to envirojments (literal constructors) to switch defaults

Two repetitions = exact, one Rep means common shortcut,

e.g.

[" dog = 3] is short for [ "dog" = 3 ]

["" dog = 3] is short for [ "dog" = "3" ]

(" dog cat) is short for ("dog" "cat")

what about symbols, like :sym in ruby? 'sym like in Lisp?


Haskell-style pattern matching in function calls; Haskell-style cons in patterns; except patterns are first-class objects, and they can have variables (statically at the time of creation) intepolated into them, using $varname as variable interpolation operator.

also, pattern matching should have multiple patterns ANDed together and within that, be 'two-way', e.g. unification (see http://www.haskell.org/tutorial/patterns.html ). so i guess it's like Prolog. no need for NOT operator. So i guess it's like type matching.

also need something like Haskell's 'as' operator in patterns (see above link). i guess a good way to do this is to have the variables being bound to just be annotations on the pattern's expression tree, rather than require that they be nodes.

can interpolate variables into patterns with $var. formal parameters in patterns are ?var (this is default, tho, so 'var' is synonymous with '?var' inside a pattern. but what if you want to interpolate a variable containing a pattern? $var if you just want the pattern as value, but $$var if you want to integrate it into the current pattern. mb $$$var is how you would place the contents of the expression tree inside the variable into the current position before evaluating it at all (like a C preprocessor macro)?

---

hmm, in general, how to interpolate a dynamically subexpression tree held in a variable into a given position before macro expansion? that would be a form of macro expansion, so how is macro expansion ordered

the previous idea is a start; $var puts the value of the var in (as a value); $$var inside a literal puts the value of the var, if the var is of the same type as the literal, into the literal as a subexpression (e.g. before the literal is constructed, although in many cases i guess this will end up the same as $$var; $$$var interpolates the variable's value into the expression tree early in the interpreter stages (like a C preprocessor macro).

how to generalize that? it seems like the more $$s, the earlier in the interpreter stages that the interpolation must happen.

Connection to nil tower, handler stack?

--

should # be 'count' ('len'), ## be 'lazy', @ be 'macro'?

or is ! 'strict', @ 'perspective'? we still need a 'lazy'.

perhaps '?' is 'lazy'? but '?' is 'variable'. maybe '?' is 'lazy', '??' is 'var'.

actually i don't think we want to mix 'lazy' and 'var'. 'lazy' is the opposite of '!', but 'var' is the opposite of '$'. 'lazy' and '!' concern when to evaluate, whereas $ and 'var' concern whether to evaluate.

mb just '!' for strict, '-!' for lazy. mb '$' for interpolate, '-$' for 'var'.

also, need a recursive strictify. '!!'?

--

both '-!' and '-$' are boundaries; lazy is a recursive strictify boundary, '-$' (equivalent to lisp backqutoe) is an evaluation boundary

wait, so is -$ equivalent to symbol? i guess so.. and is '$' equivalent to 'eval'? i guess so..

or should Eval be $$$?

should have some syntax for custom boundary markers.. could just use annotations; e.g. ~lazy is equivalent to '-!', ~var is equivalent to '-$'.


don't forget that the placement of edges to views is supposed to be dynamic by viewpoint, and that this mapping can be changed by some sort of meta command.

now, what about overriding __get? mb that is even more meta, e.g. bypassing the assignment of edges by type

---

i think ! should be the strictify operator.

in fact, perhaps strictify and macros can be merged. they both have to do with telling some stage of the interpreter, 'go ahead and evaluate this now, even though normally it wouldn't be evaluated until some later stage'. ! is the 'now' operator. ! is strictify, !! is macro. in general these are all shortcuts for things like '!strictify' and '!macro', which are 'now markers' targetted at various stages of the interpreter (i guess that means they are graph annotations)

---

btw a fundamental operation is converting between a graph and the graph of cosets under some homomorphism

a fundamental operation is converting between a graph and the graph where its nodes are annotation of certain node types (going the other way: taking certain node types and turning them into annotations)

and note that the 'bind' operation is usefully applied to the expression tree during substitution

---

'now' means, 'take something which was being discussed hypothetically and actually do it'. it could also be described as the 'actualize' operator or the 'for real' operator. i guess the opposite of this is '?', the 'hypotheticalize' operator. '?' is in general a barrier, a wrapper.

a fundamental operation is converting between a graph and the graph where barriers of a certain type are leaf nodes

so perhaps '?' not '~' should be somehow worked into the nil tower?


the reason that human 'is' (which i am proposing that == should mimic) treats both equality and isa is that it is really saying 'has-attribute'; and if the thing on the right is a single thing, then implicitly we are talking about the attribute of being that thing. E.g. 'i am human' means 'i have the attribute of isa-human'. Attributes naturally fall into a hierarchy; e.g. if 'i am an animal', this implies 'i am alive'.


must make sure that list comprehensions are convenient


so, mb ~s have to do with being okay with exceptions.

by default, if f raises an exception or returns nil, then the exception propagates up through the caller.

~f means 'i'm ok with f returning nil, don't treat this as an exception'

~~f means 'i'm ok with f returning nil, don't treat this as an exception, and also wrap the nil using the exception tower' (optional argument: the label associated with the wrapping)

~~~f means 'catch all exceptions and turn them into nils, adding two levels to the exception tower'. optional argument: exception filter.

in all cases there is an optional argument for an exception handler.

maybe generalize the optional argument for the exception filter, too.

how to syntactically separate the optional arguments for ~ from the arguments for the underlying?

mb some sort of construct like ~f ~filter=() ~handler=() ?

this would then be generalized to allow multiple fns to be grouped together and mixing their arguments, with some precedence relation determining which fn is wrapping which other..

interesting, i like that..

mb ~ should be reserved for that instead..

or `

ah, i see, just use the same symbol multiple times to denote different layers of wrapping

e.g. ~except f a b c ~filter=() ~handler=() would be equal to

except (f a b c) filter=() handler=()

doesn't really seem to be too helpful though, does it? just use the darn parens if you want to do that in general..

so mb using ~ just for exceptions is good enuf

btw ~ and ! are very good symbols because they are in the corner, mb should use them for something more common

like mb use ~ for annotations in general, or for map, or for count, or for assertions/type annotations, or views

hmmm ~ would also be good for footnotes. this could be combined with the exception handling syntax somehow. footnotes are for handling unusual cases, and for labeling hooks, right? hmm..

mb f~blah means "do f and then wrap it with footnote 'blah', if there is one. 'blah' is also the name of a hook interface point". there is a convenient syntax for catching exceptions in footnotes, as well as ~~ ~~~ things sort of like described above.

multiple OOB channels, e.g. STDERR, STDINFO, STDOUT, except not just character streams...

mb what is sent via each channel is a 'compound message', which is a set of nodes.

should views have a syntax? e.g. @?

---

besides source filters, need an easy way to chain two programs together in the same environment, e.g.

jas -e common_imports.jas -e myfile.jas

when common_imports is executed first, and then myfile is executed in the same environment, so common_imports can import things into the environment for myfile

there should also be an easy way to do this in the Jasper header, in addition to the source filter syntax

the Jasper header should also allow #s or somesuch to put in stuff for the version control system


hmm if patterns define types. thinking about logics for patterns. the usual tradeoff b/t expressiveness and feasible inference.

since type are in a hierarchy, this suggests descripition logics.

i feel like we can cut out negation somehow. but not quite sure..

some types, like 'immutable' or 'functionally pure', are 'purity' types, meaning that they are true whenever some condition of impurity never obtains in some transitive/data-flow-y manner.

others, such as Integer, are 'classy' types, defined as subsets of other classy types. in this case they aren't true unless you have a reason to believe that they are.

so it seems like you may want to write some notation and then put a 'not' in front of it. but you'll still never need a 'not' inside of it.

mb logics without negation are called "positive"?

i think the 'purity' types correspond to positive 'impurity' types, e.g. 'mutable', which we can use negation-as-failure semantics on, e.g. if you never any code that would mutate this value along the dataflow, you may consider it immutable.

description logics have negation, and are PSPACE-complete (at least as hard as NP-complete), so that's pretty hard (but hey, at least it's decidable)

positive first-order logic is NP-complete (but hey, at least it's decidable) ( http://ecommons.library.cornell.edu/handle/1813/7017 First Order Predicate Logic Without Negation is NP-Complete by Kozen, Dexter)

do we really need both universal and existential quantifiers in our patterns, though?

hmmm... if there exists any mutation along the dataflow, then the variable must be mutable, so there's a there-exists. and a list of ints must contain all ints, so there's a for-all. but perhaps these are two different kinds of reasoning? e.g. it doesn't seem like you would ever need a forall if you are looking at a list of known types and trying to deduce new types, e.g. if the thing has type Int and type single-world-in-memory you don't need to look at both of those to deduce type Number, that's just a there-exists. similarly, you might want the type of a list of ints to say each element is an int, but you don't care about a list of things-with-at-least-one-int, so there you only need forall, not there-exists.

but just thinking about graph patterns, sure, sometimes you want to match a node which has at least one arc going to (Int or Real or Complex), and sometimes you want to match a node whose arcs all go to Int.

mb just that you can have either forall or thereexists but not both on one expression?

but what if you want to look at a list and see if it's a list of ints; you look at every item in the list (follow each arc), look at each of their types (follow the type arcs in the meta view), and then look for at least one of these type arcs to terminate at Int. But this is saying, forall arcs in the list, there exists a type arc from the target of the initial arc that targets Int. So we are mixing them.

we want OR; if something is an Int or a Float then it is a Number.

and i guess the last statement shows that we want Implies too?

could we possibly get rid of AND?

hmmm.. mb the two different activies above are, reasoning from the types to allowed values, vs. reasoning from program code to types.

e.g. reasoning from program code to types, we see that if there is a possible mutation, we must have type Mutable.

reasoning from the types to allowed values, we see that a list of ints must contain all ints

e.g. if you see a single non-int go into that list, then you deduce the impure type "non-list-of-ints", which is like Mutable.

similarly, from Immutable, you deduce that for every step in the dataflow, there is no mutation.

i think i may be conceptualizing this wrong. first off, for straight-up graph MATCHING, the problem is simple (this is like checking if an assignment to variables satisfies some set of FOL statements). the inference complexity comes when you are not presented with a single graph, but rather with a statement in this language (a statement about the type of the graph, i.e. a graph pattern), and you must deduce merely from that whether it will match another graph pattern, without reference to a single particular graph.

also, when i say that we want to deduce that because something is one type, it is another type, the types form a lattice. we can require the users to explicitly construct the rungs of this lattice, we don't have to infer them from the definitions of the types.

also, when i say that the compiler should infer if matching this graph pattern sufficies to show that that graph pattern will be matched, we can require a user to use the assertion facility to write a proof of this for us; the compiler is only checking the proof, not writing it. so if the problem of finding the proof is NP, then of course the problem of checking the proof is in P.

so mb it doesnt matter if theorem-proving in this logic is NP.

mb we just tell the programmer:

wait a minute... i think i am misunderstanding quantifier here.. we won't have statements like 'for all arcs a from node n, and for all arcs b from node n, a = b'. we'll have statements like 'for all arcs a from node n, let the target of the arc be m. for all arcs b from node m, ...'. we'll only quantify once at each level. so no, the quantifiers won't be exchanged. but used this way, this isn't really FOL quantifiers. we can't express things like 'for all arcs a from node n, there exists an arc b from node n such that b's label = a's label + 1'.

also, the reachability is like * is regexs, leading to the analogy that the reachability component of these patterns is like a regex over linear paths instead of over linear strings. a character in a string has a single successor but a node in a graph can have many (i.e. the outgoing arcs; or even the ingoing ones, i see no need to require the programmer to always respect directedness here, although that may be a good default, and some types of graphs may not permit the 'what links to here' query). so we need forall and thereexists to choose how to handle the multiple (Kant would say manifold) successors.

remember that we need to ask things about the reified edges, too, like what their label is

so i guess the type system will accept two kinds of definitive statements; "type A is a subset of type B" and "if something matches this pattern, then it is of type B". If you say "variable V is type B", then the system will enumerate all known types which are subsets of type B, and go through all known "if something matches this pattern, then it is of type T" for each such type T, looking for a match.

note: semantically, giving a pattern with "C or D" for type B is the same as saying "type C is a subset of type B" and "type D is a subset of type B". However, the system may not bother to infer this. (or will it? perhaps it should try replacing each type in the pattern with all of its subtypes..? or can we leave it up to the user to assert those things? no, if you have a type of 'list of numbers' and you assert that a list of Ints is of this type, that assertion should typecheck without you having to explicitly say it is a 'list of numbers' first. so it does try to match each type in the pattern against any subset of that type; but what it does not do is try to match the patterns of each of those subset types (because then we'd be recursing..). so if complex numbers are defined as things of the form (float, float), and you have a list like [(float, float), (float, float)], but you haven't asserted that the (float, float)s are complex numbers, then this list won't typecheck as a list of numbers ([Num]), because although it known that complex numbers are a type of number, it's not going to bother to match these patterns.

if you have a 'list of list of numbers', and you give it a list of list of ints, this should typecheck, but why? because it substitutes the 'list of numbers' match structure directly into the list match structure at the top. so it'll substitute in match structures of things directly mentioned, but not their subset types.

note: this way we never have to actually go into the 'type' metanode in our patterns for typechecking (except mb if we are doing something weird, not sure if we NEVER have to..)

how can we make the syntax for these patterns like [Num] for list of numbers? i guess that's just universal quantification. but mutability is existential..

with mutability: i guess it's important to be able to say that fns by default mutate unless we say they don't.. so we allow negations at both ends of the pattern (at the atomic elements, and at the top), just not in the middle.

hmm.. also should have an 'exactly one' operator (in addition to forall and exists), and an 'is-injective' operator (no two arcs have the same target), and a 'is-function' (no two arcs have the same label) operator.

hmm, now that we aren't doing inference, why not allow FOL anyways?

also remember we that we need a cons operator.. and i guess we need the analog of cons for graph nodes (i guess it's 'add this edge to this node') probably other more graph-y ones will arise.. the cons operator and its like must be able to appear on the left hand side of function definitions. Haskell used : and Shen used

.

also need parametric types; e.g. "swap(a,b)" of type A,B -> B,A where A, B are type variables

hmm.. principal types are too restrictive (since we want to uniformally treat type qualifiers such as 'unique'), but mb each node should only have one type when seen from each perspective?


@@ could refer to the last mentioned perspective (lexical scope), sort of an 'ibid' for Jasper


mb -f means 'negation of f' (negation of True is False, negation of 3 is -3), and f- is 'inverse of f'

---

messages to handlers are a graph with a set of nodes distinguished as 'headers'. handlers match on any of the headers, and then get the graph, with that header as an entry point

---

another fundamental attribute of nodes is reverse arcs, that is, 'what links here'

---

http://www.shenlanguage.org/learn-shen/prolog.html

---

http://clojure.org/Protocols

http://clojure.org/datatypes


easy for loops like python enumerate

---

types:

there are three kinds of properties associated with types. one is propositions that hold true for every value of the type; these are things like "list of Ints". the second is propositions that hold true for every access of the value in the type; these are things like "unique" (seen as 'copy never applied to this'), "immutable". the third are things about how multiple accesses to the value relate to each other, or how access to this value relates to access to other values (this third definition is unsatisfactory because it is so general). included in this are things like 'unique' (seen as 'this variable's value cannot be changed without accessing it directly'), 'deterministic' (vs stochastic), 'no side effects', 'purely functional'.

a suitable notation for the first one need only use positive connectives, and possibly quantifiers, over values, with subtyping.

a suitable notation for the second one need only be expressed as marking each primitive operator with respect to the type, and then confirming that no negative primitive operator of the type is every applied to a variable containing the value (e.g. if you want it to be immutable, then no mutation is ever applied)

i don't know to capture the third one simply, since defining 'no side effects' seems to be impossible without a language for the semantics of the programming language. of course, 'no side effects' can be treated as the second case if we allow it as a primitive.

--- should name those. mb 'value types', 'expression purity types'.

---

types can have properties, e.g. conditions, and these can either be necessary or sufficient or both. necessary: every value of this type satisfies this condition, or in the second case above, every access to every value of this type satisfies this condition.

sufficient: if there is a value satisfying this condition, or if there is a variable whose accesses satisfy this condition, then it is of this type.

--- in fact, we should have a general notation that lets us write 'necessary', 'sufficient', and 'iff'

---

should we allow types whose semantics aren't fully defined, that is, ones for which a list of sufficient conditions is not provided? yes, because this is how we use the type system to attach arbitrary tags and qualifiers for use in metaprogramming.

---

note that we need to attach tags both to locations in the expression tree, and also to types.

mb ~ can be expression tree tags, not needing ^ also? or are footnotes different from arbitrary tags?

--

i guess if we use -> for logical implication then move should be :=

--

compiler/interpreter option to say if the necessary conditions of supertypes of the type of something are checked

--

'promise' has a different meaning for 'expression purity types' than its usual meaning; we are only promising that the expression purity holds within the function we are defining, not in general; but when we demand it, we demand it for all past and future, over all functions.

for value types, 'promise' has the usual meaning.

--

want a generic way to add modals and quantifiers to statements. 'promise' and 'demand' are modals.


mb should look at

http://en.wikipedia.org/wiki/Category:Logic_symbols

http://en.wikipedia.org/wiki/Modal_operator

interesting application of modal operators in http://en.wikipedia.org/w/index.php?title=Modal_operator&oldid=503040598

--

if Jasper is general enough it should enable us to give commands that people would describe as rather philosophical, rather than practical computer programs that we know how to execute. E.g. the 'immortality program':

   Search for a program whose semantics is extremely similar to the input-output relationships of the agent known as Eugene Baylis Shanks III, who has the attributes in the language English "human being born in Tennessee in 1979 C.E. on the planet Earth in this universe". If such a program is found, execute it.

This contains all sorts of complex ideas, all of which however are generic enough that Jasper (or its std libs) could include them: search (without specifying how to carry out a search; e.g. looking for a program on the Internet or deterministically searching the space of all programs, or stochastically searching the space of all programs), a program, the semantics of a program, similarity, an agent, a name, the idea of attributes specified in a foreign language, a conditional on the result of search, the execution of a program.

note that placing the concepts of 'search' and 'similarity' in a std lib means giving Jasper the ability to express ontologies (not necessarily baking a large ontology into Jasper, however, although i think a bit of that would be a good idea also)

btw i used 'is' instead of 'are' on purpose because 'the semantics of a program' SHOULD be a similar concept.. perhaps as noted earlier, in this context 'semantics' is interchangable with the word 'meaning'..

---

slightly older notes:

How to tell which symbols in guards are unbound variables? later: ?x means 'variable x'


http://research.microsoft.com/en-us/um/people/simonpj/Haskell/guards.html

http://www.haskell.org/haskellwiki/Pattern_guard

http://www.haskell.org/tutorial/patterns.html

First class patterns

Two way patterns / want to support patterns that have: Two way matching (unification) with backtracking?

---

Src filters

Can use #! syntax (ignore the first one) (two!s for a Jasper src filter rather than external executable)

Whitespace and lines starting with # at the beginning of the file are file-level preprocessor directives

two things we need to do there: src filter, and run-this-first-in-the-same-environment (like Python 'from blah import *')


a boolean expression on the left hand side (lhs) indicates a barrier, e.g. f a and g b = a+b means wait until both f and g have been called then return a+b.

also need constructs to return different things to the different callers. should be subsumed into the general call/cc framework.

note that this sort of barrier is also an opportunity to exchange information between threads. combining this with its barrier-ness, what it is is a synchronized way to exchange information between threads (how to generalize this to an asynch or synch way? should asynch and synch be modes like any and all? can they be identified with any and all (mb: do this when any of the participating threads are ready; do this when all of them are ready)?)

---

tree (or even semilattice?) over perspectives. subperspectives could have the same arcs but different arc labels, etc.

---

everything is late bound in the following sense: can redefine the meaning of any function called within a function that you call; effectively, you can execute a function that you call within a little environment 'bubble' that you pass in. the effect is that any symbol referenced from within a function is equivalent to referencing a formal parameter with a default value, the default value being the environment (the closure) in which the function was defined.

this seems like a category-theory diagram..

in other words, (prototype) inheritance = closure = lexical environment = default function arguments

---

warnings fork and fire off an exception object; errors stop and fire off an exception object (divert the current thread of control)

exception objects carry a call/cc

---

mb should implement in clojure b/c it already runs on 3 platforms (jvm, clr, javascript)

---

the all/any mode is pretty common. e.g. instead of an if statement, we can have a switch statement (which is something like call(map())). But if multiple branches of the switch match, do we only run one matching branch of the switch, or do we run all branches that match? i like only giving those two options (rather than the C way of doing it that executes the switch sequentially) so that we might do the switch in parallel if they opt to match everything (which would be faster if the switch statement has millions of options, e.g. it is a parallel recognizer). I guess if you guaranteed that ONLY one would execute in case of the or, it would have to be serial, but if you said AT LEAST one then you could do that in parallel too (leaving room for an 'XOR' version that would mean 'EXACTLY one', which would be in serial)

---

btw, how to represent f()? that is, how to distinguish it from f? it's not quite 'eval' because that takes source code, not a function. "call"? should be syntax for that..

---

in general, can visualize control flow in this system as a bunch of nodes with arcs between them and bright points (the points of execution; the histories of those points are 'threads', although not necessarily hardware threads as one hardware thread may emulate simultaineous execution) traveling round the network.

the barrier thingee described above is like two or more arcs converging into one node. a fork looks like a fork. a switch statement looks like a node with one arc coming in and many coming out, annotated by their conditions (like an FSM diagram).

---

i think i mentioned this before, but you can generalize the 'cons' in an lhs pattern to any function that provides a partial/sortof inverse. E.g. if you see a function defn with a cons pattern, e.g. "f cons(a,b) = a+b", and someone calls "f x", you just call "[a b] = cons^-1(x)" to try to match it.

i say "partial" inverse because the original function may not be injective (and we only need any one of the inverses, altough again, there's room for an any/all mode here), or it may not be surjective (in which case trying to invert it on some members of the range will lead the partial inverse function to raise a nil error/return nil).

so it's not just cons..

also, this means that 'inverse' (how to say it? '-'? e.g. '++.-' is the subtraction fn, ".-" is division? don't we need to switch perspectives there because .- is in some meta perspective? or is that the default, in which case, how to override that default?) should be one of the basic/widespread (meta?) properties of nodes.

--- read vs. write seem like another fundamental 'mode' but it can be expressed by a picture with two nodes representing two variables, and whether one is reading or writing from/to the other is just which way the directed arc is pointing.

--

mb we should make a list of the various ways that diagrams can be interpreted like that that seem related to programming language semantics -- language primitives can then be just a diagram, and which primitive way to interepret it.

time (control flow)

dependency

data flow/motion of data (like read/write)

--

Are patterns special cases of sets (types)?

--

Persepective change based on type pattern match?

But then how to determine type of new arcs for __set?

So must define newtype based possibly on pattern match first

Would like to infer typr subset relations from patttern syntax

todo: i dont remember exactly what i was thinking when i wrote that..

perhaps i meant:

if you write a function f a = a.c, and you say in the function defn that 'a' is expected to be of class 'M', then 'a.c' is short for 'a@M.c'. For example, if object 'blueBox' is a 'Box' object which is also serialized to a 'File', then you can do blueBox@Box.open to "open the box" (e.g. call the Box class's .open method) or you can do blueBox@File.open to "open the file" (e.g. call the File class's .open method), and if you requested a value of type Box in the function header (pattern match), then the default used if you just type 'blueBox.open' is 'blueBox@Box.open'.

the question about the __set was that, what if the pattern that matched was 'type A or type B or type C' -- now when you add an arc, is it only added to the perspective that happened to be matched out of that OR? so i suggest defining a newtype, 'type D = type A or type B or type C' so that the arc would be added to type D (this is still not quite satisfactory, what if you wanted to add the new arc to ALL of type A and type B and type C in that case?)

and, i noted elsewhere that the pattern matching etc knows about subtype relations; and here i guess i said, we should be able to just look at the syntax defining necessary and sufficient conditions for types and infer subtype relations. if that's what i meant, then i no longer think that, because in general, that's a difficult inference problem.

-- aw heck let's make ':' the (default) symmetric delimiter and ; the (non-default) ordered delimited after all..

so

  name = input ; print 'hi'

is ordered and

  x = 3
  y = x + 2

is the same as x = 3 : y = x + 2 which is unordered

--

@@ shortcut for '(lexically) previous perspective'? e.g. 'x = f@blah 3 : y = g@@ 4' is the same as x = f@blah 3 : y = g@blah 4

--

Capital letters as punctuation?

--

need to support 'spreadsheet behavior', e.g. when you mutate one variable, others change with it; and also, 'watcher' event handlers can be attached to variables which fire when they mutuate (allowing dynamically implemented spreadsheet-like propagation, or GUI updates)

---

i feel like i need to get deeper into common graph operations for the std lib.. this will probably illuminate some better ways of doing things in general. things like merges, recursive descent broken by barriers, homomorphisms on nodes, diagrams (mappings of nodes and arcs).

--

Caps could be used for macros/keywords, symbols, constants (dont like for macros/keyword bc they are common)

-- Or how about for labels? No need for ~ then.

i like that. --

need function composition operator to be syntactically ez so we can do Haskell-ish things like

  count w = length . filter (==w) . words

to count the number of occurences of word w in a string

we already have /; what is the diff in Haskell between $ and . again?

oh i see, length $ filter (==w) $ words == length ( filter (==w) (words)) -- in this one, 'filter' is operating ON the function words, not on the result of it.

so introduce the 'o' operator. allow it to be a binary operator

---

the annoying thing about custom binary operators is really just that the reader has to remember how to parse them -- but they are convenient for some things, like a chain of function compositions; it's easier to read '# o filter (==w) o words' than 'o # / o filter (==w) words'

mb have some rule for which symbols are used for binary operators, and make them all left associative and equal precedence with one another.

e.g. things starting with '>' or '<' or '

' are binary operators.

special rule for things starting with =; they are the AND of all of the items in the chain. e.g. a == b == c means a == b and b == c.

arithmetic ops are NOT binary.

should ordinary greater than and less than be > and < or >> and 1 3

x << 5

---

"

This is something I've always been curious about, is exactly why Google appends while(1); in front of their (private) JSON responses.

For example, here's a response while turning a calendar on and off in Google Calendar:

while(1);[['u',[['smsSentFlag','false'],['hideInvitations','false'],['remindOnRespondedEventsOnly','true'],['hideInvitations_remindOnRespondedEventsOnly','false_true'],['Calendar ID stripped for privacy','false'],['smsVerifiedFlag','true']]]]

I would assume this is to prevent people from doing an eval() on it, but all you'd really have to do is replace the while and then you'd be set. I would assume eval prevention is to make sure people write safe JSON parsing code.

I've seen this used in a couple other places, too, but a lot more so with Google (Mail, Calendar, Contacts, etc.) Strangely enough, Google Docs starts with &&&START&&& instead, and Google Contacts seems to start with while(1); &&&START&&&.

Does anyone know what's going on here? j It prevents cross-site request forgery.

Contrived example: say Google has a URL like gmail.com/json?action=inbox which returns the first 50 messages of your inbox in JSON format. Evil websites on other domains can't make AJAX requests to get this data due to the same-origin policy, but they can include the URL via a <script> tag. The URL is visited with your cookies, and by overriding the global array constructor or accessor methods they can have a method called whenever an object (array or hash) attribute is set, allowing them to read the JSON content.

The while(1); or &&&BLAH&&& prevents this: an AJAX request at gmail.com will have full access to the text content, and can strip it away. But a <script> tag insertion blindly executes the JavaScript? without any processing, resulting in either an infinite loop or a syntax error. "


"semiglobal perspectives": immutable shallow binding dynamic scoping. e.g. you can attach a perspective to a variable such that changes to the values within that perspective are thrown out as you go back up the call stack (e.g. it's like a set of shallow binding dynamic scoping variables)

there is an implicit semiglobal perspective passed into each function. you can use this as e.g. semiglobal variables, but as the program expands and later on you want to split this into two separate states which assign different values to the same globals, you can choose to explicitly pass something in here, eg. its like

fn(x) is really fx(x, semiglobal_state=sg)

and you can do fx(x, semiglobal_state=my_custom_state) instead to override the default

other than this, there are no globals (can you put a reference in a semiglobal though if you want to pass info up the stack? todo)

why would you want to attach a semiglobal perpective to another object? consider I/O streams and defaults like how many decimal places to print when you send a floating point number to the stream. you dont want a subroutine that you call to change these defaults on you without you explicitly knowing about it, but you do want to change the default and have it affect the subroutines that you call.


lexical scope for programming in the small: mutation allowed

outside of lexical scope, you are programming in the large: mutation controlled


i think ruby has quick way to do this Python bit, mb we should too:

[results['x'] for results in resultss] [results.x for results in resultss]


lets say a superclass defines a method 'idle' which will be called when idle, and intends for it to be overrided with actual functionality. Now a subclass/mixin wants to add put functionality in idle(), but wants to expose the same API to its own subclass/other mixins, that is, in order to add functionality to idle() they override idle() (rather than inventing a new API, e.g. telling subclasses to leave idle() alone and to override only idle_main()). This subclass doesnt know anything about from where idle() was called; the only thing it can do is overridle idle(). How to accomplish this?


Footnotes:

1. ? mb we should use > for directed arcs in graph constructors (and <> for a directed arc both ways, e.g. an undirected arc; of course there is also a fn that makes all directed arcs undirected, so to save a few keystrokes you can use > and then use that function on the graph).

-- old note

Indices

A language must be good at indexing

--

old note

map _2 _1 to flip map _ _ x _ y f partial function application syntax (note: this was written when i was considering right-to-left syntax) like haskell but more explicit like lisp but assumed parentheSes

actually i still like both of these

if a _ is reqd for partial fn application, it is indeed more readable and explicit

actually, on second thought, that significantly reduces expressivity. what if you want to write a hof (higher order fn) that flips the last and second to last arguments but is indifferent to the arity of the result? this would prohibit that. you could be only slightly more permissive by saying something like, in the body of a fn that requires the arity to be at least n, you must explicitly have that arity in fn applications, and use _ for partial application, but this may be too burdensome for the compiler, and seems a little silly, since this would change what is required depending on if a subroutine were broken out or were left as a part of a larger function. so nix that requirement. it's still good style tho. and still useful for when you want to partially apply arguments in the middle. so the net result is: trailing _s are NOT required (but are good style).

wait, that was never the proposal in the first place. the proposal is: if you more than saturate a function's arity, you have to have parens where you saturate. e.g.:

;;yields_plusx x = fn* ?y , + x 1 ;; how does this parse? i dont think the , works... how is , different from / again? yields_plusx x = fn* ?y +.x.1 print / yields_plusx 3 5 ;; arity error print / (yields_plusx 3) 5 ;; correct

--

for continuations: could just let each label define a continuation (after it is passed thru, but within the same fn). use an operator, mb $, in front of a label to denote that you are referring to its value as a continuation rather than labeling the current point. using that operator with the name of the current function refers to the continuation just before it is called (that is, equivalent to the 'return' command in other languages, but flexible because we can return to multiple points, which is needed for the boolean lhs syntax). umm wait.. there needs to be a distinction between PASSING the continuation to a function, and CALLING it immediately ourselves... so we need 2 operators if we want to use labels as implicit continuations. note: as an optimization, the compiler doesnt actually have to construct a continuation unless it will be used.. labels have lexical scope of the current block/environment.

which operators? mb $LABEL to call it right now (e.g. jump), and

is it essential to have continuations cause a jump whenever they are evaluated? or can we require them to be explicitly called, e.g. $continuation for a variable containing a continuation? i think the latter because we can always do:

callit x = $x

g(callit $BLAH)

if we want to pass g some normal seeming thing that actually calls a continuation when evaluated.

so let's do:

?LABEL to get a value that refers to a label, and $LABEL to call the continuation (in general, $var to call the continuation in that var's value; if that variable contains a string, eval the code in that string; if it contains an intermediate Jasper code representation, call that, etc; but if you are inside a string or pattern, interpolate in the value in var)

--

should we allow stuff like C does in the header to make mutation convenient, e.g.:

mutatePlusOne &x = x += 1

which is short for

mutatePlusOne x = *x += 1

and, unlike C, rather than implicitly changing a pass of 'x' into a pass of '&x', i would require this to be called as:

p = 3 mutatePlusOne &p

note: this sort of thing IS still unique (we haven't created an alias to p) iff x is unique in mutatePlusOne.

--

note: forking makes all local variables non-unique (because it makes a shallow copy of the local context)

--

still a little confused between the difference between a graph and a node, and what happens if you refer to SELF or ROOT within a graph.

i guess a graph == a node with arcs to each node in that graph, with these arcs labeled with their node labels (because node labels are specific to each graph, dig it?). So if x = [[image:x?]] means that x is the set which has a single element which is a set with one element, which is the set x, then that corresponds to the graph

[[ROOT?]] or mb instead of ROOT it should be [[SELF?]]

--

y'know, mb we SHOULDN'T require folks to put parens around function calls, because that breaks the identity between nodes and functions; there would be no way to make a node that works exactly the same as a binary function. Or, we could use barriers to denote the arity of functions; but that puts the hard-to-check requirement that the barrier be placed the same number of hops out for every set of arguments. Or, we could just give nodes an 'arity' meta-property (or mb even a signature meta-property?)

hmm.. i like the last solution the best..

--

mb x anything= y z is short for (x = (anything x y z)) e.g. x += 1 is short for (x = + x 1)

exception: things starting with =

--

would be nice if forking could be expressed in terms of graph primitive on threads of control.. something like

y = CONTROL y.ENV = dup(y.ENV) ;; b/c ENV has references to each variable, not just each variable and its value ;; note: dup is a deep copy, not shallow, although it doesn't actually make a deep copy right now, it only recurses does it on demand $y ;; or mb y.run or something.. but if ur gonn do y.run, why not just do fork()? ah well..

but my point is that forking corresponds to a graph operation.. hmm..

--

lhs implicitly in graph context (space delimited)?

how to do multiple return arguments? mb

ret1 ret2 = f(a,b) and g(a,c) = (ret1 = a+c : ret2 = a + b)

which is equiv to:

ret1 ret2 = f a b and g a c = ret1 = ++ a c ret2 = ++ a b

if you only have 1 =s then the mult returns are omitted and ret arg is whatever is on the last line:

plusone x = + x 1

and, or are also binary ops

if you only ask for one return arg do you get all of them in a graph, or just one? let's say just one by default; mb use *f to get all of them (note contrast to f*, which is for variadic functions; also, note that if we use * this way, we'll have to think of something else for the inverse of &; how about just -& ? also, should ? be replaced by -$ ? i kinda like having ? and $; mb for basic punctuation syntax we provided both inverses)

--

hey, with labels can have lazy barrier with

LAZY

--

hmm is hard to read if you dont know if a label's name is some meaningful metaprogramming keyword or just a local label.

mb have the former be in all caps, and the latter capitalized but also have some lower case? but that's kind of backwords in that local labels will more often be one letter. so how about: all caps means local label, caps means meaningful label.

so the previous example would become 'Lazy'.

---

mb the way to do autoreturns is to use variables $0 $1 $2 .. $9. If the return arg isn't captured, it still goes in there (unless it is never used, then the compiler is free to optimize it away?). if a subroutine explicitly sets $0..$9 then that goes, too

e.g.

s = 'hi there bob' r = 'hi there (.*)' regex r s ;; regex sets $0..$9 print 'your name is $$1'

mb use % for this since $ is used already? namely, % is a prefix for variables which the function can set. if it has multiple returns, then those variable names are set, e.g.

ret1 ret2 = f a b = ret1 = ++ a b ret2 = -- a b

now the caller

f 1 2 ;; effectively short for %ret1 %ret2 = f 1 2 print %ret2

---

three uses of *, need to disambiguate:

--

for consistency, if "f &x" is for mutation, then

y& = x should mean "from now on, if i mutate y, that should mutate *x too" -- e.g. y& = x& is equivalent to (bind x y)! should just use that in place of 'bind'!

---

to emph:

y& = x& instead of bind x y

--

hmm mb NOT have the exhaustion needs parens rule, just b/c it makes parsing more complicated

--

mb allow user to have custom delimiters for metaprogramming, as long as they start with : or ;

--

is ;; for 'print'? how about ';\n' is for print? is ;; for comment? or ..? or ,, ?

hmm, ,, seems like a good bet for comment actually.. so, ;; and :: for print? or ;\n and :\n for print? hmm you need ;\n somethings so let's have it be ;; and ::

---

when you have multiple entry points (boolean and/or expressions in fn defn), you need different return args for diff entry pts, e.g. it should be

ret1 ret2 = f a b and ret3 = g a c =

(easier to read: (ret1 ret2 = f a b) and (ret3 = g a c) =

mb make parens compulsory?

)

the way to handle async is to use 'or' in the expression. then if you refer to one of the other entry point's formal parameters, a future is returned.

so e.g. to have a container to pass one thing back and forth in:

(f x) or (ret2 = g) = ( ret2 = x )

now one caller can say 'f 3' and it returns immediately. later another caller can say 'g' and it returns 3. if g was called first, it would return future. accessing that future strictly (ie non-lazily) would block.

note that you can reuse the same formal parameter in the expression, indicating matching, e.g.

(f channelnumber x) or (ret2 = g channelnumber) = ( ret2 = x )

now you can do f 1 3, then f 2 4, and then g 1 returns 3, and g 2 returns 4.

--

'or' in fn defs for async vs. for multiple forms contradicts; so mb just always be async, and let 'or' be for multiple forms

---

(moved to whitespace notes)

--

'*' can be 'recursively'. *! can be 'recursive strictify', rather than '!!'. That leaves '!!' to be 'even earlier', e.g. something to be done during preprocessing.

this allows *op on other operators to be 'recursively'. Must find something else for 'expand in place'. How about '$'?

--- the semantics of laziness in Jasper is not 'guaranteed not to be executed until needed', but rather, 'if it hangs, the whole program will not hang unless its needed'. E.g. if there are processors available, Jasper might speculatively evaluate thunks (this gives the compiler room to try and optimize if it finds itself running on a massively parallel architecture). The main difference is that side-effectful operations in unneeded thunks may execute. So, don't rely on the lack of side-effecting of thunks that you think shouldn't be evaluated.

-- the need to pass in sensors and effectors for security suggests transient globals, that is, that when you pass into a function, you can also pass in globals. These globals are deleted when the function terminates (but if it passes a continutation of itself back up, they are maintained in the continuation..).

--

Globals can also be 'targeted', that is, scoped so that they are only visible from within certain namespaced (use labels and say 'they are only visible beneath a label of this name'?)

if labels are used as addresses in this manner then they must themselves have lexical scoping so that they can be hierarchically addressed, e.g. so that you don't have confusion when two different modules use some of the same label names.

e.g.

( BOB ( JAMES ( ELLEN ( 2+1 ) ) )

  RANDY (
    ALAN (
      ELLEN (
        c = 2 + passed_in
      )
   )
  )

to refer to Ellen here you would say either BOB.JAMES.ELLEN or RANDY.ALAN.ELLEN, or BOB.*.ELLEN if there were only one ELLEN under BOB, but you didn't know the internal structure of BOB.

)

---

,, for comments? or a , b , c ,, d , e --> ((a) (b) (c)) ((d) (e))

hmm i like the latter. mb for comments? we still have "" for them empty string, right?

--- needed library ops: convert depth-first traversal to stream and parse stream to depth-first (rec descent parse)

---

mb use the "homogeneous view" of graphs; for all x, x@meta.self is an arc which is equal to x (e.g. the nodes are just a subset of the arcs)

---

mb perspectives help solve the prob in that haskell paper whereby haskell typeclasses allows extension of functionality but OOP allows extension of data? what was that paper?

---

how exactly to handle hypergraphs?

hmmm.. normally n.a means "call n.__get(a)", where n is a node, and a is the label of an arc. to generalize this, we need to specify (a) more args, (b) which role n is standing in. also, in general, this returns a set rather than a single node, since multiple arcs may have the same label.

perspective == role i guess

--- ok, i guess here's how multi and hypergraphs will be handled:

multi: __get and the like, by default, select one of many arcs with the given label (or mb produce implicitly mapped sets, see below). for multigraphs, you use variant operators which produce sets of results.

  need a general notation (at least a convention) for 'variant operators'

hyper: each perspective corresponds to a role (mapping that role into the "target" slot). the default __get gets the arc and then gets the target of that arc, but that can be customized. each role can have its own __get.

e.g.

personA@father.0.@child[1] gives the second child of the father of person A.

--- any, all: should we have "implicitly mapped sets", upon which operations are implictly mapped?

how to generalize that meta-programmatically?

i guess it's saying f x --> map f x

so are we saying that variables can carry instructions to transform the functions that operate upon them?

hmmm.. sounds interesting.. but, security problems?

surely we'd have to restrict which functions are transformed? no?

this seems like a special case of monads? in a way it's more general since you could have multiple variables in different monads..

hmm.. seems like it fundamentally alters what notation means.. a variable is no longer a carrier of a value, the expression "f x" can't be interpreted as "perform f upon x"... that might be ok though..

this seems to match up with the idea of perspectives.. in the default perspective, performing an operation upon an implicitly mapped set implicitly maps it.. but in the "implicitly mapped set" perspective, the value is actually a set.

mb an "absolute" perspective that removes this property from values.. good for security.. or do we just make it sandboxable, e.g. a computation must be explicitly passed sensors and effectors in order to be able to use them.. however there is still the problem of computations that just purposely hang (denial of service)

this could make some things easier.. implictly mapped values.. what else is it good for? can we do implicit backtracking, like with general monads? i think we could..

ordinary __get and __set sufficies for read- and write-'traced' values for debugging.. write-'watched' values for spreadsheet-style propagation.

this seems rather dual to the way that functions have parametric and ad-hoc polymorphism (in particular, this seems like parametric polymorphism with no type restrictions).. the function transforms the value and the value transforms the function.. perhaps we can generalize it so as to unify/symmetrisize it in both directions? is this related to Chu spaces (i don't think so)?

we already sort of have two homogeneous viewpoints here.. data values == functions (both are nodes), and nodes == arcs. now we're saying that we also want some more symmetry between a function being applied, and the value it is being applied to.

i guess.. first the value transforms the function.. it can be polymorphic, etc, as usual... by default it does not transform it, that is, it transforms it by sending it through the identity..

---

how to handle type coercion?

in a way, perspectives are type coercion.. e.g. an "implicitly mapped set of ints" sent thru a fn that wants an "int" is exposing an 'int' interface, but it also has an "implicitly mapped set of X" interface for things that recognize that type. but this isn't the whole story, because you could still expose the "int" interface to something that can recognize both "int"s and "implicitly mapped set of X"s.

---

shouldn't perspectives themselves be somehow combinable, not just "flat"? how does that work?

e.g. above we saw that perspectives may be like monadic values, which means they may be e.g. things of type "implicitly mapped set of ints"

similarly, in the exception tower, there has to be the ability to ascend and descend the tower.

so it seems that perspectives themselves can be combined into a structure.


in addition to an 'absolute' perspective (which is just the representation available from the meta perspective), we have the ability to 'project a perspective', which means to take only the info in that perspective and copy it into another variable (actually, we have this implicitly; just go to the meta perspective, access this perspective, and you have the node as seen from this perspective; just copy that, it won't copy the whole node)


seems like in general environments (like (), [], ["] ) are things that do some sort of transformation on the things in them


todo

so, some remaining questions:

a) how are perspectives related to type coercion, if at all? b) how are perspectives combined? e.g. an "implicitly mapped set of ints". e.g. monadic lifting? c) what is the syntax for accessing a part of a combined perspective? do we need a 'path syntax', e.g. a.b.c.d can mean that path as a whole rather than == __get(__get(__get(__get(ENV, a), b), c), d)? couldn't we just use [a b c d] for that? c) how to control the passsage of sensor and effector access permissions down? d) how does the exception tower work? this should be a clean special case of perspective combination e) if both the function and the value are polymorphic, who polymorphs first? i'm guessing that first the value chooses what it does to the function, then that modified function looks at the value's type and polymorphs and applies f) ML's module system g) problem chaining calls when object-style calls and function-style calls are mixed (e.g. i cant think of an example but in ruby you can go a.b.c sometimes when the Python would intermix function calls with method calls, leading to a chain that goes in both directions). how to make prototype inheritance and Haskell-style polymorphic functional dispatch match up?

---

matrix literals utilizing ':' or ';' for the second dimension (or : for 2nd, ; for 3rd, and generalize, mb :3 for 4th, e.g. ; == ;2

---

need a std var in toplevel env to store commandline arguments in

(and/or: may as well be the same way that fn args come in..)

actually, that's interesting; any fn can be used on the commandline as a 'main', just write the fn defn as usual.. it'll treat the last fn in the file as 'main'..

--- for matrices/paths: could do as Numpy does; you can access a 2D matrix as:

mat[a][b]

or as

mat[a,b]

and the latter gives scope to do, say,

mat

or mat[b,:]

what symbol should serve as the ':' here?

mb not '*' because that is 'recursive :', that is, mat[a * d] instead of mat[a : : d]

can't actually use mat[a :] because that's a syntax error since : is an EOL character .. wait no it's not, we're inside a data environment ('[]'). Can we just use ';'? Naw, that should keep its meaning of 'ordered'. How about '::'? hmm, mb. Mb the EOL debug should be ::: and ;;;, leaving us to use '::' and ';;' as such generic 'marked separators'.

or mb should use '_'. Yes, i like that best. _ means 'put something here'.

so: mat [_ b]

mb '*' should be 'all', not 'recursive'? in a way, 'recursive' is a special case of 'all'...

-- need a character for C's '*' (dereference pointer). How about just '-&'?

---

if = is the last non-whitespace thing on a line, convert it to = (


(done) look up how other languages (e.g. Ruby) sanely handle metaquoting

Ruby's way looks pretty good: http://en.wikibooks.org/wiki/Ruby_Programming/Alternate_quotes

http://www.techotopia.com/index.php/Ruby_Strings_-_Creation_and_Basics#Quoting_Ruby_Strings

---

the diff between / (Haskell's $) and ,:

a , b , c --> (a) (b) (c) --> (((a) (b)) (c)) a / b / c --> a (b (c))

the / effectively makes everything right associative, the , merely surrounds in parens, leaving the underlying left associativity intact

---

mb

if x (print 'x was not == nil') (print 'x was == nil')

sel x [ 1= print 'x was == 1' 2= print 'x was == 2' ] (print 'x was neither == 1 nor == 2')

sel=== x [ 1= print 'x was === 1' 2= print 'x was === 2' ] (print 'x was neither === 1 nor === 2')

"if v ifnotnil ifnil" is short for:

sel v [nil=ifnil] ifnotnil

hmm mb should be "if" instead of "sel" and "ifnn" instead of "if".

hmm let's compare to Python:

if x: print 'x was not == nil' else: print 'x was == nil'

ifnn x (print 'x was not == nil') (print 'x was == nil')

(ifnn x print 'x was not == nil' print 'x was == nil' )

if x == 1: print 'x was === 1' elif x == 2: print 'x was === 2' else: print 'x was neither === 1 nor === 2')

if=== x [ 1= print 'x was === 1' 2= print 'x was === 2' ] , print 'x was neither === 1 nor === 2'

if x == 1:

elif x == 2:

else:

if=== x [ 1=

  2=
    ] ,

yeah it's just as good..

mb could use

if x *[] default

instead to combine the two? then if there are two unlabeled args, it must be an ifnn, otherwise, it must be an if (or, vice versa, if there are labeled args, it must be a switch)

so

( if x print 'x was not == nil' print 'x was == nil' )

if x *[ 1= print 'x was == 1' 2= print 'x was == 2' ] , print 'x was neither == 1 nor == 2'

or

if x 1=(print 'x was == 1') 2=(print 'x was == 2') (print 'x was neither == 1 nor == 2')

if x 1=(print 'x was == 1') 2=(print 'x was == 2') def=(print 'x was neither == 1 nor == 2')

and

if x 1=(print 'x was == 1') 2=(print 'x was == 2') def=(print 'x was neither == 1 nor == 2') op=(===)

for ===

now compare a truly nested if:

if a == b: if c == d: print 'a == b and c == d' elif j == k: print 'a == b and c != d and j == k' else: print 'a == b and c != d and j != k' else: print 'a != b'

if a==b ( if c == d print 'a == b and c == d' ( if j==k print 'a == b and c != d and j == k' print 'a == b and c != d and j != k' )) print 'a != b'

hmm, looks pretty good actually... in fact i find it more readable than the Python (!).

and for === use the 'op' optional argument:

1=(print 'x was == 1') 2=(print 'x was == 2') def=(print 'x was neither == 1 nor == 2') op=(===)

how to distinguish "no labeled args' from 'labels 0 and 1', since those labels are by default? well, they aren't by default actually; a function with a list of named arguments gets only named arguments, the positions are thrown away. so it's fine.

mb the defn of if is just:

if T=N, N=N, *kw: ...

so passing two positional args is just like doing a switch to T, N. However, we don't really want T here, we want -FF, the class of things that are Not Falsish.

if (-FF)=N, def=N, *kw: ...

where Falsish things are F, nil, empty strings, zero, empty graphs.

--

(moved to whitespace notes) ---

from E: maybe there should be a syntactic way to distinguish functions whose side-effects take place immediately, and those that take place eventually. e.g. if you say

a = b

but a is a remote object, and you want to do the __set asynch, then mb we need some more machinery here?

otoh isn't this already implicit? if the user cares about waiting for something they'll use a semicolon instead of the implict colon.

hmm but how about if we want to monitor these promises explicitly?

well i guess we'd delegate a and override a.__set and have a semicolon after the call to the delegate and then we do whatever we want to do after the __set actually completes.


fn is short for "map fn" fn'condition' is short for (map fn) o (filter condition) fn is equivalent to (map fn) o (filter T)

should have something like this for reduce too..


note: the fundamental attribute __apply allows you to override e.g. map for your object if you have a faster way (e.g. if it's over a network connection). e.g.

__apply fn = if fn === map my_special_map me fn me

note: this won't actually work because map has already been partially applied to the map fn. but mb we can use pattern matching to un-partially apply it:

__apply fn = if fn === map mapped_fn my_special_map mapped_pn me fn me

can also use the type of the function (here, function_that_should_be_applied_remotely is a type):

__apply fn = if fn == function_that_should_be_applied_remotely me._send_remotely_and_apply fn fn me

---

are types just created by overriding the instanceof and subtypeof methods?

---

coroutines? generators? multiple inheritance?

--- built-in line input looping

--- " Outside of Common Lisp, Python ctypes may be the best FFI out there."

---

how to write code that can return from a calling scope?

a) macros b) continuations via labels -- actually even a macro would have to use this. but this brings up a problem; unlike RETURN there is not a std label name for the nearest function scope. so create one: FUNCTION

--

mb 'me' should just refer to the next scope above the current function definition?

naw.. b/c of embedded functions..

-- swap @ and ' --

Python (from http://news.ycombinator.com/item?id=291164 ): sum(i2 for i in range(10)) any(p % i == 0 for i in range(2, p - 1)) dict((v, k) for (k, v) in dict_to_invert)

equivalent Jasper:

sum (@@(= exp _1 2) ..10) any (@@(= mod p _1 == 0) 2..(p-1)) dict (@@([k,v] = [v,k]) tuples(dict_to_invert))

sum(i2 for i in range(10)) sum (@@(= exp _1 2) ..10)

any(p % i == 0 for i in range(2, p - 1)) any (@@(= mod p _1 == 0) 2..(p - 1))

dict((v, k) for (k, v) in dict_to_invert) dict (@@([k v] = [v k]) tuples(dict_to_invert))

the last one could be clarified if we had an arcmap..

dict((v, k) for (k, v) in dict_to_invert) dict (arcmap (= [_1.to _1.label]) dict_to_invert)

actually i think the original is clearer-- use the same ops, but transform the graph first, e.g. with 'tuples'. so back to:

dict((v, k) for (k, v) in dict_to_invert) dict (@@([k v] = [v k]) tuples(dict_to_invert))

---

need to give method_missing like behavior by having a __def (default)

---

" This also bugs me to no end: nil.to_i == 0, 0 evals true, but nil evals false. "

ok, we don't do that.. 0 evals false (it is in NN, 'nilish')

---

earlier i said you could put &x in a fn argument list like C to save the trouble of dereferencing the pointer each time you refer to it. nah! if you wanted to only read from it once you could just copy it. But if you want to mutate it, let's require the deref each time to make it more obvious to the reader that something dirty is going on.

(what to do about object methods?)

--

" g) problem chaining calls when object-style calls and function-style calls are mixed (e.g. i cant think of an example but in ruby you can go a.b.c sometimes when the Python would intermix function calls with method calls, leading to a chain that goes in both directions). how to make prototype inheritance and Haskell-style polymorphic functional dispatch match up? "

hmm.. as noted by others, object method dispatch can be thought of as a special case of polymorphic functional dispatch where the object is the first argument and the function is polymorphic only on the type of the first argument. e.g. "mylist.map(mapfn)" is really "map(mylist, mapfn)".

but what about prototype inheritance?

--

and something like "map mapfn mylist" in Haskell can be thought of as a special case of object dispatch where the current namespace is the environment, e.g. it's really "myenv.map mapfn mylist"

i guess the problem with chaining stuff in Python may be more that methods tend to mutate, whereas functions tend to return the modified value. the impedence mismatch may be just coming from inplace vs. functional function return styles.

--

which is better?

obj2 = obj.+ 3 obj2 = + obj 3 obj2 = obj.+ obj 3

seems to me that the second one is better

obj2 = + obj 3

but how would that one do prototype inheritance?

well.. it might be simple enough... that + is imported from some namespace, np.. and that can inherit..

obj2 = np.+ obj 3 (obj2 = np + obj 3 would be the same)

i guess this doesn't break encapsulation; after all, the definition of the form of the data storage (the type of obj) is still bound up with the definitions of the functions that can be performed (in the instance definition for the typeclass in np that defines +).

so i guess what we have are two kinds of things (really just two patterns i think):

namespaces, which have prototype inheritance

objects, which have state (although they are still pass-by-value unless you take a pointer with &)

---

y'know, instead of ever explicitly taking pointers, you could just do:

x = 1 &y = 3

f x f &y g y

meaning that y is a pointer, &y is the dereferenced value pointed at by y, the function f takes integers, and the function g takes pointers to integers.

basically we are just saying "y is a pass-by-reference variable. but we pass it by value to f". we let the language handle the creation and destruction of the pointer. so e.g. "&0" wouldn't make sense, because & means dereference. If you really wanted to pass around a pointer to a literal value, you would assign that value to a pass-by-reference variable.

--

a = (__init__ me barg = (me.b = barg); c me = b + 2; d = 4;)

a = (__init__ &me barg = (&me.b = barg); c &me = b + 2; d = 4;)

hmm... that's a pain.. mb 'me' is special and is always short for &me.. otoh perhaps these methods do not mutate me unless inplacified? or unless me happens to be stored in a pass-by-ref variable?

for some reason i kind of like the latter, not sure why..

---

mb could use e.g.

a --= b _

for

a = -- b a

a --= b

for

a = -- a b

--

so as to objects holding state:

x = __init me = /me."b" = 4 / umm.. too cumbersome to type ."b".. mb ' should be quote or symbol after all.. me.'b = 4

   increment_b = 
      me.b ++= 1
      'success'

inherit x the_superclass / this would replace x@meta.__def with a function that uses the superclass inherit_with_super x the_superclass / this replaces x@meta.__def with a function that uses the superclass, and hooks every function to call 'super' first by default, and makes every fn return 'me' in its first return arg by default (stated return args go into the second arg, etc; first arg is named me, e.g. %me) how does it do that? does it override __set? even if so, how does it distinguish functions from fields?

new x = x.__init []

y = new x x2 = y.increment_b / make a copy of x, then increments the copy, and puts it into x2

[x2 result] = y.increment_b / make a copy of x, then increments the copy, and puts it into x2; capture both returns arguments print result

&y2 = new x &y3 = &y2.increment_b increments x.b in place, then puts a pointer to it into y3.

hmm.. i think i like that. mb no need for an inplacify operator.

-- can we make this shorter:

[x2 result] = y.increment_b / make a copy of x, then increments the copy, and puts it into x2; capture both returns arguments

e.g.

x2 result = y.increment_b / make a copy of x, then increments the copy, and puts it into x2; capture both returns arguments

that seems to conflict with the fn defn syntax tho.. mb require leading =s for all fn defns?

(= 'fnname' [vars] = body)

(= [return_vars] = 'fnname' [vars] = body)

(= [return_vars] = 'fnname' [vars]

[conditions] and [return_vars2] = 'fnname2' [vars2] = body)

or even

fnname1 fnname2 = = [return_vars] = [vars]

[conditions] and [return_vars2] = [vars2] = body)

seems kinda crazy, mb just use 'fn'?

---


ok, i thought of some things we could use {} for.

(a) could use it as a pattern literal constructor

(b) could use it to surround 'notes to the compiler', that is, type annotations, other annotations (including constraints which will determine which implementation is chosen for a data structure), assertions. We could just consider these all to be assertions of various kinds. If we do this, it seems like the ^ character is unneeded.

note that all of these environments ({}, (), []) are really just quoted lists put through some function (in core jasper).. (except that: a compiler assertion is directed to the compiler, yes? so ^ would be used? if {} is primitive then do we not need ^?)

hmm pattern literals are still data and seem to make sense thought of as 'just quoted lists put through some function' more than assertions. so let's use {} for 'notes to compiler'.

now is ^ unneeded? i think so. Instead of saying 'f^(returns an integer) = a + b', we say '{returns an integer} f = a + b'. This seems like it may satisfy the high criteria of wasting a grouping punctuation type, because it will be nice for the reader to visually distinguish the notes to the compiler (of course, their text editor could hide it for them, so maybe this isn't as big a deal as it seems.. hmmm..'. i also like putting the annotation before the object it is annotating instead of after (although we could of course do this with just ()^f).

We still need a syntax to distingush assertions from type statements. We could use '!' for type statements, because they are evaluated sooner (during compilation), whereas assertions are at runtime.

so we'd have stuff like:

{!returns an integer} f = a + b {f > a; f > b}

of course we need to work out a syntax for "returns an integer". Presumably this is a pattern match for a node the types of whose targets are all integers (see below).


note for the pattern matching syntax: defining the type of a function's arguments means asserting that every label on every outgoing arc from a node matches a certain type; defining the type of a function's outputs means asserting that every target on every outgoing arc from a node matches a certain type (and we also gotta find a good way to do multiple output arguments without too much extra pain).

The patterns for these things must be very concise and syntactically clean since they will be used a ton.

one pattern syntax might be:

" root =^T B T isa blah-type B isa foo-type B =^any C C isa bar-type "

meaning that: the object is question ('root') contains arcs (a forall on the arc labels are bound to 'T' in this pattern) with targets (a forall on these targets is bound to 'B' in this pattern). Those arc labels (T) are all of type blah. Those targets (B) are all of type foo. Each target has at least one target satisfying the conditions of 'C': C is of type bar.

(we could use / instead of =, which is nice because it is asymmetrical, but we're already using it as a grouping operator)

Note that the quantification on bindings is by default 'all' (alternatives are 'any', 'exactly one'; we also need ways to express constrains like 'there must be at least one arc' vs. 'there may not be any arcs'). Note that =^T means 'T is bound to the label of this arc'

Note that the labels are all in caps; these are local syntactical position labels just like the CAPSLABELS elsewhere.

Note that the parts of the pattern are just implicitly terminated statements, and that the pattern is equivalent to "(root =^T B) (T isa blah-type) (B isa foo-type) (B =^any C) (C isa bar-type)"

Now, to define the types of a function's inputs and outputs, we'd say something like:

' root =^I O I isa input-type O isa output-type '

To make this more concise, we could start by predefining some of the labels, which just means introducing some implicit statements into it:

" root =^X Y "

Now we can just say:

" X isa input-type Y isa output-type "

This is better, but we'd still like to just say the input-type and the output-type. We could define a macro:

"Ftype input-type output-type" which would compile to the previous expression.

Or we could give it a binary syntax:

"input-type -> output-type"

That would compile to:

" root =^X Y X isa input-type Y isa output-type "

i tend to like that one the best.


js bind: bind 'this' inside a function to the given object

we don't need that, but we do need something more general, namely, merge the following dict into the environment (closure) of this code


the dilemma posed by variadic functions, default keyword arguments, and cheap syntactic partial application:

i think i discussed this long ago but i thought about it again without looking at the other one, so now i'll have both lines of thought and can see if either was missing anything.

you want to be able to have variadic functions (functions that take a varying number of arguments. why? because otherwise you have to pass

f a b [c d e]

instead of

f a b c d e

not a big difference, but it can be convenient if you use the function a lot, e.g. for a 'switch' function; or if you want to make one function that can mimic many.

but to do cheap syntactic partial application, you want there to be no difference between on the one hand a function that take an argument and returns a function which takes an argument and returns a result, and on the other hand a function that takes two arguments and returns a result. if the function which returns a function is itself variadic, then you need to provide a way to keep it from eating both inputs so that the returned function can eat the second input and return a result, or alternately to tell it to eat both inputs and return a function.

in the latter case, you don't want it to eat the second argument by default, because then it doesn't interoperate with higher order function code which gives it some arguments but not all (todo: is this right?)

todo


hmm, so one option is to leave keywordness and variadicness open while we're lazy, but when the function is actually evaluated to close them. this seems like it may work in practice, but it seems kinda weird to introduce additional coupling between evaluation strategy and semantics (i say 'additional' because there is already semantic coupling in that a non-terminating function that is never evaluated does not cause the next level up to become non-terminating (which can only happen if a lazy strategy is applied; if you strictify that, the next level up won't terminate).

another option is to close them immediately by default, e.g. if f is variadic then if you partially apply f's varadic argument, by default it closes the variadicity there and the resulting function is not variadic.

--

we want people to be able to write separate implementations of functions which are to be used when the function is used strictly instead of lazily (because sometimes a different algorithm is more efficient in the strict case).

---

need to be able to have syntax like Python's "yield" generator-construction syntax. i guess it suffices to define sequences as data types implementing a 'head' and a 'rest' interface; when you call 'head' it returns the next fn, when you call 'rest' it computes the next element after the current head, puts it somewhere so head can get it, then puts a closure of itself into rest. The yield syntax (which allows the closure to be taken in the middle of the function, and puts the closure and the return of head in the same place) could be implemented as a label and a macro that searches for that label.

--

in the pattern syntax: consider adding a Haskellian syntax such as [[Int?]] for "list of list of Int" (in Haskell; in Jasper, [[Int?]] would seem to just mean "everything that you can reach in two hops from this node is an Int", e.g. without saying that we are dealing with 'lists', which have various properties but one of which is that all of their labels are integers). The principal seems to be that you make a graph, and any types attached to any parts of that graph are given implicit forall quantification.

If you wanted to really say "list of list of Int" in Jasper, you might have to say "Int -> Int -> Int", which is right associative, so "Int -> (Int -> Int)", which means, 'the labels on the arcs going out of the root are all Ints, and the targets are all of type (Int -> Int); and Int->Int means that the labels going out of those nodes are all Ints, and the targets are all Ints too'.

this would compile to

"root =^X Y X isa Int Y isa (Int -> Int) "

which would compile to

"root =^X Y X isa Int Y =^Q Z Q isa Int Z isa Int "

It's important to explain to Jasper newcomers that it's best to thing of the equals sign as an asymmetric "->", not as an equation!

i guess the above should also be able to be written

"root =^X Y =^Q Z X isa Int Q isa Int Z isa Int "

also, Haskell can use arrow stuff to say "(largest, smallest) = (maximum &&& minimum) xs". We should be able to generalize that to create any graph structure where the nodes each have code associated with them; then when an argument is applied, it is applied recursively to each node, to produce a new graph with the outputs of those code nodes as node labels.

--- call external programs as functions:

http://amoffat.github.com/sh/index.html

---

--

hmm, mbm distinguishing b/t attached and unattached is a bad idea:

"

Problems with optional parenthesis

Take a look at these two snippets. Next to the CoffeeScript? code is the resulting JavaScript?:

doSomething () -> 'hello'

doSomething(function() { return 'hello'; });


doSomething() -> 'hello'

doSomething()(function() { return 'hello'; }); "

-- http://ceronman.com/2012/09/17/coffeescript-less-typing-bad-readability/

--

that page also has:

" Now let’s take a look at a classic problem associated with implicit semicolon insertion in Javascript:

function foo() { return { foo: 1 } }

"

but i don't think we'll have that problem because we only combine multiple lines when there is an explicit open parens on the first line which is not at the end

--

"fold" could be foldl or foldr (fold! or not) depending on whether it is evaluated in a rec strict context or not. i guess foldr is the non-strict default, and foldl is the strict default.

this brings up the idea of "context" in general, which may select between many functions with one name, just as perspectives do. should be implemented as a perspective

also it shows that contexts and hence perspectives do have multiple facets (because you might be in a 'strict' context and also a 'sequential' context at the same time). really, i guess the type system can be used for this since it already has attributes.

perhaps each edge is hidden or shown depending on if it matches a type?

is this 'semantic' contextual polymorphism different from implementation polymorphism? maybe. or mb the only important thing is that the signature is absolute, the rest (semantic or just implementation) is contextual

--

i guess should use / for assignment and directed arc, not =. And use

for right-associative grouping, not /. e.g. "f a b / g a h b" means "f is a function of two variables, a and b, and it equals (g a) (h b)".

could be symmetric arc, or it could be function definition, or it could be (=== or isa). i'm thinking fn definition:

full: funcname / return args =[args] fn_body implicit args: funcname / return args = fn_body_with_implicit_args with no return args: funcname / =[args] fn_body_with_implicit_args anonymous: (= fn_body_with_implicit_args)

huh, so are we saying that we can't use "f a b = a + b"? it would be "f / =[a b] a+b"?

that's three extra characters. how about the normal args go on the left of the equals, and the return args go in the list:

   f / a b = a+b

now it's only 1 extra character (for naming the fn). that let's us assign multiple return args without [] on the left:

   f / a b =[add, mult] add / a+b : mult / a*b
   g / f 3 5                                    /// g == (f 3 5).add == 8
   h j / f 3 5                                 /// h == 8, j == 15
   k / f 3 5 : m / %mult                        /// k == 8, m == 15

note that if only one return argument is requested, the other return arguments are ignored, unless they are needed later with the magic % operator. whether or not the other arguments are computed is determined at compile time by seeing which %s appear after a function invocation and comparing to the function return arguments. Note that even if : sequencing is applied this works, it uses the order of the introduction in the source code. The scope of these hidden variables ends with the next close parens after the current assignment statement (which is lexically above it, of course; e.g. it ends with the assignment statments's environment --- or should it use 'named function scope'? i kind of like the latter..).

full: funcname / args =[return args] fn_body implicit args: funcname / =[return args] fn_body_with_implicit_args one anonymous return arg: funcname / args = fn_body_with_implicit_args anonymous: (= fn_body_with_implicit_args)

---

folds should have a default initial element depending on type

---

so far i still like the "default arguments and variadicity closed upon strictify".

---

need to remember to have a "garbage symbol" like Haskell's '_'. Both for 'any' in patterns, and for 'throw this away'. ... i think?

---

i guess we can use labels without ^ to label arcs in graphs and patterns:

[x A/ y : a.color='orange']

sets the 'color' attribute of arc A to 'orange'. Note that capital 'A' labels (defines) the arc, and binds lowercase 'a' to it. This is a special treatment of ALLCAPSLABELS within a data lexical context.

--

differentiating between attached and unattached symbols may be annoying, but it allows us to reuse the semantical connotations of symbols. e.g. "for;" could mean "sequential for loop", whereas "for" is by default parallel.

--

mb a.b could treat b as a symbol, e.g. if symbols are just strings, then it's as if you said "a.'b'". In that case '.' does more than just rearrange parens to surround the two arguments, which you could do with commas:

Python: write(handle, a[b(z)][c], apple(bananna)) Jasper: write handle, a ,, b z,, c, apple banannas

hmm, double commas don't really help much do they..:

Jasper: write handle, a (b z) c, apple banannas

now we can also use . unattached on the left as a symbol generator:

.harry = symbol 'harry'

--

multi assignment: now that we separated the assignment syntax from the fn args syntax, we can do

  a b c / f 3

to do a multiple assignment from the 3 output args of f.

in fact, we can generalize this to a pattern matching assigment, as if the lhs were implicitly a data lexical context (environment type):

  a [b c] d / [1 [2 3 4] 5 6]  /// a == 1, b == 2, c == 3, d == 4

or actually i guess the lhs is actually a pattern matching context: a [* [b] c] d / [1 [[[2 3] 4]] 5] / a==1, b==2, c==4, d==5 a .bob=b .harry=c / [1 2 .harry=3 .bob=4 5] / a==1, b==4, c==3

--

recall that inside a data lexical environment, which is space-separated, you can switch back to code context with parens.

--

if multiple array dims can be separated with many ;;;s, then need a function that generates the nth semicolons symbol.

-- hmm, can't use = for subassignment anymore, conflicts with fn defn. what to use? either = or / give parsing probs. but must be unshifted.

i guess we have all of the arith ops free. mb '-'? but we want dashes allowed in names.

hmm would be nice just to use . But that breaks the idea of having ++ -- be normal arithmetic ops.

mb a combo? like /.? or /= ? /. is ez to type, but it makes the argument look like a symbol.

  a .bob/.b .harry/.c / [1 2 .harry/.3 .bob/.4 5]  /// a==1, b==4, c==3

mb mk fn assignment more complicated, like :=, or require the []:

full: funcname / args =[return args] fn_body implicit args: funcname / =[return args] fn_body_with_implicit_args one anonymous return arg: funcname / args =[] fn_body_with_implicit_args anonymous: (=[] fn_body_with_implicit_args)

f / a b =[] a + b

def f(a,b) = a+b

or could always use backticks:

  a .bob`b .harry`c / [1 2 .harry`3 .bob`4 5]  /// a==1, b==4, c==3

but they aren't in Italian keyboards: http://spaghettidba.com/2011/09/19/typing-the-backtick-key-on-non-us-keyboards/

or we could use '==', and mb switch the use of = and / once again, and use 'is' and 'eq' instead of '==' and '===':

full: funcname = args /[return args] fn_body implicit args: funcname = /[return args] fn_body_with_implicit_args one anonymous return arg: funcname = args /[] fn_body_with_implicit_args anonymous: (/[] fn_body_with_implicit_args)

  a .bob==b .harry==c = [1 2 .harry==3 .bob==4 5]  /// a==1, b==4, c==3

or we could use '==' for fn defn:

full: funcname / args ==[return args] fn_body implicit args: funcname / ==[return args] fn_body_with_implicit_args one anonymous return arg: funcname / args ==[] fn_body_with_implicit_args anonymous: (==[] fn_body_with_implicit_args)

  a .bob=b .harry=c / [1 2 .harry=3 .bob=4 5]  /// a==1, b==4, c==3

or we could use '/' for fn defn and = for subassignment and == for assignment:

  a .bob=b .harry=c == [1 2 .harry=3 .bob=4 5]  /// a==1, b==4, c==3

full: funcname == args /[return args] fn_body implicit args: funcname == /[return args] fn_body_with_implicit_args one anonymous return arg: funcname == args /[] fn_body_with_implicit_args anonymous: (/[] fn_body_with_implicit_args)

nah, we want subassignment and assignment to be the easiest to type. but we want assignment to be easier to see. how about:

  a .bob/b .harry/c = [1 2 .harry/3 .bob/4 5]  /// a==1, b==4, c==3

full: funcname = args ==[return args] fn_body implicit args: funcname = ==[return args] fn_body_with_implicit_args one anonymous return arg: funcname = args ==[] fn_body_with_implicit_args anonymous: (==[] fn_body_with_implicit_args)

now if we use 'is', though, then there is no obvious way to generalize that to user-specified assignments. otoh its clearer than === vs. ==.

could use ``s to enclose arguments:

full: funcname = args `return args` fn_body implicit args: funcname = `return args` fn_body_with_implicit_args one anonymous return arg: funcname = args `` fn_body_with_implicit_args anonymous: (`` fn_body_with_implicit_args)

hmmm i like that a lot.. it makes it a lot easier to type. mb have a workaround for ppl without backticks: [/ ]

otoh i also like the Haskell way of using backticks to infixify. but this doesn't need a grouping character: we could just use a single char as prefix for that, e.g a ^member b.

now that we're not using =s anyhow, and we have to type `` no matter what, let's use it for arguments, rather than return args:

full: funcname = return args `args` fn_body implicit args: funcname = return args `` fn_body_with_implicit_args one anonymous return arg: funcname = `args` fn_body_with_implicit_args anonymous: (`` fn_body_with_implicit_args)

  a .bob/b .harry/c = [1 2 .harry/3 .bob/4 5]  /// a==1, b==4, c==3
  f .bob/3 .harry/4

note that now we can still use a/b to denote a directed arc in data.

note: could also distinguish between attached and unattached = for keyword vs. other things

--

use == for 'is', and =eq for equals. having both == and === is too confusing.

--

so we have at least three cool metaprogrammy things so far:

--

todo:

for now, it seems that:

--

mb + could be cons. mb it could also be string append.

mb it could also be 'merge with overwrite' on general nodes. e.g.

  [.a/3 .b/5] + [.a/4 .c/6] == [.a/3 .b/5 .c/6]

note that in this case + only has a partial inverse, e.g.

  [.a/3 b/5 c/6] -+ [.a/3 .b/5] == [.c/6]

+ could also be 'merge with multiset':

  [.a/3 .b/5] + [.a/4 .c/6] == [.a/3 .a/4 .b/5 .c/6]

which is better?

mb we could have a "destructive" lexical operator, mb

  [.a/3 .b/5] &+ [.a/4 .c/6] == [.a/3 .b/5 .c/6]

this doesn't match list append tho:

  [' a b c] == [0/'a' 1/'b' 2/'c']
    so
  [' a b c] + ['d e f] == [0/'a' 0/'d' 1/'b' 1/'e' 2/'c' 2/'f']
    but we wanted
  extend [' a b c]  ['d e f] == [0/'a' 1/'b' 2/'c' 3/'d' 4/'e' 5/'f']

mb one of them is + and the other is ++.

++ is commutative? so + should be list extend?

and cons = `x y` [x] + y

hmm so 'list cons' seems pretty different from 'node merge'. the latter only affects arcs with the same name as in the stuff being added, and also is more like 'extend' than 'append', whereas the former is more like 'append' than 'extend' in that it wraps one of its arguments in a new node, and also it touches lots of arcs, as many as it needs to 'make room'.

so we really have three things here:

node merge, list append (cons), list extend

and of course there's the question of whether cons should go at the head or at the tail. thinking of cons as an 'append' it should go at the tail, but traditionally it goes at the head.

---

todo: search code analysis tools for common error patterns and make sure it is at least conceivably possible for some sort of analysis tool to catch the corresponding error in Jasper, perhaps with the aid of an annotation discipline:

http://www.altdevblogaday.com/2011/12/24/static-code-analysis/ http://www.coverity.com/ http://randomascii.wordpress.com/category/code-reliability/ http://www.viva64.com/en/developers-resources/


Coffeescript .? Operator: a .? b appears to mean (if a then a.b else Nil), that is, it's like a.b except it returns Nil (or mb False?) if a is Nil. it's b/c this is a common pattern, where you have to check if an object is nil before dereferencing it

mb we'd use .~ or ~.

---

"all" form of multiple inheritance where when a method is invoked which is not overrided, or when super is called, ALL of the parents which match that method are called, rather than just one (the "any" form, which Python uses)

---

it's nice that Python boolean ops on non-booleans return non-booleans, e.g.

  highStrike or ceil(current_price)

returns highStrike if it is strue or ceil(current_price) otherwise


todo: how to attach a label to a binary operator?

e.g. from a / b to

  a Label / b

should we allow it to look like we are setting one of the args of the operator?

  a Label/ b

should a Label / b

be enough?


in particular the pattern matching on multiple return arguments should allow syntactically lightweighted named multiple return values (matched via name not via order). no more returning a long list of arguments and getting bugs because you didn't have the arguments in quite the right order in the calling function!

---

should be easy to say "this next bit of code is in Java", and Jasper transparently figures out that it needs to spin up a Java interpreter (of course this is expensive, that's ok), establish a connection to it, create copies of any preexisting variables referenced and their values (e.g. if the Jasper code has said "x = 3" and then the java code refers to an "x", Jasper should create a variable x in the Java environment and set it to 3), and then get the returned data out at the end. maybe it's okay to only allow complete functions in Java/other languages, so that you only have to copy in values into function params and copy them out as returns from functions :)

in other words, jasper should be a true 'glue language'

this ideas of non-native functions, non-native namespaces, references to non-native data, the concept of code in a foreign language, the concept of other interpreters and their states and connections to them, should all be easy to express in Jasper.

it should interoperate with JVM, Python, Javascript, C, C# (.net), and shell. i think Clojure provides interoperability in all of these but C and Python, so writing a Jasper implementation for Clojure would be a good start here

---

jasper isn't really into backwards compatibility, but just as it interoperates with JVM, Python, Javascript, .net, and C in the sense of being able to consider modules in those languages as libraries and to pass data in and out of functions and objects in those languages, it should similarly interoperate with past and future versions of itself. so, if a programmer is willing to tolerate the resource overhead of multiple Jasper interpreters running in order to run a single Jasper program, then they don't have to upgrade their code in order to take advantage of Jasper libraries written for a newer version of Jasper -- their code, which only works with an older version of Jasper, can call libraries written for a newer version of Jasper.

---

http://worrydream.com/LearnableProgramming/ is very interesting. Jasper should support this sort of stuff.

(and obviously it should support a REPL)

---

better interoperate with

http://thinkaurelius.github.com/titan/

and cassandra

---

todo: really need to take a look at https://github.com/tinkerpop/gremlin/wiki


woah...

http://c-faq.com/decl/spiral.anderson.html

(i saw this from an essay that said that Go is "Typed (important for JIT and IDE’s) but not cumbersome and ugly like C or C++’s spirals." -- http://uberpython.wordpress.com/2012/09/23/why-im-not-leaving-python-for-go/ )

---

should generalize the notion of 'pattern' and 'pattern matching' to let programmers extend/write their own pattern language, and reuse this machinery for other things (e.g if you want to compile a program in one language into another language, you'll be doing a lot of searching for patterns in the parse tree of the source)

---

i guess if guards can have multiple lines and variable defns that refer to each other, these variable defns should not be allowed to be recursive, b/c then otherwise merely evaluating the guards would be a Turing-complete constraint satisfaction problem? (not sure about that, i'm forgetting my recursion theory; are a set of recursive equations sufficient for recursive computation or do you need an unbounded minimization operator too? i think the former..)

--- working on generalizing pattern matching to make it customizable:

i guess in general a successful pattern match is a graph homomorphism from a subset of the graph of the pattern template to the graph of the value being matched. (i say "subset" because some parts of the template may be optional components that are matched to nil). That is, a successful match assigns values from parts of the value being matched to parts of the pattern template.

is the homomorphism requirement absolute, or is it really a homomorpishm to some function of the value being matched? let's leave the homomorphism requirement as absolute for now, so that the defn isn't so unconstrained such that it barely means anything. we'll remove it later if that proves more convenient.

so a pattern match syntax could be defined by a predicate that recognizes valid pattern matches. This then induces a (possibly non-unique) function that goes from pair (template, value) to either (False, nil) or (True, match (e.g. assignment of pattern template variables to parts of the value being matched)); or, in 'all' mode, a unique function from (template, value) to set(all matches).

an implementation of a pattern match semantics is then an actual function that realizes the above function. useful classes of pattern match syntaxes/semantics could be defined by generator functions which walk the pattern and value being matched in the manner of e.g. regular expressions, and match according to programmer-given criteria

are we really, in general, replacing all assignment statements with pattern matching and treating the lhs as a pattern literal (which can have subpatterns interpolated via '$')? so far i think yes. how to specify which pattern semantics we are using? =@? =#?

---

should @ and # even be different?

---

maybe for didactic purposes use the word "network" instead of "graph"

---

actually i guess the pattern labels are the most common case, so normal variable identifiers in patterns should be pattern labels, not caps. should caps be constants inserted into the pattern, or should $var be that? and the other one should be pattern interpolation.

so, note, there are three things here:

--- note: boundaries/barriers can be thought of as a form of 'named quoting'; when you e.g. interpolate an expression template into an expression template that will be substited into to form a regexp expression template that will be substituted into to form a regexp subexpression that will be interpolated into a regexp, you have to worry about how many quotes go where, based on how many times the thing will be substituted into as a template before it gets to its final destination. instead, you can use named quoting to say upon each substitution/interpolation that only the things that match the current name will be resolved, the rest are left quoted.

---

i guess four common operations are substituting into (interpolation; evaluation of a function is a special case i guess), pattern matching (assignment is a special case i guess), quoting the value of a symbol, and just letting the symbol represent the value of a symbol with that name, or a string whose content is the symbol's identifier.

quotations can be nameless or have names (boundaries).

---

so... if assignment is a special case of pattern matching, then it can fail. how to represent that? i guess we are being dragged towards letting assignment be an operator within an expression after all. yuck.

if it's not within an expression, then a failure raises an error?

hmm.. maybe a failure always raises an error, unless you use =~ (or is it ~=). then, as usual, the ~ transforms the error into a nil.

hmm.. i like that much better. then we can say that = may not be used within an expression.

---

must support Python-style multiple inheritance which allows the 'mixin' approach to object decomposition

i think we already support this..

---

must support Python generator syntax including something coroutiney like 'yield'

---

must generalize and support something like Python list comprehensions.

the genius of list comprehensison is that it combines a map and an optional filter (the user passes in two lambdas), but you don't have to type out the anonymous function syntax, and you only have to enter the names of the anonymous function parameters once.

mb we could do something similar by using default variables?

  [x+2 for x in list if x > 0]
  map _ + 2 | filter _ > 0 | list

still too long, let's define a function 'lcomp' that combines map and filter:

  lcomp, _ + 2, _ > 0, list

hmm, looks pretty good.. note that lcomp is variadic, though, b/c the filter is optional

---

it's way annoying that Python encourages you to name all your files '__init__.py', because that makes it hard to see which project is being talked about in contexts when you just see a filename (e.g. emacs buffer switching)

---

it's irritating how Python tracebacks include the method name but not the object; errors during initialization often give tracebacks where most of the places are just called "__init__" with a line number

but in Jasper, what is the equivalent of object name anyways?

---

in Python, i made a method called "meth". Then i passed it to another function. When it wanted the value, the other function called it as "self.meth()".

but then i thought it should be an attribute. so i used Python's property decorator. But now if i pass it to the other function as "self.meth", that evaluates it at the time of passing, not later, when the other function needs it.

in Python, i should either go back to it not being an attribute (the other fn will call it with () when it wants it to evaluate). In Jasper, how would one handle this? is it just a-ok b/c it's lazy anyhow, or is it more complex than that?

---

Python's way of evaluating default arguments only once when the function is defined is really annoying, because if you set a default to something mutable like [], then you can run something and create a new thing and think you're starting fresh when in fact the old list is still sitting around and will be reused. This leads you to have to do anoying things like this all the time:

def fn(..., currentUniverseChangedCallbacks = None, ...

        if currentUniverseChangedCallbacks is None:
            currentUniverseChangedCallbacks = []

instead of just

def fn(..., currentUniverseChangedCallbacks = [], ...

---

i often have the bug in python where i accidentally shadow a variable, e.g.:

def fn(a, b): r = [blah(a) for a in b] return a + b

(a is shadowed in the list comprehension, and then a + b is the last value of a in the list comprehension, not the parameter passed into fn)

---

common copy-paste things that should work:

copy and paste from fn defn to function call

--- in Python, you can do:

  raise ExceptionClass(args)

or just

  raise ExceptionClass

---

another annoyance about pandas: Series has 'map' for elementwise map, but DataFrame? calls it 'applymap'

---

instead of having not None, not 0, not "" all eval to false, mb another operator (null, nilish)? or just isnil.. or mb not..


ReconnectOnExecutionErrorBrokerWrapper? is pretty ez in Python but could be even easier with some 'delegate' stuff in the std lib


hmm i guess ReconnectOnExecutionErrorBrokerWrapper? is like a 'boundary' around some code such that when you pass thru the boundary, the function you call is wrapped. connections to monads? connections to boundaries? connections to exception/maybe tower? should perspectives change when you cross a boundary too?

---

a useful constraint is: after initialization (but not during), variable x is guaranteed to contain a value of type y

---

need to have things like pandas.dataframe.combine_first, pandas.dataframe.join, and what about pandas.dataframe.combine, pandas.dataframe.combineAdd, pandas.dataframe.merge?

TODO need a better syntax for these sort of things does R do it well?

---

shell echo execution-like (-x ?) mode

---

pandas Series.name attribute allows

df = DataFrame?([[1e-7,1], [2,3]], columns=['a', 'b']) col = df1['b'] col.name == 'b' df2 = DataFrame?(col)

and the name of the column in df2 is the same as it was in df1.

this is useful... it's a form of metadata (the node label of the root node) which should be in some perspective, and which should vary by perspective (e.g. depending on which graph you are looking at, the same node can have different labels).

--

def compose(a,b): # why dosen't this Just Work when you say a[b]? return reshape(ravel(a)[ravel(b)], shape(a))

def compose_assign(orig, idxs, new_values): """ note: this assigns to orig, not just a copy of orig """

	flat = ravel(orig)
	flat[ravel(idxs)] = ravel(new_values)
	return reshape(flat, shape(orig))

def compose_identity_for(a): return reshape(range(len(ravel(a))), shape(a))

---

assertion should print (a) the source code of the assertion expression, (b) the current values of each variable in that expression, (c) optionally, a user-specified string (which may have interpolated variables)

---

in Python:

--> 432 if D_valid_for_normalization: 433 # now again with the actual D and D_valid

    434 

ValueError?: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

occurs if D_valid_for_normalization contains a non-scalar e.g. a matrix. this defeats the purpose of "if" having a concept of things that eval to False. This should be either treated as if you asked "if D_valid_for_normalization is None" or as if you asked "if D_valid_for_normalization is None or all(D_valid_for_normalization) == 0"

---

functions:

findall in list find1 ('index' in python), findall in dict (bidirectional dict) invert dict


some loosely related concepts that may be more tightly related:

levels, meaning

hypercard difficulty level

voice control input choice prior probabilities

meaning as changing the UI representation of choices

voice in concert with text on the computer to teach kids to read, or as assistance to blind folks


need a quicker way to write

        print 'shape(x) = %s, shape(y) = %s' % (shape(x), shape(y))

this sort of contraption should be easier to write:

    minloc = None
    mindist = Inf
    for i in range(k):
        if mindist == 0:
            break
        for j in range(k):
            dist = df1.index[i] - df2.index[i]
            if dist < 0:
                continue
            if dist < mindist:
                mindist = dist
                minloc = (i,j)
                if mindist == 0:
                    break
                

--- jasper needs to do this stuff well:

http://www.drbunsen.org/explorations-in-unix.html


"Third: obviously I got Kelly’s joke about “streams of bytes”, uh, that’s why I quoted it. It’s funny, and it makes the point (which I fully agree with) that the decades-old Unix “pipe” model is just plain dumb, because it forces you to think of everything as serializable text, even things that are fundamentally not text, or that are not sensibly serializable." -- http://regex.info/blog/2006-09-15/247#comment-3085

just copied here to remind me that we want to include something like Unix pipes, but with arbitrarily structured data (e.g. graphs) in them


need syntactic sugar to earily passthru all vars in formal arguments, e.g.

there should be an easier way to do

  def subrountine1(a, b, c, d, e=0, f=0, g=0):
    ...
    subrountine2(a,b,c,d, e=e,f=f,g,g)

---

need an operator for beginning evaluation of a future

---

Named quoting is like unifying dicts and lists into nodes

Similar problem defining append vs merge? Relevance to handler towe?

---

http://docs.oracle.com/javase/6/docs/jdk/api/javac/tree/index.html

---

" For instance, in Go everything is passed by value. If you want to pass a reference, you must pass a reference type (like a pointer). This means that you can pass around structs without having to put them on the heap. In JVM-land, nearly everything is passed by reference, and the data must go on the heap.

Relatedly, in Go if you allocate an array of n values of type T, the size of that array in memory is n * sizeof(T), and the items are laid out sequentially in memory. There are no other bookkeeping data structures and no indirection.

Here's a story about one project's struggle with data structures and Java: http://marc.info/?l=git&m=124111702609723

Relevant quote:

    JGit struggles with not having an efficient way to represent a SHA-1.
    C can just say "unsigned char[20]" and have it inline into the
    container's memory allocation.  A byte[20] in Java will cost an
    *additional* 16 bytes of memory, and be slower to access because
    the bytes themselves are in a different area of memory from the
    container object.  We try to work around it by converting from a
    byte[20] to 5 ints, but that costs us machine instructions.

In Go, you would just write [20]byte. If you wanted a struct with a field of [20]byte you can do so, and all it costs is the 20 bytes.

reply

Locke1689 2 days ago

link

The JVM does not support this, but the CLR does. "

" Go's memory model has similarities to C. For instance you can use the heap or the stack. Go figures out which one an allocation should live in by doing escape analysis. If it never leaves the function it goes on the stack. If it does it goes on the heap. GC ensures anything on the heap gets cleaned up. It's not an API.

reply

Locke1689 2 days ago

link

Both the JVM and the CLR do this. This is not a Go technology.

In fact, in certain cases the JVM can detect type information and remove the allocation entirely by hoisting integer fields and the like into registers.

reply

pcwalton 2 days ago

link

To be fair, Java does escape analysis for this too. This isn't a difference between Java and Go.

reply

ericbb 2 days ago

link

It's not about escape analysis.

The programmer explicitly specifies when to use pointers. Whenever pointers are not used, the object is stored in-place (for example, on the stack, in the array or slice, or in the struct).

reply "

" Does Java do coroutines/green threads? No. At a deep architectural level, the segmented stack required to do green threads is what sets Go apart. And if it wasn't for this, Go could have just been another JVM language probably. I guess they felt this was important enough to justify building their own runtime. "

" You should really write a GO code to understand what the author means. Writing GO code has no frills. And I have seen nothing cooler than: http://gofmt.com/.

reply

pron 2 days ago

link

Even if that's the case, Go just brings too little to the table. And if you think gofmt is cool, take a look at Project Jackpot [1], or its use in the NetBeans? IDE [2].

[1] https://bitbucket.org/jlahoda/jackpot30/wiki/Home

[2] http://netbeans.org/kb/docs/java/editor-inspect-transform.ht...

reply

zaphar 2 days ago

link

Those aren't in the stdlib. gofmt is. So is gofix, go vet, godoc and a host of other things. Batteries included means a lot.

reply

chimeracoder 2 days ago

link

> Those aren't in the stdlib. gofmt is.

Which also means that you can count on any Go code you find online to be formatted the same way too.

It's like PEP 8, except better, because it's a lot more widely enforced.

reply "


Kotlin uses the word 'trait' to mean something like 'stateless class'

---

Kotlin's 'when' is a better 'switch':

when (obj) { is String => obj[0] is Int => obj + 1 !is Boolean => null else => 0 }

when (x) { 0 => "Zero" 1, 2, 3 => "1, 2 or 3" x + 1 => "Really strange" in 10..100 => "In range" !in 100.1000 => "Out of range" }

see.. each case seems to be guarded by the application of a 1-argument predicate that takes x as its argument to x.. in C, the 'switch' statement is a special case of this 'when', but with the predicate set to ==

---

Go-style green threads ('goroutines') and channels look good

note that Go channels are synch (unbuffered) by default, but can have buffers too

also, should add select capabilities to allow selection of certain msgs within a channel, like Erlang.

also, i guess channels could be specified as 'any' or 'all', in that if there are multiple recipients listening to the channel, should each message go to each recipient, or should each message only be received by the first recipient to take it?

maybe should us http://zguide.zeromq.org/page:all as a guide to how channels should work? in fact, channels should probably be implemented via zeromq and have a defined zeromq behavior, making interop easier. Apparently Mongrel uses zeromq for inTRA-process communication and apparently zeromq is designed to be used this way, so it could work -- dunno if that's true tho, i only read it in a blog.

---

thinking about things with multiple entry arguments and multiple exit points that act as 'antibodies', e.g. trigger when certain conditions are true (e.g. the entry points have guards): in traditional strict languages if you say

y = f(x)

then it means, evaluate f(x) right now. In lazy languages it means 'if you ever need to know what y is, then evaluate f(x)'. Then we have futures, where

y = f(x)

means 'call f(x) now and if it returns something, put that into y, and until then, put an IOU into y'

And we have goroutines and channels, where instead of doing

y = f(x)

if you want y to have a future, you make a channel, put it into y, and pass y to x:

y = make(chan) go f(x, resultChannel=y)

(this seems clumsier to me than futures)

So what if f is one of multiple entry points, and the f has guards (that span multiple entry arguments)?

You might want to call f and wait for the return value. Or you might want to call f in the background and then go on and do other things.

i guess futures (and goroutines) are in between lazy and eager; with lazy, you want to call the guy later; with futures you want to call the guy now, but you don't want to block until it returns; with eager, you want to call the guy now and block until it returns. So i guess we need syntax to specify this.

maybe one ! for a future, two !!s for eager execution?

so

y != f x

for the future, and

y !!= f x

for eager execution?

the future can be a synonym for the obvious channel idiom (create a channel, assign it to y, call f in a green thread which will put the result of f into the channel), and we can still provide channels..

---

need a cheap syntactic way to indicate which of multiple return arguments you want to take. should mirror fn calling (e.g. can use positional encoding or keywords to indicate)

i guess if lhs is special then we can give commas special syntax in the lhs even if they are being used for grouping in normal expressions.. instead of letting commas just indicate tuple construction, as in Python, let them indicate argument separation hmmm actually arguments can just be space separated, no need for commas and you can have keywords too

e.g.

def y1 y2 y3 = f x y: y1 = x+y y2 = x-y y3 = x*y

myy1 myy2 = f 3 4 myy1 == 7 myy2 == -1

myy1 myy3=y3 = f 3 4 myy1 == 7 myy3 == 12

note that attached = is syntax for keyword arguments, and unattached is assignment (and fn defn, although i think i had some better ideas on that earlier, see above)

ah yes.. i looked above, my earlier idea was:

f = y1 y2 y3 `x y` y1 = x+y y2 = x-y y3 = x*y

myy1 y3/myy3 = f 3 4

that seems nice.. recall that an lhs is a pattern match.. (and note that we are using y3, not .y3, b/c in lhss we assume that the labels are not dynamic.. although we should still have some other syntax to capture all the outputs of f as a graph and then operate on it like normal..hmm.. maybe ^= ?)

is there any way we could still use computations in an lhs? yes, i think so:

g = 2 x # implicit argument x here.. y = g y3/myy3 = f 3 4

this will assign y = g(f 3 4) = 2*(3+4)... you see, the middle guy (the first pattern match) assigned to variables and then passed the pattern onto the second pattern match (the guy on the left)... but since g is a fn, not a variable, it took 7 as an argument... hmm wait a minute that can't work we need to disambiguate fn execution from variable assignment.. hmm..

what we're looking for here might be something like Haskell arrows, a way to conveniently chain together functions while rerouting multiple return arguments to multiple input arguments in between them (if that's what arrows do, i dont understand them yet)..

i guess what we're missing is the concept of graph paths, or graph addresses.. we want to say, 'take the third return value of f and route it to the second input of g'; we have above a way to refer to the third return value of f, but not a way to refer to the second input of g (nor do we have a way to specify the inputs of g separately in separate places, and to collect its outputs later). The quickest way to do this right now would be:

myy1 y3/myy3 = f 3 4 r = g 1 myy1 3

and the question is, can we (and should we) think of a syntax to collapse that onto one line?

howabout only in the special case where g only has a single free input?

then we could do something like:

r =

(g 1 _ 3) y3/myy3 = f 3 4

also, would like it if you could return multiple return arguments with short-circuit evaluation that stops when all the requested return args have been returned. that's kinda like laziness.. of course, if the function has side-effects it might want to ensure that it gets to finish what it's doing before any returns.. hmm mb that could be detected automatically (e.g. side-effectful fns finish before returning anything, pure fns stop when they return the desired argument(s))..


consider a '2D' language that associates to the right by default and primarily uses newlines to indicate multiple arguments: http://chrisdone.com/z/ http://news.ycombinator.com/item?id=4992603

note: could use right associativity by default but use commas if you want to put stuff on the same line after all

or, heck, could just use some other special syntax to say 'interpret this line as left-associative' even if the default is right-associative (in Z the '#' does this)

(i guess Haskell's '$', or my '

', is the analog of the comma proposal here if you want left-associative to be the default)

however, the Z guy says this would be missing the point: " Regardless, you can pretty much ignore all I said above, because it's just an aesthetics thing, kind of irrelevant to the core concept. The whole point of the indentation and right-associative application is for the Markdown-inspired macros: everything after the name is up for grabs for a macro (or special operator). The idea of adding a comma pretty much negates that and the whole point of z-expressions. If I could have commas, if I were to make that concession, I'd just have parentheses, and use s-expressions and reader macros. "

so, for him, the point of right-associativity is not that it's nicer in general, just that it makes macros simpler

--- actually, i really like the idea of having a syntax that makes its scope right-associative (or left-associative). which operator shall it be? mb

?

and could use

for 'right-associative until the end of the stanza (until a line which is only whitespace)

--- "

> Type inference is a huge productivity win.

When writing code, sure. When reading and/or maintaining code, not so much. Especially when it stretches across function or method boundaries. Languages like C++ and C# have the right happy medium in my opinion: `auto`/`var` keywords for type-inferenced local variables, but explicit parameters and return types for all methods.

"

hmmm... interesting idea to make fn args and returns require an explicit type declaration, and local vars not requiring one..

---

to take the wrong lesson from http://chrisdone.com/z/ (the right lesson is something about right-associativity enabling simpler macros, not sure i understand that though):

could use ' to mean string-until-the-next-EOL, or:

print 'hi there dudes instead of print "hi there dudes"

more generally, could have a character (like ') that makes ANY subsequent grouper automatically go until the EOL:

'[ a b c --> [a b c]

and if there is only a ', it's the same as , that is, string until EOL

otoh above we had .bob short for "bob". Maybe 'bob goes fishing should be short for "bob" goes fishing and bob goes fishing should be short for "bob goes fishing"

but then we need something else for lisp's quote:

http://stackoverflow.com/questions/134887/when-to-use-quote-in-lisp

(and lisp's backquote, e.g. quasiquotation, btw)

---

actually, could use angle brackets for fn args...

f = y1 y2 y3 <x y> y1 = x+y y2 = x-y y3 = x*y

hmm.. harder to type for US keyboard, tho (shifted). i guess if you're gonna do that you may as well use curly braces.

f = y1 y2 y3 {x y} y1 = x+y y2 = x-y y3 = x*y



Lisp's cond:

(cond (condition1 expression1) (condition2 expression2) (condition3 expression3) )

i think it goes thru until it finds a condition that matches, if any (instead of 'else', you can make condition3 = true ('t', in Lisp terms)


:before and :after methods (from CLOS, i think)


multiple dispatch


continuations


STM (software transactional memory) (clojure's 'dosync' blocks)


Lisp's ability to basically add new 'block-level' language features, presumably via macros, e.g. Clojure's dosync blocks


laziness


"generic setters"

landoflisp says:

with fn inverses you could do this for real, e.g. say

(head x) * 2 = 60

and have it use the inverse of * to deduce that you want to set the first element of x to 30. Of course, if there are multiple variables, you'd have to mark the argument that is ultimately being mutated:

y = 2 (head @x) * y = 60

so that it known to use the inverse of the * fn which holds the second argument constant and solves for the first argument

if we have this, why stop there? we should be able to accomodate multiple inverses:

sqrt(@x) = 2

should yield x containing a choiceset (choiceset being my name for something which is a set but which acts like a single element, using 'apply' and somesuch) {2, -2}

and we should be able to get sets of assignments to multiple variables, even infinite ones:

@x + @y = 3

which should yield some sort of situation where x + y = 3, but it could be x=1, y=2, or x=2, y=1, etc.

---

"

c141charlie 2 days ago

link

Go nails code readability and documentation better than any other language I'm aware of.

For example, look at the package documentation for Go's list data structure at http://golang.org/pkg/container/list/.

5 seconds of reading this you immediately get what the package does and how to use it. Now let's say you want to know how the list is implemented. No problemo. Click the package files link list.go, http://golang.org/src/pkg/container/list/list.go, and you're presented with very readable source code.

Now compare this with Java. I just Google'd Java List. First link is this: http://docs.oracle.com/javase/6/docs/api/java/util/List.html

Documentation looks OK, but wait, this is the interface. I want to see docs for a concrete implementation. I'm tempted to click on the AbstractList? link, but oh wait, that's just another non-concrete class that other List classes probably inherit from. Let's see, let's go to the ArrayList? ... this looks good. http://docs.oracle.com/javase/6/docs/api/java/util/ArrayList...

Nice. I wonder how they implemented this. And I'll keep wondering because I can't find a link to the source code. Maybe there is a link to it, maybe not. We're talking about Oracle so without knowing better, I'll assume there is not ...

Let's try Scala. Google "Scala List". Click on first link. http://www.scala-lang.org/api/current/index.html#scala.colle...

What the fuh is a "sealed abstract class List[+A] extends AbstractSeq?[A] with LinearSeq?[A] with Product with GenericTraversableTemplate?[A, List] with LinearSeqOptimized?[A, List[A]]"

Oh sweet, this thing has all kinds of methods, i.e. ++, ++:, +:, /:, :+, ::, :::, :\

Reading further, I see section in the documentation called "Shadowed Implict Value Members". Wow, I have no idea what that is.

Looking back to the Go documentation, I immediately "relax" as another commenter put it.

For some reason, I think Scala will end up being the next Java. It has so much momentum, runs on the JVM, has seamless interop with Java code. Has the Play Framework, AKKA, and thousands of other awesome libraries written for it. And if Scala powers Twitter, then I think this answers the scalability and concurrency question.

While I'm bullish on Scala, at the end of the day, I find Go's simplicity make it more beautiful than any other language.

reply

rorrr 2 days ago

link

> Go nails code [cut] documentation better than any other language I'm aware of.

That's because you haven't seen PHP's documentation.

Look at your Go's list example.

It doesn't even show how to create a list and fill it with items.

It doesn't have users' comments.

It doesn't explain much about the data structure. Can it be a circular doubly link list, for instance?

Is there a method to empty the list, or quickly insert more than one element?

Instead it has strangely named sections (like "type Element") that aren't obvious to somebody new to the language.

Sorry, but this documentation is shit. "

---

apparently the 'generic setters' functionality can be added to a function by: defsetf:

" You can use the "defsetf" facility so that

        (setf (annoyingness something) 1000)

expands at compile time to

        (set-annoyingness-of something 1000)" -- http://lists.warhead.org.uk/pipermail/iwe/2005-July/000130.html

"

        The book _Paradigms of Artificial Intelligence Programming_,
        by Peter Norvig, includes a section titled "What Makes Lisp
        Different?" that describes seven features of Lisp. Perl shares
        six of these features.

Which one is missing? "Uniform syntax." "

note: in Lisp, you can mimic the inverse addition to generic setters that i was talking about if there is only one input in the expression to be inverted; e.g. for sqrt:

        (defmacro set-sqrt (place v) `(setf ,place (* v v)))
        (defsetf sqrt set-sqrt)

now you can do

        (setf (sqrt something) number)

"

But to enable all this, setf must be able to reliably analyze its argument and decide what it looks like. Lisp's uniform, simple syntax renders this possible. In Perl, setf would have to take apart a string and figure out what all the punctuation meant, and hope that nothing had been redefined in a weird way, and hope that no weird syntactic exceptions had come up (ha!), and so on, and so on. "

-- http://lists.warhead.org.uk/pipermail/iwe/2005-July/000130.html

---

http://readable.sourceforge.net/

(Awkward) S-expression

(define (factorial n) (if (<= n 1) 1 (* n (factorial (- n 1)))))

(Improved) Sweet-expression

define factorial(n) if {n <= 1} 1 {n * factorial{n - 1}}

" We have three tiers, each of which builds on the previous one, as described in [Solution]. We have devised them to try to meet our [Goals]. First, let's discuss why these three tiers.

Each tier improves the notation, but has a trade-off; by creating three tiers, people can choose the tier they are comfortable with, yet use them together:

    Curly-infix-expressions (c-expressions) add infix notation, the notation people are trained in and most programming languages support out-of-the-box. This notation uses {...}, which are not used by many Lisps (including Common Lisp and Scheme), so in most cases there is no compatibility issue adding this. It's also trivial to add to Common Lisp (just modify the readtable). Thus, this creates a simple "first step" that people can adopt without concern.
    Neoteric-expressions (n-expressions) allow people to use a function name outside of the parentheses, which is the notation most people are taught in school and is used by most programming languages. This involves a subtle incompatible change in most Lisp readers, so a few might hesitate doing this. But code where this difference matters is considered extremely poor style, and is unlikely in modern code - and a pretty-printer would eliminate any such problems (just apply it first). Neoteric-expressions build on curly infix, since the prefixed {} requires handling curly infix.
    Sweet-expressions (t-expressions) add indentation support, eliminating the need for many parentheses. However, this adds another subtle change, and some people won't want indentation to be relevant. By making this a separate tier, people can adopt neoteric-expressions if they don't want meaningful indentation."

rationale for curly infix w/o precedence: " Curly-infix-expressions (c-expressions)

The reality is that nearly everyone prefers infix notation where it's traditionally used. People will specifically avoid Lisp-based systems for some problems, solely because they lack built-in infix support. Even Paul Graham, a well-known Lisp advocate, admits that "Sometimes infix syntax is easier to read. This is especially true for math expressions. I've used Lisp my whole programming life and I still don't find prefix math expressions natural." Paul Prescod remarked, “[Regarding] infix versus prefix... I have more faith that you could convince the world to use esperanto than prefix notation.” Nearly all developers prefer to read infix for many operations. I believe Lisp-based systems have often been specifically ignored even where they were generally the best tool for the job, solely because there was no built-in support for infix operations. After all, if language creators can’t be bothered to support the standard notation for mathematical operations, then clearly it isn’t very powerful (as far as they are concerned). So let’s see some ways we can support infix, yet with minimal changes to s-expression notation.

Many previous systems have implemented "infix" systems as a named macro or function (often "nfx" meaning infix). This looks ugly, and it does the wrong thing - the resulting list has "nfx" at the beginning, not the operator. Many of these systems also created a whole new notation which simultaneously lost Lisp's abilities for quoting, quasiquoting, and so on. Therefore, they haven't caught on. Key insight: No built-in precedence

Many "infix" systems in Lisp also implemented precedence, as precedence is usually baked into languages with infix support. However, it is not possible to preset the precedence rules for all uses. Lisp systems often process other languages, freely mixing different types of language, and thus the same symbol may have different meanings. What's worse, these precedence systems hid where lists were being created, losing homoiconicity. So having an infix system that forces the use of precedence makes the system harder, not easier, to use.

The key insight here is that although other languages implement precedence, building precedence into a language is not necessary to have a useful infix system. You can easily add infix notation if you're willing to not force precedence into the reader, and it turns out that is enough for real-world use. Even in languages with precedence, people often parenthesize to make things clear, so not having precedence systems is actually not a big impact. (It's even less of an impact because of the nfx rule described below).

By intentionally not building in a precedence system, we make things amazingly simple - we don't need to register functions, decide their order, or anything like it - making programming much simpler and easier. There's no need to memorize a precedence system, code transfers easily, and code is generally easy to read too (again, because you don't have to memorize a precedence system). The reader implementation is easier to get "obviously right" since it has less to do. As discussed later, curly-infix supports adding an "nfx" operator that can provide precedence in those few cases where it's valuable, without harm to the language or complex infrastructure. Why not limited precedence for a short list of operators?

Actually, there's a reasonable case to be made for precedence for just a few operators, in particular, +, -, *, and /. Even when these operators don't mean add, subtract, multiply, and divide, people using them as infix operators would typically be willing to agree on their precedence. And these operators are certainly widely used.

But there are several arguments against support for limited precedence:

    Any such system harms homoiconicity, which is a serious concern.
    Any useful rule takes more time to explain, and I worry that this would inhibit - not encourage - acceptance.
    Supporting precedence turns out to be less important than you'd think. It's trivial to group operations so that explicitly surrounding them is easy, e.g., {{a + b + c} - d - e - f}. Similarly, although it'd be nice to be able to say {a < b <= c}, many widely-accepted languages work just fine with the equivalent of {{a < b} and {b <= c}}.
    The call out to "nfx" also makes this less important.
    Where do you end? Once you allow precedence, it's much harder to determine where to stop. Stopping at these 4 operators is reasonable enough, but then someone asks about power-of (often spelled ** or ^), comparisons (<, <=, =, ==, !=, /=, <>, >=, >), and so on - all of which could have precedence. You need to stop somewhere, and it's harder to agree on that stopping point.
    In general, it would be much more difficult to get widespread agreement on it. There would be many arguments about the operators to include, their relative precedence, whether or not to support right-to-left (and if so, for which operators), and so on.
    This creates a little more implementation work. It's not much - implementing precedence is well-understood - but we want to get this approach widely adopted, so minimizing implementation effort is useful.
    Precedence could be added later to curly-infix, once the curly-infix rules as given have been accepted, so there is no need to add it now."

"

Why not use the Racket "infix convention" (a . > . b)?

Racket allows an infix notation of the form (a . > . b), as defined here:

http://docs.racket-lang.org/guide/Pairs__Lists__and_Racket_Syntax.html

A pro is that it doesn't need to use up {}, so it might be easier to implement in some Lisps which already define {} for use in a local extension.

However, it has many cons:

    This notation is much longer and more awkward. Every infix operator adds 6 characters, including "." characters not used in any other infix notation. Infix operations are a common operation, so convenience matters. An expression like (1 . + . 2) is far longer, and less convenient, than {1 + 2}.
    It doesn't look like other languages or math. A human notation should be maximally understandable to people given what they already know. {a + b} is much more similar to standard notation than (a . + . b).
    It is easy to make mistakes. If you forget a "." somewhere, you end up with the wrong thing. This would also make it harder to see improper lists; improper lists are important but rarer, so it's good to make them obvious, and this notation doesn't do that. The Racket documentation even goes out of its way to emphasize that infix convention use is unrelated to improper lists.. which suggests that they are easily confused.
    We could redefine prefixed f to instead allow f{a . + . b}, for consistency, but it's still ugly.
    We'd lose {x} as an escape mechanism. We could revert to (. x) as the escape mechanism, at which point dots-in-lists becomes rather busy (!).
    Racket's implementation does not allow multiple operations, e.g., (a . + . b . + . c . + . d). That could be added, but this makes the notation even more unwieldy; compare this to {a + b + c + d}.
    Even Racket users don't use it much. Its documentation says that "Racket programmers use the infix convention sparingly—mostly for asymmetric binary operators such as < and is-a?."

In short, infix is extremely common, so its notation should be convenient. The Racket "infix convention" may be the next-best notation for infix notation after curly-infix, but it's next-best, and we should strive for the best available for a common need.

Curly-infix does not conflict with the Racket infix convention; implementations could implement both. We recommend that an implementation that implements the Racket infix convention should allow multiple operands and use curly-infix semantics, pretending that . op . is a single parameter. In that case, (a . + . b . + . c) would map to (+ a b c), and (a . + . b . * . c) would map to (nfx a + b * c). "

regarding the "curly infix" proposal of http://sourceforge.net/p/readable/wiki/Rationale/: i don't wanna type {a + b} all the time; i think i like my "binary operators with no whitespace are infix" convention better.. e.g.:

a+b --> + a b

one problem is that we want to be able to use '-' in fn names, but '--' is our choice for the arithmetic subtraction. Should we tell the scanner that -- is a special case?! maybe so.. then we can also make -a a special case (inverse of a or anti-a)

---

i recall that one thing i hated about Lisp was that syntactically it was hard to see when something was being indexed, or when an indexed member was being assigned to. My arrays-are-just-functions idea could give this problem too. is the a.b notation enough, or do we need to use a[b]?


toread: http://www.tbray.org/ongoing/When/200x/2009/12/01/Clojure-Theses


TAILCALLOPTIMIZATION toread: http://web.archive.org/web/20110716163344/http://projectfortress.sun.com/Projects/Community/blog/ObjectOrientedTailRecursion http://www.tbray.org/ongoing/When/200x/2009/10/27/Recur


" With pure C code, you can see call stacks, variables, arguments, thread locals, globals, basically everything in memory. This is ridiculously helpful especially when you have something that went wrong days into a long running server process and isn't otherwise reproducible. If you lose this context in a higher level language, prepare for much pain. "


" Someone the other day said that the great thing about C is that you can get your head around it. When Java, or C++, or Erlang, or Common Lisp, or Haskell programming becomes unbearable, it's almost always because the chain of abstractions overwhelms the person building the system. And to be honest, it's why I'm a little less enthusiastic than most of the people here about Rust. "

--- "

First, Scala's syntax avoids some of the boilerplate that burdens Java programs. For instance, semicolons are optional in Scala and are usually left out. There are also several other areas where Scala's syntax is less noisy. As an example, compare how you write classes and constructors in Java and Scala. In Java, a class with a constructor often looks like this:

  // this is Java
  class MyClass {
  
      private int index;
      private String name;
  
      public MyClass(int index, String name) {
          this.index = index;
          this.name = name;
      }
  }

In Scala, you would likely write this instead:

  class MyClass(index: Int, name: String)"

---

" As an example, imagine you have a String variable name, and you want to find out whether or not that String contains an upper case character. I

...

Whereas in Scala, you could write this:

  val nameHasUpperCase = name.exists(_.isUpperCase) ...

In principle, such control abstractions are possible in Java as well. You'd need to define an interface that contains a method with the abstracted functionality. For instance, if you wanted to support querying over strings, you might invent an interface, named CharacterProperty?, which has just one method, hasProperty:

  // this is Java
  interface CharacterProperty {
    boolean hasProperty(char ch);
  }

With that interface you could formulate a method exists in Java: It takes a string and CharacterProperty? and returns true if there's a character in the string that satisfies the property. You could then invoke exists as follows:

  // this is Java
  exists(name, new CharacterProperty() {
      public boolean hasProperty(char ch) {
          return Character.isUpperCase(ch);
      }
  });

However, all this feels rather heavy. So heavy, in fact, that most Java programmers would not bother. They would just write out the loops and live with the increased complexity in their code."

---

" The Uniform Access Principle was put forth by Bertrand Meyer. It states "All services offered by a module should be available through a uniform notation, which does not betray whether they are implemented through storage or through computation." This principle applies generally to object-oriented programming languages. In simpler form, it states that there should be no difference between working with an attribute, precomputed property, or method/query.

While most examples focus on the "read" aspect of the principle, Meyer shows that the "write" implications of the principle are harder to deal with in his monthly column on the Eiffel programming language official website. "

---

scala has a rather verbose solution to the haskell typeclass namespace problem (the problem is that in haskell, typeclass implementations dont like in a namespace):

    abstract class SemiGroup[A] {
      def add(x: A, y: A): A
    }
    abstract class Monoid[A] extends SemiGroup[A] {
      def unit: A
    }
    object ImplicitTest extends Application {
      implicit object StringMonoid extends Monoid[String] {
        def add(x: String, y: String): String = x concat y
        def unit: String = ""
      }
      implicit object IntMonoid extends Monoid[Int] {
        def add(x: Int, y: Int): Int = x + y
        def unit: Int = 0
      }
      def sum[A](xs: List[A])(implicit m: Monoid[A]): A =
        if (xs.isEmpty) m.unit
        else m.add(xs.head, sum(xs.tail))
      println(sum(List(1, 2, 3)))
      println(sum(List("a", "b", "c")))
    }
  (code from http://www.scala-lang.org/node/114 )

see what's happening? the Monoid unit lives in the Monoid namespace (i'm making up my own terminology here). There are different Monoid implementations. To disambiguate, you don't just say unit, you say m.unit, where m is some specific Monoid. In the case of the 'sum' method, we want to use a Monoid for the template type A. To fetch this and put it into 'm', we say "(implicit m: Monoid[A])".

i don't see why it wouldn't be simpler to just say m = Monoid[A]. Presumably there is some distinction between m and the value Monoid[A].

---

toread

http://www.artima.com/pins1ed/a-scalable-language.html#footnote1-17


to reiterate: property graphs where the choice of which property := 'edge' is the perspective


val: a primitive that strips its argument of any apply semantics and returns a plain value (also turns a reference into a value)


jail: apply a capabilities sandbox to a value's get, set, apply, etc. note: since referential purity is a readability convention, not an enforced property, you must use this to enforce it upon any supposedly referentially transparent values that you get from an untrusted source


i guess the syntax for the 'mutable update' transformations can just be combined with the value vs reference syntax; if the symbol is prefixed by a &, then updates are in-place, otherwise, they're not.

---

However, whether or not something has side-effects is not strictly enforced (for the purposes of allowing custom tracing, profiling, and logging functions attached to otherwise referentially transparent functions), which means that technically speaking, it is only a readability convention. This means that if you are passed a value from an untrusted source, even if it is marked in the Jasper type system as not having side effects, you must sandbox (jail) it before touching it; even reading from an untrusted value allows its side-effectful custom logging functions to run. You can remove this danger by making a global declaration of whitelisted logging code, but you still need to jail untrusted values because reading from a value calls its 'get' metafunction, which could cause an infinite loop. ---

hmmm... okay, to remove this danger for everyone, only allow whitelisted side-effectful fns to be marked as no side effects.

---

assertions are demands; type declarations are promises

read http://en.wikipedia.org/wiki/Linguistic_modality

todo https://www.google.com/search?client=ubuntu&channel=fs&q=demand+promise+modal+contract&ie=utf-8&oe=utf-8#hl=en&client=ubuntu&hs=gnI&tbo=d&channel=fs&q=demand+promise+modal+contract+logic&nirf=demand+promise+model+contract+logic&sa=X&psj=1&ei=0fz1UI7cC-u00AGFj4GgDw&ved=0CDAQ8BYoAQ&bav=on.2,or.r_gc.r_pw.r_cp.r_qf.&bvm=bv.41018144,d.dmQ&fp=de88dc4d077bddb4&biw=1340&bih=1984

todo dl.acm.org/citation.cfm?id=2137449

---


should be able to easily append the patterns described in the book "release it": circuit breakers (stop doing something if it fails a bunch of times, then try again after a wait, if it fails again then wait longer (backoff), etc), handshakes (before you tell a service to do something, you ask if it can handle you), timeouts (if something you call hangs, eventually give up, dont wait forever). also needs to easily handle async stuff e.g. async database access/futures, callbacks.

re. async database access: take a look at van Rossum and team's newish async db API for google app engine: https://developers.google.com/appengine/docs/python/ndb/async https://developers.google.com/appengine/docs/python/ndb/


implicit futures ( http://en.wikipedia.org/wiki/Future_%28programming%29#Implicit_vs_explicit ) (of course, having everything use __get might allow the implicit futures to be implemented as a library?)

---

"It turns out that eval() is one of the key things that gets in the way of performance optimizations. It can easily invalidate all sorts of otherwise reasonable assumptions about variable bindings, stack addresses and so on. It's also pretty important, so you can't just get rid of it."

---

" Here's a short list of programming-language features that have become ad-hoc standards that everyone expects:

    Object-literal syntax for arrays and hashes
    Array slicing and other intelligent collection operators
    Perl 5 compatible regular expression literals
    Destructuring bind (e.g. x, y = returnTwoValues())
    Function literals and first-class, non-broken closures
    Standard OOP with classes, instances, interfaces, polymorphism, etc.
    Visibility quantifiers (public/private/protected)
    Iterators and generators
    List comprehensions
    Namespaces and packages
    Cross-platform GUI
    Operator overloading
    Keyword and rest parameters
    First-class parser and AST support
    Static typing and duck typing
    Type expressions and statically checkable semantics
    Solid string and collection libraries
    Strings and streams act like collections

Additionally, NBL will have first-class continuations and call/cc. I hear it may even (eventually) have a hygienic macro system, although not in any near-term release.

Not sure about threads. I tend to think you need them, although of course they can be simulated with call/cc. I've also noticed that languages with poor threading support tend to use multiprocessing, which makes them more scalable across machines, since by the time you've set up IPC, distributing across machines isn't much of an architectural change. But I think threads (or equivalent) are still useful. Hopefully NBL has a story here. " -- http://steve-yegge.blogspot.com/2007/02/next-big-language.html " Rule 6: Multi-Platform

NBL will run, at a minimum, both standalone and on the JVM. I'm not sure about plans for .NET, but it seems like that will have to happen as well.

And there are two other platforms that NBL will run on which, more than anything else, are responsible for its upcoming dominance, but I'd be giving away too much if I told you what they were. " -- http://steve-yegge.blogspot.com/2007/02/next-big-language.html

---

" The features I've outlined don't make NBL a great language. I think a truly great language would support Erlang-style concurrency, would have a simpler syntax and a powerful macro system, and would probably have much better support for high-level declarative constructs, e.g. path expressions, structural dispatch (e.g. OCaml's match ... with statement) and query minilanguages. Among other things. "

---

how would jasper implement the active data structures described in the connection machine?

need a declarative, regex or recursive language for describing the structure of the graph based on its regular features, and attaching computation

r there multiple curry howard isomorphism like things?

---

" The problem is that Scheme provides perhaps too little. The C++ standard library provides you with standard containers, such as hash maps and binary trees, as well as algorithms operating over those containers, such as sorting algorithms. It also provides basic support for multithreading and string tokenization. The latest Scheme standard (R6RS) doesn’t specify any of this, even though these are very basic features that many if not most programmers will need at some point. "

" Batteries Not Included

One of the biggest frustrations I’ve had when trying to write Scheme code is the poor documentation. It’s not that easy to learn what you need to know about the language. Google searches will often yield irrelevant results, or results that are specific to one Scheme implementation. The official language specification document is probably the best source of documentation, which is rather sad. What if you need to use implementation-specific features? Well, the Gambit Scheme page has a long list of undocumented extensions.

Perhaps the Scheme community suffers from a lack of desire to document. From what I’ve seen, it’s fairly typical for Scheme code to have very few comments (if any) and be full of one-letter variable names. There seems to be a prevalent assumption that the code is self-evident and doesn’t need any explaining. This assumption is rather strange, because Scheme is probably the language that most needs documentation out there. In a language where you can use macros to (re)define language constructs, you should never assume that anything is self-evident. "

---

very interesting:

http://dl.rust-lang.org/doc/tutorial.html#the-rust-memory-model

shades of bartosz milewski's system..

---

see prosAndCons/exceptions for context:

i guess the problem is that, say that multiple things within the 'try' block can both throw the same type of exception. Now you can write the exception handler (the 'catch' block), having in mind the first thrower, but forgetting that there was something else in the same code that can throw that type of exception. It's hard for a later reader to distinguish your neglect of the second case from your having thought about it and decided that both of them can be handled the same way. With explicit error code return it is easy to see if you thought about that (did you check the error code in the second case?)

This is a problem because of side-effects; you don't just care what the function returned to you in the end (in both cases, nothing), but you also care how far it got before it threw, because there are side effects.

Jasper's LABELS/footnotes/wrappers idea could help here, if done right. The idea would be, when there is an imperative/ordered sequence of side-effectful commands, rather than allow the coder to say, 'if an exception arises anywhere in here, do this', instead allow them to label each line with a handler. This is better than exceptions because it forces the exception checking to be localized to each function call. It is better than error codes, b/c:

in other words, the prob is not exceptions. the prob is just that try blocks are blocks rather than single lines. Well... not actually... b/c ppl could mimic try blocks by creating a helper function with the contents of the block. Still, this should provide a suggestion to do the right thing, at least.

The Go idea of resuming after an exception by dropping the lowest frame in the call stack also seems like a good one.. except that this messes with the usefulness of exception resuming as a non-exceptional control flow technique. Which was their intention. Maybe have Errors and Exceptions.

(later): i just wrote a piece of Python code that called fns in buggy library a number of times and wrapped a bunch of stuff in a try..except block to catch exceptions from the library. This was harmless, as the block of code had no side-effects. So, i propose allowing try..except in addition to wrapper labels, but only for side-effect-less code. or... maybe by default exceptions are marked 'stateful' and have this effect, but if the user thinks that the client doesnt need to worry about incomplete operations/inconsistent state, you can 'cleanse' them, which means you can catch them in try..except blocks above.

yknow... (see next after next --- )

---

actually as part of the march towards the mirror world/backwards/antibody/multiple entries and multiple returns idea, i guess exception handling should be generalized to include any out-of-band information that you want to send up the call stack in the middle of execution. e.g. progress indicators.

hmmm... that sounds kind of like a structured stderr, except you're passing control back up there, too... unify those? control passing there is kinda like beaming your consciousness as a signal into the matrix, as phill would put it... hmm... can maybe unify 'throw' with 'return'.. both pass a message, along with control, up the call stack. 'throw' has properties (antigens) that a handler in the call stack must match (antibodies)... return can be seen as a degenerate case of this, with no conditions (e.g. with properties that match everywhere, including the site of the caller). hmmm.. and can we generalize this further to graphs? The matching is done by traversing a tree (the call stack) in a given ordering. I guess a call stack, a linear graph, is a special case of a general tree, which perhaps corresponds to serial vs. parallel matching.

hmm.. and recall from the mirror worlds/backwards idea, that which graph is traversed, and how it should be traversed, should probably be decided by the caller, not the returner. Note that since we have all of exception handling, and normal returning, and progress reporting, that multiple such channels (should this be unified with channels?) are multiplexed onto the same downwards function call.

hmm, should read this: http://docs.racket-lang.org/reference/cont.html

looks like racket has barriers (are they the same as mine?) and 'composable delimited continuations' and other cool sounding stuff like that...

generalize the stack to a tree.. processes on separate branches execute in parallel (? or can the return-to tree be different from the actual thread stacktree map?).. the process of searching for a handler to an exception can be generalized... if you find a handler you dont search down that branch behind it further, but other branches are still searched in parallel, e.g. branches mean 'search down both (all) of these and return both results'. this seems like what is used for inheritance as well.

perhaps the same handler (node) can appear multiple times at different places in the tree, with different match criteria, and match multiple different bubbles before executing (multiple inputs).

i like where this is heading... we do seem to be getting closer to expressing control flow in terms of graphs...

--- "Each frame is conceptually annotated with a set of continuation marks. A mark consists of a key and its value; the key is an arbitrary value, and each frame includes at most one mark for any key. Various operations set and extract marks from continuations, so that marks can be used to attach information to a dynamic extent. For example, marks can be used to record information for a “stack trace” to be used when an exception is raised, or to implement dynamic scope. 1.1.12 Prompts, Delimited Continuations, and Barriers

            +See Continuations for continuation and prompt functions.

A prompt is a special kind of continuation frame that is annotated with a specific prompt tag (essentially a continuation mark). Various operations allow the capture of frames in the continuation from the redex position out to the nearest enclosing prompt with a particular prompt tag; such a continuation is sometimes called a delimited continuation. Other operations allow the current continuation to be extended with a captured continuation (specifically, a composable continuation). Yet other operations abort the computation to the nearest enclosing prompt with a particular tag, or replace the continuation to the nearest enclosing prompt with another one. When a delimited continuation is captured, the marks associated with the relevant frames are also captured.

A continuation barrier is another kind of continuation frame that prohibits certain replacements of the current continuation with another. Specifically, a continuation can be replaced by another only when the replacement does not introduce any continuation barriers (but it may remove them). A continuation barrier thus prevents “downward jumps” into a continuation that is protected by a barrier. Certain operations install barriers automatically; in particular, when an exception handler is called, a continuation barrier prohibits the continuation of the handler from capturing the continuation past the exception point.

A escape continuation is essentially a derived concept. It combines a prompt for escape purposes with a continuation for mark-gathering purposes. As the name implies, escape continuations are used only to abort to the point of capture. "

-- http://docs.racket-lang.org/reference/eval-model.html#%28tech._continuation._barrier%29

a ha! so this sort of thing is directly expressible in terms of simple graph ops (which will make everything much more comprehensible to newbies, too, i hope). i dont understand continuation barriers or escape continuations yet. todo.

--- the diamond inheritance problem: really i guess you want to know if a class that inherits a parent's member variable wants to (a) ('hygenic') get its own copy of the variable, so if another class inherits from the same variable and changes it, it won't affect this member's class, or (b) ('non-hygenic') share the variable with any other users in the same instance. i guess (a) is similar to composition; you get your own separate copy of the parent instance.

do we also want to support private methods, e.g. methods that (by default) dont appear in the subclass's namespace?

isn't this getting too complicated?


should be easy to do fig.3 and fig.4 in http://www.linuxjournal.com/article/3882


i like the 3 types of Rust pointers but really we need to put that in a library... maybe uses annotations?

---

still thinking we want syntacic support for indexing to make things clearer... but still think that's inelegant... hmmm.. what makes containers differnt from other functions, anything? maybe __set?

---

---

ad-hoc polymorphism seems so ugly and.. ad-hoc. But it is needed in order to instantiate interfaces into concrete types, e.g. if you want something that obeys the 'file' interface but to replace reading from a file with reading from the network, you really need ad-hoc. Does this mean that it should remain available in general, or that it should be restricted to cases like that, whatever that means?

---

in theory the compiler could parallelize, but in fact the overhead makes most parallelization opportunities not worth it, so we need an annotation to hint when it is

--

guile and racket have 'dynamic wind' which defines 'dynamic extents' which are like try..finally blocks -- if a continuation tries to penetrate them, the finally block is called. hmm..

---

" Ask yourself how many times you have written code like this:

int? friendsSonsAge = null; if (person != null && person.Friend != null && person.Friend.Son != null) { friendsSonsAge = person.Friend.Son.Age; }

OK, OK. I was never really good at short contrived code examples but the point is that often you want to know the value of a property buried deep in a hierarchy of objects, each of which may or may not be null. In most mainstream languages (Java, C, Javascript, VB.NET, C#, Ruby) you have no recourse but the ugly boilerplate code shown above.

In C-Omega you can do this:

int? friendsSonsAge = person.Friend.Son.Age;

if (friendsSonsAge != null) do something

If any of the intermediate objects in the expression on the right of the assignment are null the whole expression evaluates to null. This is called null propagation and it works very nicely together with nullable types. I have no idea why this feature wasn't added to C# 3.0. No matter, we can create a macro that duplicates this behavior ourselves. :-) "

---

what if you add a cache update to a pure function? that changes it to a side-effectful fn. But you may not want to think of it like that. Let's allo side-effectfulness to be defined relative to a domain, making the 'logging' exception more extensible. E.g. you can say this has side effects, but only in the 'cache' domain.

i think this is getting closing to how Haskell deals with state, but i'm not sure.

---

fns that could be 'in-place' should be written as such, and the compiler will transform them to work on a copy

--- (copied to/from prosAndCons) hmm.. transitive implicit coercion might be confusing if you have a zillion clever/nonobvious ways to coerce types into each other which are not all defined in one place.. otoh it sucks to have to write 'a = sqrt(32); print str(a)' instead of just 'print a'.. what's the answer?

i think scala has full-out transitive implicit coercion (not sure tho) but ppl say it may be confusing. C has only explicit coercion.

is 'non-transitive implicit coercion' the answer (and if the compiler does it automatically and does type inference then you have to work to make it nontransitive, b/c even if it only goes one coercion per operation, if something is passed thru a chain of operations that would normally lead to a chained coercion, unless you explicitly represent whether it has already been implicitly coerced at least once)? is a universal 'coerce here if necessary' operator the answer?

scala uses coercion instead of moneypatching/open classes:

" However, it's not as nice as we would like it to be, e.g. "nice" * 3. But how can we get there, because obviously we can't go and put the '*' method to String class. Well, the first thing to do is to create a class StringRepeated? that has that method.

class StringRepeated?(original: String){ def *(times: Int) = { def multiply(times: Int, accumulated: String): String = { if(times > 1) multiply(times - 1, accumulated + original) else original + accumulated } multiply(times, "") } }

Now if we create a StringRepeated?, we can use the '*' like this:

val repeated = (new StringRepeated?("nice")) * 3 == "nicenicenice"

But we can't do this on normal Strings directly. The implicit conversion is what comes to rescue; it makes the conversion from String to StringRepeated?.

implicit def string2repeated(x: String) = new StringRepeated?(x)

That's it. Now when compiler sees a "nice" * 3, it first concludes that there is no '*' method defined in String. Then it looks for implicit conversion that would give a new object that has a '*' with correct parameter type. If there is no ambiguouty in choosing what implicit conversion to apply, the compiler will insert the conversion.

So "nice" * 3 will be statically translated to something like (new StringRepeated?("nice")) * 3.

Now as this small example shows, implicit conversion can be somewhat handy. For additional example, I have an application that prints text that is partly colorized or otherwise styled. I have taken advantage of implicit conversion so that I can write printWith("Hi,"

"
"this" * Bold " is " * Fore(RED)"cool."). It will print sequentially "Hi, this is cool."

but the same page shows that this interacts badly with other features:

http://scalada.blogspot.com/2008/03/implicit-conversions-magical-and.html

" In Scala == is structural equality test method, unlike in Java, where it is reference equality test. It takes one argument type of Any, which means you can compare your object against any object. I mean any, and compiler will allow you that. Doesn't sound too bad, you say? Well, if you have lots of inheritance, you might really want that ability, so that you can reveal whether two objects are the same even if their static type is not the same. But usually I just want to know two statically equivalently typed objects to be tested against each other, not arbitrary objects.

I have had problems with this simply, because you don't always compare things you should. Humans do mistakes (I sometimes wonder, am I ever doing something *right*). For example, I might have a tuple of Ints, which has name 'boom'. And i have another tuple 'doom', and I'd like to compare their first elements, i.e. boom._1 == doom._1. But I might be a bit sleepy, and write "boom == doom._1", i.e. comparing a tuple against an Int. Compiler won't say that you are comparing completely different objects. It just sings logically incorrect bytecode. It might be very hard to notice that the comparison isn't ever going to give a true value just by testing it few times.

Ok, that's bad enough as it is, but when we mix implicit conversions with this magical soup, we are really doomed. An example: String has no reverse method. Therefore it has been extended with RichString? in the same kind of way I described earlier for RepeatedString?. When you say "yay" reverse, you get a RichString?, not String. Guess what, "yay".reverse == "yay" won't give you true. Happy debugging times ahead, my friend. It isn't helping that with type inference in use, even with intermediate variables (val x = "yay" reverse) you might think you're dealing with a normal String. That might happen if you just forgot that there ever was a RichString? class, and thought that reverse was defined in String. "

another solution might be to have only the 'end-consumer' say whether coercion is allowed. e.g. 'print' can say 'i allow coercions via str() in my first argument'. This is no better than saying 'print needs to be defined like: def print(s): s = str(s); ..do stuff..'. Note also that in Python's scheme, it is the class of the value being coerced that decided how it gets coerced (via the __str__ method, or the __repr__ method if that's absent). There's no chained coercion.

it seems to me that we dont want people using implicit coercion in the way it was used in that blog post. A Haskell typeclass instance would be a cleaner way to do that. However, this still seems to me like a case of ad-hoc polymorphism for something 'ad-hoc', e.g. it's not being used to add e.g. read and write to a file, it's being used to add multiplication to a string. maybe if you added multiplication to anything which had an append operation, i'd feel better.

hmm.. perhaps that's it.. on the one hand, there is the implementation of an interface for a specific implementation. On the other hand, there is saying, 'if something supports interface A and interface B, then here is how to make it support interface C'. Haskell allows the latter, also via the typeclass interface mechanism:

" instance (Eq a) => Eq (Tree a) where Leaf a == Leaf b = a == b (Branch l1 r1) == (Branch l2 r2) = (l1==l2) && (r1==r2) _ == _ = False " -- http://www.haskell.org/tutorial/classes.htm

---

 ! as mutate-in-place marker prefixing arguments. to be able to do in-place mutation, a function just has to have an output argument with the same name as an input argument, e.g.:

listToBeSorted = def sort listToBeSorted sortPredictate=<< blah blah

now call it like

  y = sort x

for immutable behavior

or

  sort !x

for in-place mutation. sort !x is merely short for

  x = sort x

unless x is a reference, in which case its actually in-place mutated:

  sort !&x

(but remember the value can be a reference even if this function doesnt know that)

this generalizes to multiple arguments mutated, or to select which ones

(constructor problem: if x has other information hidden in other perspectives, they survive x = sort x iff the sort subroutine used the same name in its inputs and outputs (is this just a convention or is there language support? i guess at least support for checking this implicit promise, but does the language have to do anything to make it true in light of the constructor problem?), e.g. the same conditions for sort !x to be valid. For readability, it is suggested that sort !x be used whenever possible)


but if ! is mutation, then what syntax for 'hurry'?


what are the semantics of giving a ref to a subroutine that expects a value? simple: no other aliases to that ref may be accessible to that subroutine. E.g. that subroutine can never see the value change from under it.

but what if another thread changes the value? i guess this means you just cannot give refs that are shared with other threads as unmarked values to subroutines?

but then you can't have e.g. def euclideanDistance x, y run on a ref? i guess fns have to say when they are threadsafe e.g. can tolerate a ref:

def euclideanDistance &x, &y

alternately, the compiler replaces a call to def euclideanDistance x, y like euclideanDistance &a, b with euclideanDistance copy(&a), b

and a call to sort(!&x) with &x = sort(copy(&x))

hmm.. i like that..

(note: the compiler could detect that certain simple shapes are always threadsafe e.g. when a variable is only accessed once, and not bother with the copy -- but then what if x and y are changed but not synchronously? mb compiler should auto-wrap euclideanDistance &a, &b in a transaction if euclideanDistance is ref-unaware and &a and &b are both shared between threads, e.g. def euclideanDistance x, y ?. No, it had better acquire a lock to both of them and then copy them and then release the lock in reverse order, b/c o/w what if the other thread is mutating them faster than this function can finish? then optimistic concurrency, e.g. a transaction, would retry forever, right? i think no, b/c it isnt writing, only reading, so i think a transaction is fine, not sure tho)

does !&x on a ref shared between threads imply a transaction? i think not -- if you are dealing with refs you are responsible for keeping track of this stuff. or we could have &x for threadlocal refs and &&x for shared refs. hmmm, i kinda like that. so !&x on a shared ref implies 'i dont wanna think about concurrency, do it for me (e.g. guarantee that this value won't change except when i cause it to change)'. !&&x implies 'i know what i'm doing'.

e.g. say def fn &x &y = blah. if &x and &y are related cells in a spreadsheet, or values constrained by our constraint logic system, then changing &y can lead to a change in &x. However, you are guaranteed that if you set 'a' to a copy of x and then set 'b' to a copy of y, that a and b will be consistent. But if &&x and &&y then all bets are off; another thread could mutate &&y after you got a copy of x but before you got a copy of y.

btw i guess a = &x implies a = copy(&x).


scala allows xml literals in code: http://www.scala-lang.org/node/131

couldnt we have a more general mechanism for this using reader macros?

---

in Scala, type parameters are object fields, e.g.:

abstract class Buffer { type T val element: T }

and can be more specialized in subclasses, e.g.

abstract class SeqBuffer? extends Buffer { type U type T <: Seq[U] def length = element.length }

---

http://stackoverflow.com/questions/8122109/difference-between-oop-interfaces-and-fp-type-classes

http://www.haskell.org/haskellwiki/OOP_vs_type_classes#Type_classes_are_like_interfaces.2Fabstract_classes.2C_not_classes_itself


"i can impose an interface on your code" as doug put it, in typed javascript.

in jasper, of course you can provide an implementation for an interface, but in addition, you can add arbitrary annotations to the type metainfo of a value, and, in metaprogramming, to other code.


http://clayallsopp.com/posts/the-story-of-pull-request/

---

note that since we have immutability except for references, but mutability within functions, if you had something like:

def f(x:assoc): x 3 = 4 y = x 3

x = assoc x 3 = 2 y = f x promise y == 4 promise x 3 == 2

then y == 4, but after the function returns, x 3 == 2! this is because f wont change anything it is passed unless it is marked as a reference, e.g.

def f(&x:assoc): &x 3 = 4 y = &x 3 promise y == 4

x = assoc x 3 = 2 y = f &x promise y == 4 promise x 3 == 4

hmm.. may want a non-shifted character to indicate refs since it will be so common. mb `, or mb .

also, mb a lone expression is implicitly taken to be a promise which is sequenced immediately after the previous line, e.g.

def f(.x:assoc): .x 3 = 4 y = .x 3; y == 4

x = assoc x 3 = 2 y = f .x y == 4 x 3 == 4

hmm, that implict promise thing conflicts with the convention that the last line of a fn is a return value. mb use a semicolon?

def f(.x:assoc): .x 3 = 4 y = .x 3; ;y == 4

x = assoc x 3 = 2 y = f .x ;y == 4 ;x 3 == 4

also, note the colon x:y as a shortcut for <x@meta.type += y>, which becomes <demand y in x@meta.type> when found in an argument list.

3 things, maybe related:

1) if you dont have 'let x=y in __' or '__ where x = y' then you gotta have something like 'def' to indicate a block in which you have some local variable definitions 2) but maybe we could just literally use blocks, that is, {} 3) hmm, i wonder if the .x syntax could be used here like ./x in the unix file tree to refer to (child of the current working directory). i wonder if this could in fact jive with the use to indicate references; e.g. f .x passed the LOCATION OF X to f, not a copy of the value currently in x. You could also have ..x, ...x, etc, to ascend more blocks. The same notation could be used in graph literals.

also, you could have 'me' at object boundaries, although due to scoping it wouldnt be necessary. i guess 'me' is dynamically, not lexically, scoped?

if the first statement of the block is an assignment, we could let the rvalue be the value of the LAST expression in the block, so

f x = y = x + 2 y * 2

would yield f(x) = 2*(x + 2)

hmm actually the above example could be better done with just in-place mutation, no need for references:

x y = f x:assoc = x 3 = 4 y = x 3; ;y == 4

x = [] x 3 = 2 y = f !x ;y == 4 ;x 3 == 4

note that only returned arguments with the same name as one of the inputs may be inplace-mutated (the ! operator). in this case, y = f !x is short for x y = f x. the compiler has an optimization that detects when an output is being assigned to a variable of the same name as the corresponding input and optimizes this case by using an in-place version of the subroutine.

note: const-by-default (marking references explicitly) is very important b/c it's hard to add const to a language later: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4211070

btw i think more commas should bind tighter, not looser, e.g. a , b ,, c, d means (a , (b ,, c), d) not (a , b) ,, (c, d)

because the common case will be when you start typing, if you have multiple arguments, you dont know how many levels of nesting there will be in the second and third argument when you type the comma to delimit the first argument from the second.

btw note that the comma syntax takes us back to good ol' fn syntax in many cases:

f (x,y,z)

altho if you just have a unary fn you can do

f x

and if you have a pipeline

h g f x

(for h(g(f(x))) )

so you'll see things like

h g f (x,y)

this is equivalent to

h g (f x) y

hmm wait i think that's not right; f (x,y) would mean f ((x) y), right? that's not what we want, we dont want to apply x to y. so i guess we have to have the convention that f x,y also reverses the association direction in the first space to the left of where the comma is, e.g. it's ((f x) y). hmmm maybe better would be to put a comma in between f and x and let the first comma be special (which lets us have spaces within the first argument), so we get:

h g f,x,y

for

h(g((f x) y))

and we get

h g f,x ++ 1, y

for

h(g(f (x ++ 1)) y))

note that if the function you want to apply is itself the result of an expression, you need parens, eg

h (g f),x ++ 1, y

for

h((g f) (x ++ 1) y)

---

operations within a whitespace block are sequenced, eg

a = f x b = f y

--->

{a = f x ; b = f y}

an assignment in which the rhs refers to the lhs is let-sequenced with those above it, e.g.

x = x + 1

x = x + 1

y = f x

-->

x = x + 1

x2 = x + 1

y = f x2

boolean expressions on a line by themselves are either constraints or promises(or both!), e.g.:

x