Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Now able to generate a single-page, HTML version, of the documentation. #4

Merged
merged 24 commits into from

2 participants

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Nov 27, 2011
  1. @romac
  2. @romac
  3. @romac
  4. @romac

    Implement a basic Markdown to HTML converter.

    romac authored
    Featuring pandoc and Pygments.
  5. @romac

    Remove 'intro' header.

    romac authored
  6. @romac

    Give the document some style.

    romac authored
  7. @romac

    pandoc needs moar options.

    romac authored
  8. @romac
  9. @romac

    Lighter text.

    romac authored
  10. @romac

    Fix a typo.

    romac authored
  11. @romac
  12. @romac

    Beware of Git.

    romac authored
  13. @romac

    Fix the Git workflow.

    romac authored
  14. @romac

    Improve the Git workflow, again.

    romac authored
  15. @romac
  16. @romac

    Check the master out back.

    romac authored
  17. @romac
  18. @romac
  19. @romac
  20. @romac
  21. @romac

    Rename README to README.md.

    romac authored
  22. @romac
  23. @romac

    Add "how to build the doc ?"

    romac authored
  24. @romac

    Add a GitHub ribbon.

    romac authored
This page is out of date. Refresh to see the latest.
View
2  .gitignore
@@ -1,4 +1,6 @@
.libs
+_build/
*.swp
*.swo
*_tmp/
+.DS_Store
View
5 000-intro.md
@@ -0,0 +1,5 @@
+
+Introduction
+============
+
+*Coming soon, hopefully.*
View
45 constructors.md → 001-constructors.md
@@ -1,18 +1,19 @@
+
Constructors
============
-Intro
------
-
In ooc, unlike Java/Scala/C++/C#, 'new' isn't a keyword, but a static method.
For example:
+~~~
dog := Dog new("Pif")
+~~~
However it's uncommon to directly define a new method. Instead, an init method is
defined, like this:
+~~~
Dog: class {
name: String
@@ -20,10 +21,12 @@ defined, like this:
init: func (=name) {}
}
+~~~
When an 'init' method is defined, a corresponding 'new' static method is defined, in our case,
the code above is equivalent to:
+~~~
Dog: class {
name: String
@@ -39,9 +42,11 @@ the code above is equivalent to:
}
}
+~~~
'alloc' is a method of Class, which can be defined like this, for example:
+~~~
/// Create a new instance of the object of type defined by this class
alloc: final func ~_class -> Object {
object := gc_malloc(instanceSize) as Object
@@ -50,6 +55,7 @@ the code above is equivalent to:
}
return object
}
+~~~
In ooc implementations, Object and Class are often classes defined in .ooc source
files, so you can easily study their source code. You can typically find their definitions
@@ -60,14 +66,17 @@ Reminder: member-arguments and assign-arguments
This:
+~~~
DiceRoll: class {
value: Int
init: func (=value) {}
}
+~~~
is the equivalent of this:
+~~~
DiceRoll: class {
value: Int
@@ -75,9 +84,11 @@ is the equivalent of this:
this value = value
}
}
+~~~
which is the equivalent of this:
+~~~
DiceRoll: class {
value: Int
@@ -85,6 +96,7 @@ which is the equivalent of this:
this value = value
}
}
+~~~
Ie '.' allows 'value's type to be inferred from the member variable
of the same name, and '=' does the same plus assigns it in the constructor.
@@ -107,6 +119,7 @@ method.
You can also call a super-constructor with super()
+~~~
Dog: class {
name: String
@@ -118,6 +131,7 @@ You can also call a super-constructor with super()
init: func (=name) {}
}
+~~~
Inheritance
-----------
@@ -125,6 +139,7 @@ Inheritance
A common mistake is to think that constructor are inherited, because they are standard
methods. However, this behavior would be harmful, as explained in the following example:
+~~~
Logger: class {
prefix: String
@@ -147,12 +162,15 @@ methods. However, this behavior would be harmful, as explained in the following
output write(prefix). write(msg). write('\n')
}
}
+~~~
What would happen if the first constructor defined in Logger was available
for FileLogger? Let's find out
+~~~
warn := FileLogger new("WARN")
warn log("Somebody set us up the stacktrace")
+~~~
The constructor call, if it was valid, would either return a Logger, which is
not what we want, or by some miracle trick, return a FileLogger - but one
@@ -164,6 +182,7 @@ Super func (and beyond)
However, there are times when one truly wants to relay a constructor
in an inherited class, such as:
+~~~
Expression: abstract class {
eval: abstract func -> Int
}
@@ -177,6 +196,7 @@ in an inherited class, such as:
Add: class extends BinaryOp {
init: func ~lr (=left, =right) {}
}
+~~~
Repeating the 'init~lr' definition in Add violates the Don't Repeat Yourself (DRI)
principle. Besides, if functionality is added to the base BinaryOp init~lr, it
@@ -184,29 +204,18 @@ wouldn't be replicated in Add init~lr.
For this precise case, the 'super func' construct exists:
+~~~
Add: class extends BinaryOp {
init: super func ~lr
}
+~~~
This behaves exactly as if we had written:
+~~~
Add: class extends BinaryOp {
init: func ~lr (.left, .right) {
super(left, right)
}
}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+~~~
View
22 covers-vs-classes.md → 002-covers-vs-classes.md
@@ -1,3 +1,4 @@
+
When to use covers and classes
==============================
@@ -17,6 +18,7 @@ By-reference, by-value
Classes are by-references. Which means every object is a reference. Doing that:
+~~~
Number: class {
value: Int
init: func (=value) {}
@@ -33,6 +35,7 @@ Classes are by-references. Which means every object is a reference. Doing that:
answer := Number new(42)
modify(answer) // does nothing
modifyInside(answer)
+~~~
What happens in 'modifyRef' is that we change what 'n' refers to in the
modifyRef function. It doesn't modify what 'n' referred to in the first place,
@@ -50,24 +53,29 @@ Covers are trickier. There are two types of covers: primitive covers, and compou
Primitive covers allow to add methods to an existing type. For implementations
of ooc on top of C, it means you can do stuff like:
+~~~
Int: cover from int
+~~~
And it's actually the way all C types are used from ooc.
As a consequence, covers are by-value. Which means that
+~~~
modify: func (i: Int) {
i = -1
}
answer := 42
modify(answer)
+~~~
Doesn't modify answer.
But compound covers (you can think of them as structs) are also by value,
which means that:
+~~~
Number: cover {
value: Int
}
@@ -80,6 +88,7 @@ which means that:
answer value = 42
modifyInside(answer)
+~~~
Won't modify 'answer' at all, but a *copy* of it that has been
passed to 'modifyInside'.
@@ -88,18 +97,22 @@ As an interesting side effect, a 'clone' method is futile for covers.
It also means that this won't work:
+~~~
Number: cover {
value: Int
init: func (=value) {}
}
+~~~
Because init will be working on a *copy* of the object, thus leaving
the original object unmodified. That's why func@ exists, ie.:
+~~~
Number: cover {
value: Int
init: func@ (=value) {}
}
+~~~
Where 'this' will be passed by reference. Same goes for any cover method
that modifies its content.
@@ -109,15 +122,19 @@ Heap allocation, stack allocation
When you do
+~~~
NumberClass: class {}
n := NumberClass new()
+~~~
n may be allocated on the heap or on the stack, however the compiler sees fit.
However, with:
+~~~
NumberCover: cover {}
n: NumberCover
+~~~
n is allocated on the stack.
@@ -174,6 +191,7 @@ Stack-allocated variables are deallocated when they go out of scope.
What does that mean? It means that this code is wrong.
+~~~
getAnswer: func -> Int* {
// answer is allocated on the stack
answer := 42
@@ -186,9 +204,11 @@ What does that mean? It means that this code is wrong.
answerPtr := getAnswer()
"answer = %d" printfln(answerPtr@)
+~~~
Whereas this one will work perfectly:
+~~~
getAnswer: func -> Int* {
// answer is allocated on the heap
answer := gc_malloc(Int size)
@@ -202,6 +222,7 @@ Whereas this one will work perfectly:
answerPtr := getAnswer()
"answer = %d" printfln(answerPtr@)
+~~~
However, the first version (returning the address of a local variable)
might work sometimes: don't be surprised. If the memory address (on the stack
@@ -221,4 +242,3 @@ However, keep in mind that allocation is often not the first place to look
if you want to optimize your application. Remember to always use a profiler
(I find that valgrind + KCachegrind work particularly well with ooc code)
to figure out where the hotspots are in your code.
-
View
50 first-class-functions.md → 003-first-class-functions.md
@@ -1,3 +1,4 @@
+
First-class functions
=====================
@@ -8,9 +9,11 @@ Functions are pieces of code that can take arguments, and return values.
Named functions are declared with this syntax:
+~~~
<name> : func <arguments> <return type> {
<body>
}
+~~~
Where arguments are comma-separated, enclosed between parenthesis, and return type
is prefixed with a right arrow ->.
@@ -20,23 +23,29 @@ may be omitted too, if the function is void.
Example:
+~~~
max: func (a, b: Int) -> Int {
a > b ? a : b
}
+~~~
But this is a valid expression too:
+~~~
func <arguments> <return type> {
<body>
}
+~~~
And with decl-assign, we can declare a variable named 'max', equal
to this expression. And then use it very much like a function
+~~~
max := func (a, b: Int) -> Int {
a > b ? a : b
}
answer := max(-1, 42)
+~~~
Differences between function and first-class functions
------------------------------------------------------
@@ -44,6 +53,7 @@ Differences between function and first-class functions
The first difference is: functions are immutable. First-class functions
are variables, and thus can be overwritten by simple assignment.
+~~~
// this is invalid: don't do that.
someFunc: func {}
someFunc = someOtherFunc
@@ -51,10 +61,12 @@ are variables, and thus can be overwritten by simple assignment.
// this, on the other hand, is valid
someFunc := func {}
someFunc = someOtherFunc
+~~~
The second difference is: first-class functions can capture context.
Closures are first-class functions that capture context.
+~~~
// here's a normal function
clone: func (l: List<Int>) -> List<Int> {
copy := ArrayList<Int> new(l size())
@@ -63,12 +75,15 @@ Closures are first-class functions that capture context.
})
copy
}
+~~~
Here, our anonymous, first-class function which also happens to be a closure, is
+~~~
func(element: Int) {
copy add(element)
}
+~~~
It captures the context because we access 'copy' in it - which isn't an
argument of the function, nor a variable declared inside the function.
@@ -84,22 +99,28 @@ The type of first-class functions
So, when we do:
+~~~
max := func (a, b: Int) -> Int {
a > b ? a : b
}
+~~~
What exactly is the type of 'max' ?
Let's declare it in two steps instead:
+~~~
max : Func (Int, Int) -> Int
max = func (a, b: Int) -> Int {
a > b ? a : b
}
+~~~
`Func` is a type that has a special syntax:
+~~~
Func <argument types> <return type>
+~~~
As with regular functions declaration, both argument types and return types
can be omitted.
@@ -111,52 +132,65 @@ Declaring the type of first-class functions is mostly useful in function argumen
For example, in the SDK, the declaration of each goes like this:
+~~~
List: class <T> {
each: func(f: Func (T)) {
// ...
}
}
+~~~
So it takes a function that takes one argument of type T
Hence, clearly doing that in our clone function above:
+~~~
l each(func(element: Int) {
copy add(element)
})
+~~~
Is unnecessary. Since we know that l is a List<Int>, and that each takes
a Func (T) then we know that element is of type Int.
And thus, we can write that:
+~~~
l each(|element|
copy add(elements)
)
+~~~
The proper syntax for that is
+~~~
call(|<name of arguments>|
<body>
)
+~~~
If there are no arguments, this is valid:
+~~~
call(||
<body>
)
+~~~
And is then equivalent to:
+~~~
call(func {
<body>
})
+~~~
The return type is inferred as well.
Other differences - member functions vs member first-class functions
--------------------------------------------------------------------
+~~~
Dog: class {
shout: func {
@@ -174,18 +208,22 @@ Other differences - member functions vs member first-class functions
d2 := Dog new()
d shout()
d2 shout()
+~~~
Prints:
+~~~
Woof woof
Ruff ruff
Ruff ruff
+~~~
When assigning 'Dog shout', we change the member method of *all* past and
future Dog instances. This happens because 'shout' is actually stored in the meta-class
Consider the differences with that instead:
+~~~
Dog: class {
shout := func {
@@ -203,21 +241,15 @@ Consider the differences with that instead:
d2 := Dog new()
d shout()
d2 shout()
+~~~
Prints:
+~~~
Woof woof
Ruff ruff
Woof woof
+~~~
Here, 'shout' is a member variable. Assigning to 'd shout' changes it
only for that instance, so d2 shout isn't changed.
-
-
-
-
-
-
-
-
-
View
96 generics.md → 004-generics.md
@@ -1,3 +1,4 @@
+
Generics
========
@@ -9,6 +10,7 @@ Generics are one of the most commonly misunderstood features of ooc.
Many people attempt confuse them with templates (like in C++ or D) and are
surprised when things like this don't work:
+~~~
Vector2: class <T> {
x, y: T
init: func(=x, =y) {}
@@ -16,6 +18,7 @@ surprised when things like this don't work:
new(x + r x, y + r y)
}
}
+~~~
(Don't worry about the syntax for now, I'll get to it later)
@@ -39,13 +42,17 @@ methods on them, then what are they good for? Sure looks useless from here.
Well, here's one thing we can do, for example:
+~~~
identity: func <T> (val: T) -> T {
val
}
+~~~
Woha. What just happened here? Let's recap line by line.
+~~~
identity: func <T> (val: T) -> T
+~~~
Here, we declare a function named 'identity', with one type parameter named T,
taking one parameter named 'val', and returning a value of type T.
@@ -58,20 +65,26 @@ When you declare a type parameter, it tells the compiler about a new type,
that we know nothing about at compile-time. Well, not nothing. Remember
classes? Here's how we access the class of an object:
+~~~
object class
+~~~
And if object was of type Carrot, that amounts exactly to doing just:
+~~~
Carrot
+~~~
What is that, exactly? It's an access to a class. What is a class? An instance
of Class, which is declared in lang/CoreTypes.ooc If you actually go on and open
CoreTypes, here is a simplified version of what you will find:
+~~~
Class: class {
- name: String
- size, instanceSize: SizeT
+ name: String
+ size, instanceSize: SizeT
}
+~~~
(Reminder: SizeT can be used to store the size of something. On 32-bits
platforms, it's 32-bits wide. On 64-bits platforms, it's 64-bits wide, and so
@@ -81,8 +94,10 @@ So back to our generic stuff. I said we knew nothing about generic types. And
in fact, it was a downright lie. Please accept my apologies. The reality is -
we know all that matters! If you try to execute the following piece of code:
+~~~
test: func <T> (t: T) { T class name println() }
test(42)
+~~~
You'll find out something very strange and puzzling.. it prints "Class" !
@@ -90,11 +105,13 @@ We just discovered that we can access type parameters just like any other
variable. And since T is a class, and we can access various fields of a class,
here's what we can do:
+~~~
test2: func <T> (t: T) {
- "name = %s, size = %zd, instanceSize = %zd" printfln(
- T name, T size, T instanceSize)
+ "name = %s, size = %zd, instanceSize = %zd" printfln(
+ T name, T size, T instanceSize)
}
test2(42)
+~~~
This will likely print something like "name = Int, size = 4, instanceSize =
4".
@@ -109,7 +126,9 @@ But I digress. (Then again, you're the curious one - not me.)
So let's analyze the second line of our 'identity' function above:
+~~~
val
+~~~
Let's see. It's the last line of a non-void function, so it means it's
returned. 'val' refers to a variable declaration which happens to be a
@@ -119,9 +138,11 @@ last line to yourself two or three times to impreign it into your brain)
So basically what our function does is... just pass through what we give it as
an argument! Let's try that
+~~~
42 toString() println() // just to be sure
identity(42) toString() println() // still a little trivial
identity(identity(identity(identity(42)))) toString() println() // whoa.
+~~~
Yup, it prints 42 alright.
@@ -138,9 +159,11 @@ Generic type inference
Let's do a little experiment:
+~~~
a := 42
b := identity(42)
"%s and %s" printfln(a class name, b class name)
+~~~
What did you get? Int and Int, right? But - but the return type of 'identity'
is T! Shouldn't b's type be T too?
@@ -156,11 +179,15 @@ all.
You see, when you call:
+~~~
identity(42)
+~~~
And the definition of identity is
+~~~
identity: func <T> (val: T) -> T
+~~~
Here's what the compiler figures out: well, we have one unknown type (that
is, generic type), called 'T'. Also, the first (and only) argument is of that
@@ -172,8 +199,10 @@ avoid tons of cast, and is good for your karma.
Here's another example.
+~~~
printTypeName: func <T> (T: Class) { T name println() }
printTypeName(Object)
+~~~
Then it prints "Object". Did we find a way to print strings without having to
enclose them between quotes? Hopefully not. That would be messy, man. Talk
@@ -188,8 +217,10 @@ T'. It is then not too big a challenge for the compiler to go from here.
Then again, we could have done:
+~~~
dumbPrintTypeName: func (T: Class) { T name println() }
dumbPrintTypeName(Object)
+~~~
Since we don't use T as a type anywhere. So why even bother with this \<T\>
thing, hmm? Why does the compiler even allow it? Read on if you want to find out.
@@ -200,8 +231,10 @@ Generic return types
Here's a little riddle for you. How does the compiler figure out the real return
type of this function:
+~~~
sackOfUnknown: func <T> -> T { 42 }
sackOfUnknown()
+~~~
Anyone? Ah, I see a hand in the back. What do you say? The type of the return
expression? WRONG. But that was an honest try. One point for effort.
@@ -221,18 +254,20 @@ So how do we make a function that
Well, that's precisely where that useless thing presented in the previous
section comes in very handy:
- theAnswer: func <T> (T: Class) -> T {
- match T {
- case Int => 42
- case Float => 42.0
- case String => "forty-two"
- case => Exception new("You're not worthy.") throw(); 0
- }
- }
+~~~
+ theAnswer: func <T> (T: Class) -> T {
+ match T {
+ case Int => 42
+ case Float => 42.0
+ case String => "forty-two"
+ case => Exception new("You're not worthy.") throw(); 0
+ }
+ }
rational := theAnswer(Int)
real := theAnswer(Float)
text := theAnswer(String)
theAnswer(Object) // ka-boom!
+~~~
What just happened? We used a match on 'T', which means we're comparing it.
We're comparing it with the types 'Int', 'Float', 'String', trying to return
@@ -249,23 +284,27 @@ do? Well - store them! That's the way all collections work.
Let's start with a simple one:
+~~~
Slot: class <T> {
element: T
- init: func (.element) { set(element) }
- set: func (=element) {}
- get: func -> T { element }
+ init: func (.element) { set(element) }
+ set: func (=element) {}
+ get: func -> T { element }
}
s := Slot new(3.14)
s get() toString() println()
s T name println()
+~~~
Not that bad, eh? (It should print 3.14 and Float - or some other type, if
you're in the future and ooc has a proper number tower)
But wait - get is defined like that:
+~~~
get: func -> T { element }
+~~~
And clearly T is a generic type, ie. it could be anything at runtime, and
*yet* the compiler figures it out right.
@@ -273,23 +312,31 @@ And clearly T is a generic type, ie. it could be anything at runtime, and
So what happens here? Let's look at the call, since it's the info from which
the compiler works to infer generic types:
+~~~
s get()
+~~~
Hmmph. Not many types there - except maybe.. the type of s. Which is what
exactly?
+~~~
s := Slot new(3.14)
+~~~
Well it turns out that Slot new is just a regular method call, the generic
type T is inferred to 'Float', and so 's' becomes a Slot\<Float\>
Hence, the compiler sees the get() call as:
+~~~
Slot<Float> get()
+~~~
And it sees the get definition as
+~~~
Sloat<T> get: func {}
+~~~
From here, inferring that T = Float is trivial.
@@ -300,7 +347,9 @@ One of the most advanced example of type inference in the whole SDK
is probably the List map() function. Here is its signature (ie.
definition without the body) :
+~~~
map: func <K> (f: Func (T) -> K) -> This<K>
+~~~
So basically it turns a List\<T\> into a List\<K\>, by calling f to turn
every T into a K. Makes sense.
@@ -311,14 +360,18 @@ to the function.
Well - no big deal then, if we do:
+~~~
intToString: func (i: Int) -> String { i toString() }
strings := numbers map(intToString)
+~~~
Then we know that K = String from the definition of intToString.
But wait, there's a nice infers-everything syntax for closures, ie.:
+~~~
stringsToo := numbers map(|x| x toString())
+~~~
And here, we're doomed. The closure insides attempts to infers its whole
signature (argument types, return type, etc.) from the type of the
@@ -339,28 +392,31 @@ How does it work under the hood?
Here is the naive implementation: generic type arguments as passed
as function arguments, ie a call to:
+~~~
ArrayList<Int> new()
identity(42)
+~~~
becomes (without mangling):
+~~~
ArrayList_new(Int_class());
identity()
-
+~~~
Type arguments in classes become variables:
+~~~
ArrayList: class <T> {}
+~~~
is
+~~~
ArrayList: class {
T: Class
}
+~~~
Class type arguments are assigned in the constructor to the appropriate
values.
-
-
-
-
View
26 properties.md → 005-properties.md
@@ -1,3 +1,4 @@
+
Properties
==========
@@ -14,13 +15,17 @@ and/or set (besides the actual memory read/write).
However, this results in long-winded and hard-on-the-eyes code such as this:
+~~~
setX(getX() + 1)
setY(getY() + 2)
setZ(getZ() + 3)
+~~~
When one come simply write, with regular variables
+~~~
(x, y, z) += (1, 2, 3)
+~~~
Which is much easier on the eyes.
(Read on 'tuples' for more information about multi-declaration / multi-assignment)
@@ -31,9 +36,11 @@ A dumb property
Turning a regular variable declaration into a property is as simple
as adding a pair of brackets {} after it.
+~~~
Tree: class {
age: Int {}
}
+~~~
At this point, 'age' behaves exactly as a variable, except that instead
of direct memory read/write, it's now modified via automatically-generated
@@ -41,18 +48,22 @@ getters and setters.
The above code is also equivalent to:
+~~~
Tree: class {
age: Int { get set }
}
+~~~
Or, if you prefer:
+~~~
Tree: class {
age: Int {
get
set
}
}
+~~~
Hooking on get and set
----------------------
@@ -60,6 +71,7 @@ Hooking on get and set
There's more to it. get and set can have a body, much like methods, except
without specifying argument types or return types.
+~~~
Tree: class {
age: Int {
get
@@ -68,6 +80,7 @@ without specifying argument types or return types.
}
}
}
+~~~
In this example, validation is done within the property setter.
It could be used to validate state transitions for a finite state machine,
@@ -83,11 +96,13 @@ variable of the same name existing.
For our tree class, we might define an 'old' property that is computed from
its 'age' property.
+~~~
old: Bool {
get {
age > 100
}
}
+~~~
NOTE: using undocumented 'magic numbers' in code is bad practice: don't do it.
Use constants with meaningful names instead - or better yet, make it configurable.
@@ -108,6 +123,7 @@ Foreign function interfacing
Properties setters and getters can be extern functions (ie. functions defined
outside ooc code). Let's take an example for a well-known GTK widget:
+~~~
use gtk
import gtk/[Gtk, Widget]
@@ -119,15 +135,7 @@ outside ooc code). Let's take an example for a well-known GTK widget:
get: extern(gtk_label_get_text)
}
}
+~~~
Once again, properties make code more readable and more straight-forward
to write.
-
-
-
-
-
-
-
-
-
View
46 tuples.md → 006-tuples.md
@@ -1,3 +1,4 @@
+
Tuples
======
@@ -22,6 +23,7 @@ How do we make a function that return several values?
You can use an array:
+~~~
// counter-example: don't do that
minmax: func (list: List<Int>) -> Int[] {
min := INT_MAX
@@ -33,13 +35,16 @@ You can use an array:
[min, max]
}
+~~~
But it's not practical, ie. if you want to retrieve min and max, you have to do:
+~~~
// counter-example: don't do that
result := minmax(mylist)
min := result[0]
max := result[1]
+~~~
We're using three lines only to retrieve results from a function.
@@ -52,6 +57,7 @@ Using an array doesn't allow different types, so
Let's try using a list of cells:
+~~~
// counter-example: don't do that
meanAndTotal: func (units: List<Unit>) -> List<Cell> {
total := 0
@@ -60,13 +66,16 @@ Let's try using a list of cells:
[Cell new(total), Cell new(mean)] as ArrayList<Cell>
}
+~~~
And to retrieve the values:
+~~~
// counter-example: don't do that
result := meanAndTotal(units)
total := result[0] get(Int)
mean := result[1] get(Float)
+~~~
Again, three lines, looks even uglier, no guarantees, not type-safe at
compile-time. Don't do that.
@@ -76,6 +85,7 @@ compile-time. Don't do that.
And here's the closest we'll come to a tolerable solution without using
tuples: out-parameters. Let's rewrite the minmax example with it
+~~~
// counter-example: don't do that
minmax: func (list: List<Int>, min, max: Int@) {
min = INT_MAX
@@ -85,16 +95,21 @@ tuples: out-parameters. Let's rewrite the minmax example with it
if(i > max) max = i
}
}
+~~~
And to retrieve the values:
+~~~
// counter-example: don't do that
min, max: Int
minmax(mylist, min&, max&)
+~~~
Two lines is better, but what if we do:
+~~~
minmax(mylist, null, null)
+~~~
That's valid ooc, won't be caught at compile-time, and yet crash.
So it's not the perfect solution we're looking for.
@@ -107,6 +122,7 @@ Multi-return using tuples - the solution
Tuples can be used to return multiple values from a function. Let's
rewrite our minmax function using that.
+~~~
minmax: func (list: List<Int>) -> (Int, Int) {
(min, max) := (INT_MAX, INT_MIN)
list each(|i|
@@ -115,6 +131,7 @@ rewrite our minmax function using that.
)
(min, max)
}
+~~~
The returned tuple and the declared function return type must
match exactly (e.g. same number and types of elements). Any mismatch
@@ -125,7 +142,9 @@ will result in a compile error.
We can retrieve all values by using a decl-assign with a tuple
on the left and a function call on the right
+~~~
(min, max) := minmax(mylist)
+~~~
The tuple and the return type of the function call must match exactly
(same numbers of elements). Any mismatch will result in a compile error.
@@ -143,11 +162,15 @@ There are ways to ignore some values, that are described in other sections.
In the minmax example above, we can retrieve only min if we want:
+~~~
min := minmax(mylist)
+~~~
It can even be used as an expression:
+~~~
"Minimum is %d" printfln(minmax(mylist))
+~~~
Which leads to this rule: **when a function returning multiple values
is used as if it returned only one, the first value is used.**
@@ -158,7 +181,9 @@ is used as if it returned only one, the first value is used.**
What if we want only max? We can use '_' in place of a name, in a
multi-variable declaration:
+~~~
(_, max) := minmax(mylist)
+~~~
However, there is no way to use it as an expression, it has to be
unwrapped first, with a multi-variable declaration.
@@ -170,14 +195,18 @@ interesting to least interesting**.
Take for example Process getOutput() in the sdk:
+~~~
getOutput: func -> (String, Int) {}
+~~~
The first returned value is what the process wrote to stdout, and
the second value is the exit code of the process.
The function used to be declared like that
+~~~
getOutput: func -> String {}
+~~~
And didn't allow to get the exit code. Adding functionality didn't
hurt compatibility at all though - no code broke, because of careful
@@ -193,20 +222,26 @@ on either side of a multi-variable decl-assign should match exactly.
For example, given this:
+~~~
plainWhite: func -> (Int, Int, Int, Int) { (1, 2, 3, 4) }
+~~~
The following lines are invalid:
+~~~
(one, two) := plainWhite()
(_, two) := plainWhite()
+~~~
Why? So that when incompatible changes are made to an API, you're
aware of it at compile-time, not at run-time.
However, both these lines are valid:
+~~~
one := plainWhite() // as we've seen before
(_, two, _) := plainWhite()
+~~~
Although plainWhite() returns 4 values, a tuple with only 3 elements
works.
@@ -215,11 +250,15 @@ works.
So that
+~~~
one := plainWhite()
+~~~
Is actually equivalent to:
+~~~
(one, _) := plainWhite()
+~~~
Tuples beyond return - multi-declaration and multi-assign
@@ -230,15 +269,12 @@ the assign operator (=) is valid.
Examples:
+~~~
(x, y, z) := (1, 2, 3)
(a, b) = (b, a)
+~~~
Swapping variables is valid, and should be supported by compliant
ooc compilers/runtimes.
-
-
-
-
-
View
25 version-blocks.md → 007-version-blocks.md
@@ -1,3 +1,4 @@
+
Version blocks
==============
@@ -6,19 +7,23 @@ Syntax
Version blocks use the following syntax:
+~~~
version (<version expression>) {
<body>
} else {
<alternative body>
}
+~~~
Where <version expression> can be any of:
+~~~
<version name>
!<version expression>
<version expression> && <version expression>
<version expression> || <version expression>
-
+~~~
+
Semantics
---------
@@ -68,11 +73,13 @@ on the compiler setting -gc=[off,static,dynamic].
Custom version names can be used, and turned on/off with the -D and -U compiler flags, for example:
+~~~
version(debug) {
"[%d] Saving database %s" println(timestamp(), db name)
}
db save()
-
+~~~
+
The code inside the version(debug) block will be compiled if -Ddebug is used. It is common practise for ooc developers
to use the -Ddebug switch to debug their applications.
@@ -89,6 +96,7 @@ methods/fields that are used in every OS.
Examples
--------
+~~~
version(windows) {
"Hi, Bill!" println()
}
@@ -107,6 +115,7 @@ Examples
} else {
"So you like your computer made of plastic then!" println()
}
+~~~
See also io/File and os/Time in the SDK for real-world examples of heavily versioned code.
@@ -116,6 +125,7 @@ Pattern for OS-specific classes
In ooc, 'new' isn't a keyword but a static method. As a result, you can define new yourself.
This allows an interesting pattern for OS-specific classes in ooc:
+~~~
// io/File
import io/[FileUnix, FileWin32]
@@ -144,13 +154,4 @@ This allows an interesting pattern for OS-specific classes in ooc:
// implement abstract methods for Win32
}
-
-
-
-
-
-
-
-
-
-
+~~~
View
66 Makefile
@@ -0,0 +1,66 @@
+
+TEMP_DIR:=$(shell mktemp -d -t /tmp)
+PANDOC=pandoc
+PREPROCESS=python _scripts/preprocess.py
+BUILD_DIR=_build
+ASSETS_DIR=_assets
+MD_OUTPUT=documentation.md
+HTML_OUTPUT=index.html
+
+all: init md2html build
+ @echo '-- The documentation has been successfully generated.'
+
+init:
+ @mkdir -p $(BUILD_DIR)
+
+cprsrc:
+ @echo -n '-- Copying the required assets into the temp folder...'
+ @ditto 0*.md* $(TEMP_DIR)/
+ @cp $(ASSETS_DIR)/stylesheet.css $(TEMP_DIR)/
+ @echo ' Done.'
+
+concat: cprsrc
+ @echo -n '-- Concatenating the Markdown files...'
+ @cat $(TEMP_DIR)/0*.md > $(TEMP_DIR)/$(MD_OUTPUT)
+ @echo ' Done.'
+
+preprocess:
+ @echo -n '-- Syntax-highlighting code blocks...'
+ @$(PREPROCESS) $(TEMP_DIR)/$(MD_OUTPUT) $(TEMP_DIR)
+ @echo ' Done.'
+
+md2html: concat preprocess
+ @echo -n '-- Converting the Markdown document to HTML...'
+ @$(PANDOC) --standalone \
+ --html5 \
+ --toc \
+ --section-divs \
+ --title="The ooc language" \
+ --css=stylesheet.css \
+ --template=$(ASSETS_DIR)/template.html \
+ --include-before-body=_assets/header.html \
+ -o $(TEMP_DIR)/$(HTML_OUTPUT) \
+ $(TEMP_DIR)/$(MD_OUTPUT)
+ @echo ' Done.'
+
+build:
+ @echo -n '-- Copying the required assets into the build folder...'
+ @cat $(TEMP_DIR)/highlight.css >> $(TEMP_DIR)/stylesheet.css
+ @cp $(TEMP_DIR)/index.html $(BUILD_DIR)/
+ @cp $(TEMP_DIR)/stylesheet.css $(BUILD_DIR)/
+ @echo ' Done.'
+
+publish:
+ @git checkout gh-pages
+ @rm -f stylesheet.css index.html
+ @cp $(BUILD_DIR)/stylesheet.css ./
+ @cp $(BUILD_DIR)/index.html ./
+ @git add .
+ @git commit -m "Update the documentation."
+ @git push origin gh-pages
+ @git checkout master
+
+clean:
+ @rm -rf $(TEMP_DIR) $(BUILD_DIR)
+
+.PHONY: all
View
5 README
@@ -1,5 +0,0 @@
-In progress, send feedback to amos@ofmlabs.org and mention 'ooc' in the subject.
-
-Thanks a lot =)
-
-Amos Wenger aka nddrylliog
View
24 README.md
@@ -0,0 +1,24 @@
+
+# the ooc language
+
+## status
+
+**In progress**, send feedback to amos@ofmlabs.org and mention 'ooc' in the subject.
+
+Thanks a lot =)
+
+Amos Wenger aka [@nddrylliog](http://twitter.com/nddrylliog)
+
+## how to build the doc ?
+
+Easy,
+
+ $ make
+
+will generate a single-page, HTML version of the documentation.
+
+Then,
+
+ $ make publish
+
+will switch to the `gh-pages` branch, commit the modifications and push the commits to `origin` (which may be your forked repository).
View
4 _assets/header.html
@@ -0,0 +1,4 @@
+
+<header>
+ <h1>the OOC language</h1>
+</header>
View
81 _assets/stylesheet.css
@@ -0,0 +1,81 @@
+
+@import url(http://fonts.googleapis.com/css?family=Radley:400&text=OC);
+@import url(http://fonts.googleapis.com/css?family=Lato:400,700,900);
+
+/* Stylesheet */
+
+body
+{
+ font-size: 16px;
+ font-family: Georgia, serif;
+ line-height: 1.6;
+ width: 720px;
+ margin: 0 0 0 400px;
+ color: #222;
+}
+
+a {
+ color: #439893;
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+header > h1 {
+ font-family: 'Radley', 'Lato';
+ font-weight: 400;
+ font-size: 5em;
+}
+
+h1, h2, h3, h4 {
+ font-family: 'Lato';
+ font-weight: 400;
+}
+
+h1 > a, h2 > a, h3 > a, h4 > a {
+ text-decoration: none;
+}
+
+body > section > h1 {
+ font-size: 2em;
+}
+
+section > section {
+ margin-top: 30px;
+ border-bottom: 1px solid #D9D9D9;
+ padding-bottom: 30px;
+}
+
+#TOC {
+ position: absolute;
+ top: 209px;
+ left: 0;
+ height: auto;
+ overflow: hidden;
+ padding: 20px;
+ font-size: 0.9em;
+ font-family: 'Lato';
+}
+
+#TOC ul {
+ padding-left: 20px;
+}
+
+#TOC > ul > li > ul > li {
+ width: 250px;
+}
+
+#TOC > ul > li > ul > li > ul {
+ display: none;
+}
+
+#TOC > ul > li:first-child {
+ display: none;
+}
+
+.highlight {
+ font-family: Monaco, Menlo, Droid Sans, monospace;
+ font-size: 0.9em;
+}
View
112 _assets/template.html
@@ -0,0 +1,112 @@
+$if(html5)$
+<!DOCTYPE html>
+<html$if(lang)$ lang="$lang$"$endif$>
+$else$
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"$if(lang)$ lang="$lang$" xml:lang="$lang$"$endif$>
+$endif$
+<head>
+$if(html5)$
+ <meta charset="utf-8" />
+$else$
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+$endif$
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <meta name="generator" content="pandoc" />
+$for(author)$
+ <meta name="author" content="$author$" />
+$endfor$
+$if(date)$
+ <meta name="date" content="$date$" />
+$endif$
+ <!--<title>$if(title-prefix)$$title-prefix$ - $endif$$if(pagetitle)$$pagetitle$$endif$</title>-->
+ <title>$if(title-prefix)$$title-prefix$$endif$</title>
+$if(html5)$
+ <!--[if lt IE 9]>
+ <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
+ <![endif]-->
+$endif$
+$if(highlighting-css)$
+ <style type="text/css">
+/*<![CDATA[*/
+$highlighting-css$
+/*]]>*/
+ </style>
+$endif$
+$for(css)$
+ <link rel="stylesheet" href="$css$" $if(html5)$$else$type="text/css" $endif$/>
+$endfor$
+$if(math)$
+ $math$
+$endif$
+$for(header-includes)$
+ $header-includes$
+$endfor$
+</head>
+<body>
+$for(include-before)$
+$include-before$
+$endfor$
+$if(title)$
+$if(html5)$
+<header>
+$else$
+<div id="$idprefix$header">
+$endif$
+<h1 class="title">$title$</h1>
+$for(author)$
+<h3 class="author">$author$</h3>
+$endfor$
+$if(date)$
+<h4 class="date">$date$</h4>
+$endif$
+$if(html5)$
+</header>
+$else$
+</div>
+$endif$
+$endif$
+$if(toc)$
+$if(html5)$
+<nav id="$idprefix$TOC">
+<h1>Table of contents</h1>
+$toc$
+</nav>
+$else$
+<div id="$idprefix$TOC">
+$toc$
+</div>
+$endif$
+$endif$
+$body$
+$for(include-after)$
+$include-after$
+$endfor$
+<a href="http://github.com/nddrylliog/the-ooc-language"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://a248.e.akamai.net/assets.github.com/img/71eeaab9d563c2b3c590319b398dd35683265e85/687474703a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f677261795f3664366436642e706e67" alt="Fork me on GitHub" /></a>
+<script>
+ window.onscroll = ( function()
+ {
+ return;
+
+ var pinned = false,
+ toc = document.getElementById( 'TOC' ),
+ offset = 200;
+
+ return function()
+ {
+ if( !pinned && document.body.scrollTop > offset )
+ {
+ toc.className = 'pinned';
+ pinned = true;
+ }
+ else if( pinned && document.body.scrollTop < offset )
+ {
+ toc.className = '';
+ pinned = false;
+ }
+ };
+
+ } )();
+</script>
+</body>
+</html>
View
33 _scripts/preprocess.py
@@ -0,0 +1,33 @@
+
+import sys
+import re
+
+from pygments import highlight
+from pygments.formatters import HtmlFormatter
+from pygments.lexers import OocLexer
+
+pattern = re.compile(
+ r'(~{3,})(.+?)\1',
+ re.S
+)
+
+formatter = HtmlFormatter( style='trac' )
+lexer = OocLexer()
+
+sourceFile = open( sys.argv[ 1 ], 'r+' )
+source = sourceFile.read()
+
+matches = pattern.finditer( source )
+
+for match in matches:
+ source = source.replace( match.group(), highlight( match.group( 2 ), lexer, formatter ) )
+
+sourceFile.seek( 0 )
+sourceFile.write( source )
+sourceFile.close()
+
+css = formatter.get_style_defs( '.highlight' )
+cssFile = open( sys.argv[ 2 ] + '/highlight.css', 'w' )
+cssFile.write( '\n/* Pygments CSS for style "trac" */\n\n' )
+cssFile.write( css )
+cssFile.close()
Something went wrong with that request. Please try again.