# Q: functional/equational language, smells a little of Python

J

#### John J. Lee

Saw this on LWN:

http://q-lang.sourceforge.net/

Looks interesting, and reminiscent of symbolic algebra systems like
Mathematica. Also, the website mentions dynamic typing and "Batteries
Included", which made me think of Python. Damned silly name, though

Anybody here used it?

Snippet from the FAQ:

2. Yet another "Haskell for the poor"?

Not quite. Even though the syntax and some of the stuff in the
standard library looks superficially similar, Q is different in a
number of ways:

* First and foremost, Q is an equational rather than just a
functional language, meaning that it is based on general term
rewriting instead of the lambda calculus (which is just one, and
very special, rewriting system). In particular, Q has no a
priori distinction between "constructors" and "defined" function
symbols, and argument patterns are not restricted to
"constructor terms". This allows you to have symbolic evaluation
rules like the following distributivity rule: X*(Y+Z) =
X*Y+X*Z. Such rules are not allowed in ML and Haskell because
they violate the "constructor discipline". Moreover, the Q
interpreter also happily reduces expressions involving
variables, so that you can try your definitions with symbolic
inputs (e.g., map F [X,Y] will evaluate to [F X,F Y]), which is
quite useful for debugging purposes.

* While other functional languages are either "eager" or "lazy", Q
tries to give you the best of both worlds: It uses eager
(call-by-value) evaluation by default (which lends itself to an
efficient implementation), but also allows you to define your
own special forms, which can be used to implement both
call-by-name functions and lazy data constructors. Using special
forms you can also define your own "meta functions" which
manipulate arbitrary (not necessarily irreducible) expressions
as literal terms. This gives you full control over the reduction
strategy, where this is desired, as well as a kind of "macros"
and "self-modifying programs", since constructs like lambda
abstractions can be analyzed, transformed and constructed in
many ways before they are actually evaluated.

* Q uses dynamic typing. This has become a rare feature in
contemporary functional languages, which usually employ a
Hindley/Milner type system which offers more safety at the
expense of restricting polymorphism. Q gives you back the
flexibility of good old Lisp-style ad-hoc polymorphism and even
allows you to extend the definition of existing operations
(including built-in functions and operators) to your own data
types. Moreover, Q has a Smalltalk-like object-oriented type
system which supports data encapsulation and (single)
inheritance. This makes it possible to manage complex,
polymorphic data with ease.

* Q takes the pragmatic route in that it provides (monad-free)
imperative programming features such as imperative I/O and
mutable data cells, similar to the corresponding facilities in
ML. While one may argue about these, they certainly make life
easier when programming complex I/O stuff such as graphical user
interfaces.

* Last but not least, the Q programming system comes with
batteries included. There are quite a few add-on modules
interfacing to third-party libraries which, while not as
comprehensive as the facilities provided by traditional
scripting languages like Perl and Python, allow you to tackle
most practical programming problems with ease. In particular,
multimedia and computer music is an area where Q shines,
providing facilities to handle graphics, digital audio and MIDI
in a convenient and efficient manner (efficient enough for soft
realtime applications), which go well beyond what is currently
being offered for other functional languages.

Some of Q's flexibility comes at a price. In particular, Q's symbolic
evaluation capabilities as a term rewriting language dictate that the
language is essentially exception-free. This means that an
"ill-formed" expression (such as "division by zero" or, more
generally, the application of a function to data for which there is no
corresponding definition) does not, by default, raise an exception,
evaluates to itself. As a remedy, Q provides facilities to add
explicit error rules which raise exceptions, and to handle exceptions
in a suitable manner. Another limitation is that Q does not allow you
to have arbitrary local definitions in the style of Haskell and ML's
"let" and "where" clauses. In Q, "where" clauses are limited to
variable definitions (similar to Haskell's pattern bindings), but
local rewriting rules are not supported as they would wreak havoc on
the term rewriting semantics.

You will also notice that Q lacks most of the syntactic sugar found in
Haskell. This is partly due to my laziness, but it also keeps the
language and the core system simple. Using Q's symbolic processing
capabilities, the usual bits like lambda abstractions, pattern
matching conditionals and list comprehensions can easily be written in
Q itself (although this does not offer quite the same performance as
when they are provided as builtins, of course).

John

K

#### Kay Schluehr

John said:
Saw this on LWN:

http://q-lang.sourceforge.net/

Looks interesting, and reminiscent of symbolic algebra systems like
Mathematica. Also, the website mentions dynamic typing and "Batteries
Included", which made me think of Python. Damned silly name, though

Anybody here used it?

Snippet from the FAQ:

2. Yet another "Haskell for the poor"?

Not quite. Even though the syntax and some of the stuff in the
standard library looks superficially similar, Q is different in a
number of ways:

* First and foremost, Q is an equational rather than just a
functional language, meaning that it is based on general term
rewriting instead of the lambda calculus (which is just one, and
very special, rewriting system). In particular, Q has no a
priori distinction between "constructors" and "defined" function
symbols, and argument patterns are not restricted to
"constructor terms". This allows you to have symbolic evaluation
rules like the following distributivity rule: X*(Y+Z) =
X*Y+X*Z. Such rules are not allowed in ML and Haskell because
they violate the "constructor discipline". Moreover, the Q
interpreter also happily reduces expressions involving
variables, so that you can try your definitions with symbolic
inputs (e.g., map F [X,Y] will evaluate to [F X,F Y]), which is
quite useful for debugging purposes.

* While other functional languages are either "eager" or "lazy", Q
tries to give you the best of both worlds: It uses eager
(call-by-value) evaluation by default (which lends itself to an
efficient implementation), but also allows you to define your
own special forms, which can be used to implement both
call-by-name functions and lazy data constructors. Using special
forms you can also define your own "meta functions" which
manipulate arbitrary (not necessarily irreducible) expressions
as literal terms. This gives you full control over the reduction
strategy, where this is desired, as well as a kind of "macros"
and "self-modifying programs", since constructs like lambda
abstractions can be analyzed, transformed and constructed in
many ways before they are actually evaluated.

* Q uses dynamic typing. This has become a rare feature in
contemporary functional languages, which usually employ a
Hindley/Milner type system which offers more safety at the
expense of restricting polymorphism. Q gives you back the
flexibility of good old Lisp-style ad-hoc polymorphism and even
allows you to extend the definition of existing operations
(including built-in functions and operators) to your own data
types. Moreover, Q has a Smalltalk-like object-oriented type
system which supports data encapsulation and (single)
inheritance. This makes it possible to manage complex,
polymorphic data with ease.

* Q takes the pragmatic route in that it provides (monad-free)
imperative programming features such as imperative I/O and
mutable data cells, similar to the corresponding facilities in
ML. While one may argue about these, they certainly make life
easier when programming complex I/O stuff such as graphical user
interfaces.

* Last but not least, the Q programming system comes with
batteries included. There are quite a few add-on modules
interfacing to third-party libraries which, while not as
comprehensive as the facilities provided by traditional
scripting languages like Perl and Python, allow you to tackle
most practical programming problems with ease. In particular,
multimedia and computer music is an area where Q shines,
providing facilities to handle graphics, digital audio and MIDI
in a convenient and efficient manner (efficient enough for soft
realtime applications), which go well beyond what is currently
being offered for other functional languages.

Some of Q's flexibility comes at a price. In particular, Q's symbolic
evaluation capabilities as a term rewriting language dictate that the
language is essentially exception-free. This means that an
"ill-formed" expression (such as "division by zero" or, more
generally, the application of a function to data for which there is no
corresponding definition) does not, by default, raise an exception,
evaluates to itself. As a remedy, Q provides facilities to add
explicit error rules which raise exceptions, and to handle exceptions
in a suitable manner. Another limitation is that Q does not allow you
to have arbitrary local definitions in the style of Haskell and ML's
"let" and "where" clauses. In Q, "where" clauses are limited to
variable definitions (similar to Haskell's pattern bindings), but
local rewriting rules are not supported as they would wreak havoc on
the term rewriting semantics.

You will also notice that Q lacks most of the syntactic sugar found in
Haskell. This is partly due to my laziness, but it also keeps the
language and the core system simple. Using Q's symbolic processing
capabilities, the usual bits like lambda abstractions, pattern
matching conditionals and list comprehensions can easily be written in
Q itself (although this does not offer quite the same performance as
when they are provided as builtins, of course).

John

I'm curious why do you think that it "smells like Python"? Because of
"batteries included"? Are dotNET and Java Python-like because they come
up with a fast object library?

Personally I think that time has passed for functional languages except
for academic studies. Features like term rewriting are interesting per
se but I believe that they can be implemented on top of dynamic OO
languages like Python with some effort too. My expectation of future
Computer Algebra Systems ( CAS ) is that they provide models of
mathematical objects like algebras, sets, spaces, manifolds,
number-fields etc. that can be refined using inheritance and can be
adapted dynamically. Despite the majority of people working in CA I
think that OO is much more the way contemporary mathematicians think
about their stuff than the FP toolbox approach. What I'm missing is a
kind of domain specific syntax, e.g. a <ket|bra> syntax for working
with states in quantum mechanics. But that's out of the scope of
current Python.

Regards,
Kay

J

#### John J. Lee

Kay Schluehr said:
I'm curious why do you think that it "smells like Python"? Because of
"batteries included"?

Partly that and the mention of dynamic typing, plus harder-to-pin down
things.

I didn't mean to say that it was close kin to Python, just that on
first impression it has some similarities.

Are dotNET and Java Python-like because they come
up with a fast object library?
[...]

What's a fast object library?

John

R

K

#### Kay Schluehr

John said:
Kay Schluehr said:
I'm curious why do you think that it "smells like Python"? Because of
"batteries included"?

Partly that and the mention of dynamic typing, plus harder-to-pin down
things.

I didn't mean to say that it was close kin to Python, just that on
first impression it has some similarities.
O.K.
Are dotNET and Java Python-like because they come
up with a fast object library?
[...]

What's a fast object library?

Sorry. 'Fast' was nonsense.

Kay