(a copy of) Potion: A Short Pamphlet

ポーション

A Short Pamphlet

by why the lucky stiff

Well, here we go. You want to learn Potion1? Sure, okay. But first, bear in mind that Potion isn’t done yet. And it does very little. It’s completely esoteric. So long as we’ve got that straight.

So why exactly are you learning this nascent do-nothing probably-broken language? For fun, right? I’m giving you till the end of sentence to come up with a good reason.

This pamphlet probably isn’t for beginners to computer programming. Just for those curious about what Potion is like.

An Understanding

Potion’s mantra is: Everything is an object. But objects aren’t everything.

Etched beneath that in faint pencil: Oh, and everything is a function.

Special Things?

So is there anything unusual about Potion? Anything of particular interest?

Potion is inspired by fellow languages Io, Ruby, OCaml, Lua, REBOL and C. In that order. (Also influenced by the works of Ian Piumarta, Nicolas Cannasse and Basile Starynkevitch.)

A Sip

Let’s start with some code.

Ad Nauseam


  loop: 'quaff' print.

I know this isn’t terribly useful, but it’s an infinite printing of the string 'quaff'.

A colon starts a code block. And the period ends it. The loop command then runs the code block endlessly. You will see the colon and period combination reused throughout Potion.

The print message is sent to the string 'quaff'. Strings are an object, like everything. They receive messages. Messages are separated from objects by a space. (In most languages, you use a dot to separate messages. But, like English, the period signifies the end of something rather than a separation.)

A List



  ('cheese', 'bread', 'mayo') at (1) print

This one prints the message ‘bread’. The stuff in parentheses is a list. We have a list of foodstuffs. And it’s being sent a message named at. Every list has an at message that looks up an item by its position in the list.

Notice that after the at message is another list. The 1 is an argument to at. It’s the position we want to look up. It looks like a list (and it is a list,) but we call it an argument because it comes after a message.

A Table



  (language='Potion', pointless=true) at (key='language') print

Okay, this one looks similar to the list, but it’s not. Here we have a table. The table pairs up things. The string 'language' is paired up with the string 'Potion'.

Notice the arguments are also a table. Lists and tables are sort of interchangeable. You can use a table or a list as arguments.

The Functional Side

Functions are throughout Potion. Whether it be anonymous lambdas, blocks or type functions.

A Function


  minus = (x, y): x - y.
  minus (y=10, x=6)

This one illustrates a bit better how tables get used as argument lists. We have the minus variable which contains a function. The function subtracts y from x. In this case, it’ll return -4.

(This is similar to keyword arguments in Lua and Python, yes. However, it’s important to see that lists and tables and arguments in Potion all share the same syntax. Less to remember.)

A List as a Function


  foods = ('cheese', 'bread', 'mayo')
  foods (2)

Here’s a case where a list is being called as a function. Yes, everything is a function! We could also have called: foods (index=2).

Strings, tables, numbers are also functions. The following returns the 3rd character of the string.


  "ヘ(^_^ヘ)(ノ^_^)ノ" (2)

Even functions are functions! I invented this concept. Just like Steve Jobs will one day.

A Block


  (dog='canine', cat='feline', fox='vulpine') each (key, val):
    (key, ' is a ', val) join print.

Functions can also be attached to methods, for use as anonymous blocks (as in Ruby.)

These blocks are merely the last argument. This also works: each ((key, val): key print.).

The Object-Oriented Side


  Person = class: /name, /age, /sex.
  Person print = ():
    ('My name is ', /name, '.') join print.

The above describes a class in Potion. Objects are very memory-efficient. Each Person object will store three properties: the name, age and sex. (These are not kept in a hashtable. They are kept in memory, immediately following the object’s header.)

Properties use a slash before their name. This comes from the computer filesystem, where slashes are used before the names of folders in which to store files.

However, if you are desperate to use an object as a hashtable, you can store anything you like in an object’s method table. Not just methods can go there. Anything can be wrapped in a closure.

Making Objects


  p = Person ()
  p /name string print

Yep, classes are functions, too! They create objects.

In this case, the name of p hasn’t been set, so the code will print nil.

But how does subclassing work?

