Skip to content

Instantly share code, notes, and snippets.

@ELLIOTTCABLE
Created June 22, 2010 02:16
Show Gist options
  • Save ELLIOTTCABLE/447828 to your computer and use it in GitHub Desktop.
Save ELLIOTTCABLE/447828 to your computer and use it in GitHub Desktop.
Paws tutorial outline
=====================
- Overview
- Conceptually simplistic object-space; everything appears as a “list”
- Extremely extensible syntax and semantics, Unicode-friendly
- Everything is completely asynchronous in nature; synchronicity is the exception, not the rule.
- Even though asynchronicity practically implied by the medium, it isn’t obtuse to use
- Provides easy, safe concurrency throughout your code
- if the implementation chooses to implement concurrency
- Operates in a distributed nature across multiple interpreters and systems
- including web-client (browser) and web-server
- Theory
- overall design goal is a robust and lovely-to-use language with which to power a new kind of web framework
- to that end, we’re building *three* programming languages, each of which builds upon the last:
- Paws
- absolutely minimalistic
- the *actual language* as interpreted by Paws implementations
- at every stage of design, striving to remove everything we possibly can and *move* it into Paws’ Core
- is not intended to be particularly usable on its own
- provides the lowest-level “building blocks” to implement Paws’ Core, nothing more
- Paws’ Core
- still minimal, but not as ascetically Spartan as Paws itself
- is a sort of minimal “standard library,” while also being its own *language*
- implemented entirely in Paws
- thus, any compliant Paws implementation can also handle Paws’ Core
- while still not a language that end-users are expected to often use, will be more friendly than Paws
- since Fullness is intended to be layered and iteratively teardown-friendly, Paws’ Core is the
“language” in which others (who want to design their own linguistic alternatives to Fullness) will be
working when they’ve torn enough of Fullness out of their environment
- Paws’ Fullness
- the final language, as *I* (elliottcable) envision it
- is very opinionated, not intended to suit everybody
- very layered, intended to be “torn apart” by those using it, and gradually replaced with idioms more to
their individiual, personal style
- no two people will be using the same “language,” if they’re applying Paws as it was intended to be used
- everyone will gradually build their own language and ecology, to some extent, as they work in Paws
- is much more lavish and sybaritic than either of the above; the vast majority of user-facing friendly
linguistic features will be implemented at this level
- heavy on the abstractions and visual nicities, as some examples:
- infix operator support
- unitization, such as `$` for dollars or `″` for inches
- convenience routines for most core Paws concepts
- Paws can be utilized without Fullness, or even Core (though you’d have to be insane!)
- **this document *only* covers Paws, and ocassional bits of Paws’ Core!**
- Fullness is too far in the future, and too nebulous, to specify at the moment
- Syntax
- extremely simple core syntax
- called ‘cPaws’ or “canonical Paws”
- every implementation required to parse
- words and sentences seperated by whitespace: `foo bar`
- a ‘word’ can be any series of non-space and non-parenthesis/brace Unicode codepoints:
- `foo`, `foo.bar`, `42`, `!@#$%^&*` are all valid words
- a ‘sentence’ is *any* series of characters within balanced Unicode double-quotes
- “foo foo.bar 42 !@#$%^&*”
- may not contain unbalanced closing-double-quote
- things surrounded by parenthesis are a list-literal
- `(foo bar)` creates a new list containing the value of of `foo bar`
- things surrounded by brackets are a routine-literal
- `{foo bar}` creates a new routine with the portion of the AST containing `foo bar`
- any of the above elements, followed by another element, constitutes a “space operator”
- the space operator *usually* consists of a “lookup” (see below)
- can be overridden to mean other things
- on `routine`s, could cause the `routine` to be run
- on `list` literals, could cause a multi-lookup
- at the end of a statement, a “terminator” call is made, allowing more complex lookup chains
- extensible via pre-compilation stage
- is optional for implementations
- output can be serialized to cPaws for transmission to non-pre-compilative implementations
- during normal execution, the AST of a document is opaque
- this is to enable bytecode compilation at a later date: an AST isn’t required to exist anymore or even
have any useful meaning at runtime; the structure of the program is statuc
- a document (when pre-compilation is turned on) is not just executed a single time
- is executed two or more times
- as precompilation instructions are executed, execution may prematurely terminate and begin ‘from the top’
while applying the newly-acquired precompilation instructions
- during pre-compilation stage, AST is exposed to libspace
- a new API is made available, to interact with the AST attached to `routine` objects
- during pre-compilation, all forms of I/O or interaction with the external world are inaccessible
- this ensures that the pre-compiler can execute the code as many times as it wants to, without any adverse
side effects
- programs can thus modify their own structure, introducing new elements of meaningful syntax, such as infix
operators, new forms of data literals, or really, anything imaginable to the mind
- precompilation instructions
- are not like the CPP
- are written *in Paws*
- are even included anywhere in your own code
- as long as the precompiler can “reach” them without “going through” a portion of code that requires
I/O, because branches of the code applying I/O will be entirely non-functional during pre-compilation
- have your full object-system and libraries at their behest
- such as Fullness, with inheritance and the like
- are simply `routine`s registered with the precompilation API
- will be executed against new documents’ ASTs, once registered
- libraries can build build precompilation instructions and provide them to things that use those libraries
- for instance, Fullness will define many useful operators
- can even define “meta-instructions” that control *other* precompilation instructions
- again, Fullness: will provide tools for defining new infix operators and negotiating their precedence
- is optional
- you can include external libraries’ precompilation instructions *without* enabling the precompilation
stage for your own document, if you’re afraid of the precompiler executing your code at compiletime
- Paws’ “core API”
- is not exposed by any form of inheritance, Paws itself defines none
- is disturbingly C-like, only with namespaces
- revolves around “routine bags” for each core datatype, including primitives
- `infrastructure` is simply a `list` exposed by the interpreter
- `infrastructure routine` is another `list`, stored on the previous list
- `infrastructure routine run`, as an example, would be a natively-implemented routine
- you would theoretically call the core APIs as if they were root-level functions, not as “methods”:
`infrastructure routine run(a_routine)`
- exposes hooks into ‘creation’ of types; “constructors”
- are not like constructors of old, have nothing to do with inheritance
- are automatically called when the interpreter has to create new object, such as through a literal
- object systems utilize to apply their magic to new objects
- globally defined (`infrastructure list allocate()`)
- can be overridden per-routine (`a.routine _ list allocate()`)
- must be overridden on some of the constructors themselves, to prevent infinite recursion
- bags are ugly, by design: it avoids having to do anything to newly created objects to “prepare” them
- such “preperation” is left up to object systems, which are written *in Paws* (such as Core)
- Paws’ Core implements very rudimentary “prototypal inheritance,” utilizing the constructors (see below)
- within Paws’ Core, the basic routines described throughout this document are often available as “methods”
instead of having to work through the crude “routine bags” provided by the interpreter
- ‘Object’ system
- `list`
- primary datatype of the language
- 1 indexed
- naughty
- are often *treated* as associative arrays, by way of `assocation`s (see below)
- `list`s-as-forks
- `list`s are not just a data storage type, conceptually
- can be more productively considered ase either:
- a first-class “‘fork’ in program execution”
- a first-class “data ‘pipeline’ between multiple locations in program execution”
- however, all together, `list`s are not simply any of the above, they’re a more powerful and generalized
hybrid, as you can fork against all future *and* past contents
- forking happens via `ramify()`
- can be ‘forwards,’ ‘backwards,’ or both
- ‘backwards’-only forking is `fork()`, only forks on the elements present when you call `fork()`
- ‘forwards’-only forking is `listen()`, only forks when elements are added in the future
- bidirectional forking is cruicial; a bidirectional fork is *temporally deterministic*, as the actual
forking of the program will proceed identically no matter *when* you fork: is best practice
- forking is core to our asynchronous design
- primitives datatypes
- all opaque
- all appear the same as ‘lists’ libside
- all store elements in a ‘list’ portion of their storage, just like a ‘list’
- so, all are also associative arrays from libside POV
- `string`
- characters are opaque
- immutable, not like Ruby’s `String` or Cocoa’s `NSMutableString`
- interned, like `Symbol`
- globally unique
- used primarily to quickly test for equality, as a ‘name’
- encoding is static, always matches encoding of Unit it was created
- i.e. encoding of the document the string-literal was typed in
- can be up-converted to `soup`
- `soup`
- is a “character soup” (think ‘alphabet soup’), or, more accurately, “codepoint soup”
- characters still opaque
- mutable
- native routines provided to manipulate: concatenating, appending, inserting, and removing, etc chars
- attributed
- like NSMutableAttributedString, can have objects associated with ranges of characters
- those objects might represent styles for printing, for instance
- the ranges are updated as mutations are preformed, ensuring attributions consist throughout mutation
- encoded
- is aware of encoding it was created with
- can be implicitly converted to another encoding as necessary
- implicit conversion can be disabled if information loss would be critical
- `numeric`
- not well defined; design on hold
- math stacks… **MORE**
- `routine`
- very simple, just represents “a bit of code”
- AST is opaque
- `execution`: opaque pointer into AST of every `routine` it touches
- common non-primitive datatypes
- these are not primitives
- they’re all implemented on top of `list`, in libspace, not natively
- they’re still very common, and low-level: not part of Fullness, instead, ‘Paws Core’
- since we don’t have inheritance or actual “types” built into the core language, these are all only
differentiated by the “routine bags” utilized to manipulate them
- Paws’ Core’s inheritance provides faux-primitives of these “types”
- `assocation`
- contains two initial elements, a key, and a value
- key can be *anything*, but is usually a `string`
- can contain other associations or elements, as with any other `list`
- often has “attributes” in the form of other associations on it, such as:
- `locked` might cause the holder of the `asociation` to reject changes
- `unique` to avoid overriding of the `association` with a new one
- are generally, themselves, transparent to lookups (see below)
- `scope`
- not “lexical scope”, nor “dynamic scope” or “static scope”
- what I call “objective scope”
- scope is just an object, a `scope`
- on a `scope` is a `locals` slot that points, recursively, to itself
- all lookups go through `locals`
- `locals` can be replaced with another object
- “variables” are simply `association`s
- this means our “variables” can be passed around, they’re first-class
- also means “variables” can have attributes, just like any other `association`
- `ghost`
- is what most asynchronous `routine`s (that is, *most `routine`s*) ‘return’
- said `routine`s will generally “materialize” the `ghost` over time, by filling it up with ‘results’
- this is how most asynchronous routines give their results to consumers
- are ‘blocking,’ and proxy all operations through all of their children by `ramify()`
- this means that preforming essentailly *any* operation on a `ghost` will fork your program
- the actual operations, through `ramify()`, will instead be preformed upon the children of the `ghost`
- are generally ignored by lookups (see below)
- lookups
- semantically, describe a ‘message to an object,’ asking it to give you something
- don’t necessarily mean giving you something from the contents of the list, though it often does
- default implementation:
- reverse-iterate elements of the `list`, looking for an `association` whose key is identical to the
argument to the `lookup`
- are commonly overloaded
- provide nearly ever feature of the Paws object system, from libside:
- inheritance
- scope, closure
- routines as “methods”
- cloning
- could also implement such non-trivial things as:
- HTTP requests
- database queries
- since every datatype in the Paws ecosystem *looks* like a `list`, every datatype responds to lookups
- “a lookup” may be attributed with intent to ‘ignore’ particular objects
- by default (in Paws’ Core), lookups will ignore `association`s
- since most lookups *result in* an association, it’s essential to chaining lookups that you be able to
introspect the value of the association instead of the association itself
- for instance, if `foo bar` returns an association such as `(, ‘foo’, 123)`, you would want the `add`
in `foo bar add(4)` to be looked up and executed on `123`, not the `association` containing `123`
- the same is true for `ghost`s, because asynchronous `routine`s return `ghost`s, but you often want to
operate on the actual results of the `routine`, not the `ghost`
- strings and numerics are created via lookups from ‘literals’
- appropriate-looking `string`, such as `23` or `“abc”` falls to the undefined handler
? (if strings cannot be created at run-time, how do we create `“foo”` from `““foo””`?)
- handler recognizes it and returns a new `string` or `numeric` instead
- `routine`
- **MORE**
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment