Welcome to Conseptizer.org


Sitemap

Main page
  Download (soon)
  FAQ (soon)

About us


    Conseptizer
programmers' coding canvas
Summary: This is a programming language mostly influenced by Scheme and Forth. It attempts to be practical while retaining an implementation that can be realistically understood in its entirety. It is dynamically typed, lexically scoped, has a self-hosting compiler (which currently produces C), and uses semi-automatic memory management (based on regions). It is still in alpha stage.
"They who would give up 
essential Freedom, to purchase
a little Type-Safety, deserve
neither Freedom nor Type-Safety."

(Benjamin Franklisp)

The Conseptizer programming language project

We don't want to bother you too much with our philosophy. Instead, we will directly explain the features of our language.

We use a stack-based computation model, but we evaluate from right to left:

 
  str-suffix? ".png" File-Name

This will push two values on the stack and call a subroutine which leaves a bool on the stack. Identifiers beginning with upper case letters are for values, which will be pushed. All other identifiers are for subroutibes (subs), which will be called. (Literals like ".png" are pushed, of course.)

Closures are wrapped in parens, and you can define something with a colon:

 
  png?: (str-suffix? ".png")
  # now we can do:
  png? File-Name

  # also:
  ++: (+ 1)     # is a builtin
  half: (/ 2)   # divisor comes first
  mean: (half +)
  mean 1 5      # yields 3

While this allows tacit/point-free programming in the concatenative style, we do not emphasize this too much. It's often useful for refactoring, though. Note that we evaluate right-to-left, but top-to-bottom, i.e. newlines are not handled like spaces. This makes code look more like it consists of statements and expressions. (Instead of a newline, you can also use a comma.)

Putting things from the stack into local vars is easy:

 
  xcons: | A B (cons B A)
  single: | Obj (cons Obj nil)
  single 1   # yields (1)

As you can see, lists work like in Lisp. (Well, mostly; we replaced car and cdr with e and z, because they are shorter names.) Of course, for small subs, we often don't need local vars:

 
  xcons: (cons x)   # x is swap
  single: (xcons nil)

  # also:
  nth-cons: (times x %z)
  nth: (e nth-cons)
  nth 1 '(a b c)   # yields b, since we start w/ zero

  # add all numbers:
  sum: (each %+ x 0)

You probably guessed it: The percent sign is used to push a sub instead of calling it. As you can see, quoting works like in Lisp. (Well, mostly; we use quote objects instead of lists starting with the symbol 'quote'.)

Local definitions are easy:

 
  recons: | e-sub z-sub C
  (E: e-sub e C, cons E z-sub z C)

  recons %++ %-- cons 0 0   # yields a pair of 1 and -1

(Newlines in argument lists are ignored, so we can split args and code into different lines. If your code itself becomes too long for a single line, refactor it or use the backslash (\) to join lines.) Often you'll want to use the top-of-stack value, but keep it. You can use the dot (.) for duplicating it, and 'drop' for removing it:

 
  single?: (if (cons? .) (nil? z) (drop, false))

  single? '()        # false
  single? '(a)       # true
  single? '(a b c)   # false
  single? "hello?"   # false

Most of the time, we refactor our code if it uses 'drop'. Or any primitive stack operation other than '.' and 'x', really. That's because it's easy to get stack ordering wrong. The stack should be a convenient language feature, not something that makes your life hard.

We have a feature that helps to quickly write recursive subs: The @-sign in front of a closure binds the closure itself to 're':

 
  len: @| L (if (nil? L) (0) (++ re z L))

You can have arguments directly after the @-sign; they will be kept when calling 're'!

 
  map: @ sub (when (cons? .) (recons %sub %re))
  map (* 2) '(1 2 3)   # yields (2 4 6)

Of course, you can have arguments both after the @ and the |. This is useful sugar because often some args to a recursive function stay unchanged. So it makes code shorter, more explicit and faster (less push'n pop).

So far, we ignored memory allocation. Well, we allocated, but we didn't free anything. There's an implicit stack of memory regions, and we always allocate in the one on top. We can run a piece of code with a fresh region and automatically drop+free it afterwards:

 
  # show all non-zero elements:
  w/new-reg (print filter (not zero?) L)

We can also copy a value into the previous region. In the following example, we get rid of all garbage (like local bindings) allocated by 'filter'.

 
  w/new-reg1 (filter (not zero?) L)

It's up to you to find good places for using additional regions, though you can get some profiling help. Memory management with regions is a bit awkward when you want to modify global data structures. However, it can be extremely fast - and it is quite convenient for purely functional code. The implementation also doesn't need to be more complex in presence of multi-threading. So it has its good and bad sides.

Often it's useful to change (i.e. update) the value of a variable. This can be done by creating a pointer to the binding with the circumflex (^) and then changing it via the pointer.

 
  Num: 0
  !^Num 16   # the '!' sub changes the value

  inc!: | Ptr (! Ptr ++ get Ptr)
  times 8 (inc! ^Num)   # Num is now 24

  prepend!: | List-Ptr
  (! List-Ptr xcons get List-Ptr)

  filter: | is? # L -> New
  (New: nil
   each< | Item   # iterate in reverse order
         (when (is? Item) (prepend! ^New Item))
   New)

Sometimes it's really useful to just keep throwing values on the stack and then collect them into a list eventually. We've got a feature for that:

 
  [1 2 + 1 2]   # -> (1 2 3)

  append-many-lists: | List-Of-Lists
  ([each< %unlist List-Of-Lists])

  append-many-lists '((1 2 3) (4 5) (6 7 8))
  # -> (1 2 3 4 5 6 7 8)

The 'unlist' sub basically does the opposite: It pushes all the elements of a list onto the stack. There are various things that can be implemented much easier with '[' and ']'. For example, generating XML/HTML can be done like this:

 
  <a: (['a [)
  href=: (cons 'href)
  >: (])
  </a>: (])

  <a href="foo.htm"> "text" </a>
  # -> (a ((href \ "foo.htm")) "text")

That's about it. The core language has only about a dozen of features. That's not because we wanted some abstract minimalistic core model. It's because we wanted technology that can be realistically understood in its entirety. The translation to C (or even Assembly) is pretty straightforward. Yet, it has some pretty high-level features (like closures).

As soon as our compiler is somewhat more complete, we will publish it as Free Software, of course.


Copyright (C) 2016 Wolfgang Jaehrling

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is here.

Contact: <wolfgang at conseptizer org>

Hi, I'm Karla - the official mascot of Conseptizer!