A Subclass


  Policeman = Person class (rank): /rank = rank.
  Policeman print = ():
    ('My name is ', /name, ' and I'm a ', /rank, '.') join print.

  Policeman ('Constable') print

The class message just gets sent to the parent class. And that’s it.

A Policeman now has four properties: /name, /age, /sex and /rank.

Notice the first line of the code. The end of the statement is a block. That block is the object’s constructor. So, in the last line, we’re passing in the string ‘Constable’ as the rank argument.

Licks


  app = [window (width=200, height=400)
          [para 'Welcome.', button 'OK']]
  app first name


Lastly, here we have a lick. This is the data language brought up earlier in the section about Special Things. This code will print ‘window’ since that’s the name of the first item in the lick.

Two languages in one? What for? I mean you can do anything you want from code, right?

There can be problems with expressing data in code. Say the above example was written in Potion code. Some thing like this:


  app = window(width=200, height=400):
    para 'Welcome.'
    button 'OK'.


In order to get this to work, you need methods for window, para and button. This could clutter the namespace. Also, under what context are those messages available? Are they methods of the created window? Or do they go through some kind of proxy object? We don’t know what’s going on behind this code. (Which isn’t bad at all, if it works.)

By having a separate little data language, you can build tree structures of arbitrary elements (akin to HTML) which act as a kind of common structure between Potion libraries. (You can also think of it as code which has been parsed, but not executed.)

The Flexibility Of…

Licks generally follow the look of Potion. Strings can be quoted. Tables are curved on the edges.


  [name (attr1='string', attr2=10) 'TEXT HERE']

Every lick can have a name, a table of attributes, and a list of children. The list of children can, instead, be a Potion data type, such as a number or string or something. (No, this isn’t a new idea. It’s very much like E4X2, but without XML.)

However, licks also allow unquoted strings, to give you some added flexibility.


  math = Grammar [
    digit  <- n:[0-9] { n number }
    value  <- d:digit+ | '(' e:expr ')' { d or e }
    expr   <- l:value op:[*/] r:value
    {
      if (op == '*'): l * r. else: l / r.
    }
    main   <- expr
  ]

Here we have four entries in a lick, getting passed to the Grammar method. The entries are: digit, value, expr and main. (So, for example, the digit entry will be paired with the string "<- n:[0-9] { n number }".

The lick syntax keeps track of nested groups of parentheses, square brackets and curly braces. The unquoted mode merely needs to start with a letter, number or non-token character.

Be careful of commas in unquoted strings. This won’t work:


  [dollars $10,000]

You’ll end up with two entries in the lick: a dollars entry and a 000 entry.

Commas are okay inside of nested groups, though. So, this would be okay:


  [dollars $(10,000)]

Pause For Effect

Okay, let’s stop. So you’ve basically seen everything in the language already. Sorry about that. It kind of blows that there’s no surprises left. :( But, hey, I said it’s little, right?

Are you starting to see some patterns in this code?

Potion Syntax

Now that you have a feel for what Potion can do, let’s talk about every one of Potion’s tokens in detail.

Encoding

Potion source code is always in UTF-8. Likewise, all Potion strings are UTF-8. Potion is too small to include other encodings in its core API.

Lines

Potion code lines are separate by a newline. (Or a CR-LF works as well.)


  x = 1
  y = 2


Throughout Potion, a comma is equivalent to a newline.


  x = 1, y = 2

This also means that tables can be written using newlines as separators:


  (language='Potion'
   pointless=true)


Spaces

Spaces are used to separate messages and objects and operators, but they usually are only used for clarity’s sake.

To borrow an earlier example, it turns out the following is legit.


  ('cheese','bread','mayo')at(1)print

There is some flexibility, in order to avoid senseless syntax errors.

Comments

Lines preceded by the octothorpe are ignored by Potion.


  # this foul business...
  String length = (): 10.

Code Blocks

Lines can be grouped together into blocks. Think of it as a collection of code you’ve group together to run later. Blocks start with a colon and end with a dot.


  block = :
    'potion' print.


Built-in Types

Potion has a rather small set of built-in types and libraries. This is to keep the language core small for folks who want to embed Potion and to constraint its memory footprint.

True, False, Nil.

Potion has three keywords for these built-in types.

nil indicates that a variable is empty: it has no value and no type. (This isn’t exactly true, though. Its class is NilKind.)

true and false are boolean values belonging to the Boolean class.

Anything which is not nil or false is considered a positive value. (Which means that the number zero is considered true in if statements.)

Numbers

Basic numbers take up no memory and are passed as plain (32-bit or 64-bit, depending on your processor) integers.


  5
  47
  -25
  0xFF

Decimal numbers use a boxed double-precision floating point number.


  999.9
  2.00001
  2e+2
  2.4e-8


Arbitrary-precision math is planned for 1.0 release.

Strings

Strings begin with a single quote, followed by a series of UTF-8 characters, then a final single quote. There is only one escape code: '' for an embedded single quote.


  'Cornelius'
  'Tuesday
  Jun 29th, 2009'
  'C:\Program Files\Potion'


Double-quoted strings allow a number of escape codes.

Expressions

In my opinion, a language can be called “functional” if every statement and expression returns a value. Even if statements or class definitions. This is just how Potion is designed. Every statement is a function, since it takes arguments and returns results. There is no void.

if, elsif, else

The if keyword tests its arguments for truth. Its block is then executed if they pass.


  if (age > 100): 'ancient'.

Like any function, if returns a result. If the condition fails, you get nil. Otherwise, you get the result of the code executed inside the block.

Beyond if, you can chain elsif and else to catch other conditions.


  author =
    if (title == 'Jonathan Strange & Mr. Norrell'):
      'Susanna Clarke'.
    elsif (title == 'The Star Diaries'):
      'Stanislaw Lem'.
    elsif (title == 'The Slynx'):
      'Tatyana Tolstaya'.
    else:
      '... probably Philip K. Dick'.


These three keywords can all be executed like functions, even though they are compiled into basic instructions.

loop

The loop keyword executes its block endlessly.


  loop: 'quaff' print.

while

The while keyword will execute its block endlessly, as long as its arguments stay true.


  count = 8
  while (count > 0):
    'quaff' print
    count--.

to

Potion has no for keyword. One alternative is the to keyword.


  1 to 5 (a):
    a string print.

The to keyword will loop from the first number, up to the last.

Another option is the times method, which starts at zero.



  5 times: 'Odelay' print.

Notice how the block doesn’t need parentheses if we don’t want the block arguments coming in.

return

Returns a value from the function. You only need to use the return keyword explicitly if you’re looking to quit in the middle of the function.

Names and Objects

The most expressive parts of a program are the naming and creation of unique objects.

Variables

Generally, any UTF-8 set of characters which isn’t seen as a built-in type or operator can be used as a variable. Variables are assigned with a plain equals sign.


  t = true
  $$ = [dollars 100]
  HTTP = 'Hypertext Transfer Protocol'
  わが身 = self 

You must set a variable before using it (even if just to nil.) Otherwise, Potion sees a message, sent to self.

Messages

Messages follow the same rules as variables. A message name can be any UTF-8 character which isn’t a built-in type or operator.


  Number $ = ():
    [dollars (amount=self)].
  100 $


In this example, the $ message converts the number into a kind of currency lick.

Queries

Objects can be asked if they respond to a message, by prefixing the message with a question mark.


  if (3 ?gender):
    "Huh? Numbers are sexed? That's amazing." print.

You can also optionally execute the message by send it arguments. And you can space out the quiz mark, if you want.


  3 ? gender ()

Since numbers don’t have a gender method, this’ll give nil rather than an error.

A better example of this would be if you are in a web application and you wanted to see if the query string contained a session entry.


  HomePage get = (url):
    session = url query ? at ('session').

That way if query is empty, you won’t get an error, you’ll just get nil. Assuming that query is a table, though — you’ll get back the value filed under the 'session' key.

Paths

A path is any set of non-whitespace UTF-8 characters preceded by a slash. A path is also called an “instance variable” in programming jargon.


  BTree = class: /left, /right.
  b = BTree ()
  b /left = BTree ()
  b /right = BTree ()

Paths cannot be randomly added to the object after the object is created. Each object has a strict set of paths. Every path which is used in the constructor is added to the object upon creation.

Path Queries

As with methods, you can query an object to see if it has a given path.


  BTree = class: /left, /right.
  b = BTree ()

  if (b ? /left):
    'left path found!' print.

And That’s It?

Yes, that’s the important stuff for now. But a number of new features are coming to Potion soon.

Thankyou for reading along. I hope this was only a brief interruption to an otherwise lush and exotic life. I’m sorry to disappoint with so little. Perhaps one day I can make things right?

Footnotes

1 The programming language. Not the drink of fables.

2 ECMAScript for XML. (See also: s-expression.)

Potion’s License

Potion is free software, released under an MIT license — the very brief paragraphs below. There is satisfaction simply in having created this. Please use this how you may, even in commercial or academic software. I’ve had a good time and am want for nothing.


Copyright © 2009 why the lucky stiff

HOWEVER. The follow MIT licensed codes have been employed.

Be it known, parts of the object model taken from obj.c.
© 2007 Ian Piumarta

And, also, the design of the VM bytecode is from Lua.
© 1994-2006 Lua.org, PUC-Rio

The Mersenne Twister (MT19937)
© 1997-2002, Makoto Matsumoto and Takuji Nishimura

Lastly, khash.h.
© 2008, by Attractive Chaos

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.