Feeds:
Posts
Comments

SCIMP

Having understood the main features of Scheme well enough to be able to make some music with Impromptu, I quickly realised that for me, coming from SuperCollider, I found it a bit strange working with those Audio Unit synths. There are some cool synths out there, but the time spent on tweaking them to achieve the right sound is longer than I was comfortable with. I just wanted to design my own synthesis. This is obviously a strong habit that I’ve built up by using SuperCollider for many years and could be seen as quite eccentric. Why would one need to know every parameter and ingredient of the synth if it sounds good? But for me, I want to know what is happening in there. I want to know how many oscillators are being used, passed through which filters and effects.

So I decided to use SuperCollider for audio synthesis. Three options present themselves here: a) Exporting SuperCollider synths as Audio Units. This is currently not working very well, as AUs made in SC only work as aumf and not aumu (for those that understand Core Audio). b) use Rohan Drape’s rsc3 Scheme client for SuperCollider. This was not so appealing as I want to work in Impromptu and many of the functions in Rohan’s Scheme don’t work there. Furthermore, it does not appeal to me to write synthdefs in Scheme, since I dream them in SC lang. c) control the SC server from Impromptu from OSC, practically making a new SC client in Impromptu. This was the most appealing option as I want to use the image and video capacity of Impromptu, and moreover, with the new Impromptu 2.5, it should be much faster than rsc3.

Impromptu 2.5!!! What a feast! With a new compiler which allows for a JIT compilation of Scheme code into LLVM machine code. This means that code can be written in Scheme that functions on par with C code in terms of speed, allowing for real-time synthesis of audio. This is my next step of Impromptu investigation.

Anyway, the SCIMP Impromptu SuperCollider client can be found here. The code went through various manifestations representing different programming paradigms. I had a rather complex client structure built up of object oriented inspired design where synths, nodes, groups, buffers and busses could be instantiated as objects and had methods reflecting those of SC Server clients such as SC lang, ScalaCollider, JCollider, scosc in Python, etc. But it gradually became clear, reflecting upon the code I’ve seen in Scheme that this is not the way Scheme flows. So the result is code that should reflect pretty much how Impromptu functions such as play-note work. The code is experimental and should be considered alpha.

Direct link to the library can be found here.

Impromptu Style OOP

Having posted on the Impromptu list with questions about OOP and related matters, Andrew Sorensen replied with an answer that showed how one can make an object orientated system in Scheme. Well, this was so elegant that I feel I have to document that here. What I really like about the system is that one can add methods on the fly:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; A Simple Object System for Thor :)
;;
;; supports inheritance, method overloading and mixins
;; 


;; the base of the object system everything derives from here
;; therefore everything inherits isa, get-method, add-method
;; get-methods and dispatch
(define make-object
   (lambda ()
      (let* ((klassname '<object>)
             (super #f)
             (isa (lambda (t)
                     (if (equal? t klassname) 
                         #t
                         (if super 
                             (super 'isa t)
                             #f))))
             (dispatch (lambda (msg methods) 
                          (if (assoc msg methods)
                              (cdr (assoc msg methods))
                              (begin (print-error 'No 'such 'method) (error "")))))
             (get-method (lambda (msg) (cdr (assoc msg methods))))
             (add-method (lambda (name closure) ;; for mixings
                            (set! methods (cons (cons name closure) methods)) #t))
             (get-methods (lambda () methods))
             (printer (lambda () (print "object")))
             (methods (list (cons 'isa isa)
                            (cons 'get-method get-method) (cons 'add-method add-method)
                            (cons 'dispatch dispatch) (cons 'get-methods get-methods)))
             (self (lambda (msg . args)
                      (apply sys:dynamic-call 
                             ((cdr (assoc 'dispatch methods)) msg methods) 
                             args))))
         self)))


;; animal inherits from object (so inherits isa dispatch get-method get-methods and add-method)
;; but also adds it's own speak method
;; animal also includes a "name" slot (with getters and setters) and a static "voice" slot
(define make-animal
   (lambda (name)
      (let* ((klassname '<animal>)
             (super (make-object))
             (voice "is silent") ;; static variable
             (get-name (lambda () name))
             (set-name (lambda (_name) (set! name _name)))
             (speak (lambda () (print name 'the klassname voice)))
             (methods (append (list (cons 'speak speak)
                                    (cons 'get-name get-name) (cons 'set-name set-name))
                              (super 'get-methods)))
             (self (lambda (msg . args)
                      (apply sys:dynamic-call 
                             ((cdr (assoc 'dispatch methods)) msg methods)
                             args))))
         self)))

;; 
;; Dog inherits from animal 
;; so inherits speak and name from animal as well as everything from object)
;; it adds age and it's own voice
;; 
(define make-dog
   (lambda (name age)
      (let* ((klassname '<dog>)
             (super (make-animal name))
             (voice "barks")
             (get-age (lambda () age))
             (set-age (lambda (_age) (set! age _age)))
             (methods (append (list (cons 'set-age set-age)
                                    (cons 'get-age get-age))
                              (super 'get-methods)))
             (self (lambda (msg . args)
                      (apply sys:dynamic-call 
                             ((cdr (assoc 'dispatch methods)) msg methods)
                             args))))
         self)))

;;
;; Duck overrides animals speak method
;; although it does still call animals speak through super.
;;
(define make-duck
   (lambda (name age)
      (let* ((klassname '<duck>)
             (super (make-animal name))
             (voice "quacks")
             (get-age (lambda () age))
             (set-age (lambda (_age) (set! age _age)))
             ;; override animals speak with call to super
             (speak (lambda ()
                       ;; call supers speak with dynamnic 
                       (sys:dynamic-call (super 'get-method 'speak)) 
                       ;; and add our own line for ducks!
                       (print name 'is age 'years 'old)))
             (methods (append (list (cons 'set-age set-age)
                                    (cons 'get-age get-age)
                                    (cons 'speak speak))
                              (super 'get-methods)))
             (self (lambda (msg . args)
                      (apply sys:dynamic-call 
                             ((cdr (assoc 'dispatch methods)) msg methods)
                             args))))
         self)))

;; some simple macro wrappers to make everything nicer :)
(define-macro (speak obj)
   `(,obj 'speak))

(define-macro (isa obj type)
   `(,obj 'isa ',type))

(define-macro (set-age obj age)
   `(,obj 'set-age ,age))

(define-macro (add-method obj name closure)
   `(,obj 'add-method ',name ,closure))

;; make an animal
(define jack (make-animal "jack"))
;; make jack speak
(speak jack)

;; make a dog
(define spot (make-dog "spot" 5))
;; make spot speak
(speak spot)
;; let's try a method that doesn't exist
(spot 'test)
(spot 'speak)

;; make a duck
(define daffy (make-duck "daffy" 6))
;; oops daffy is actually 7
(set-age daffy 7)
;; make daffy speak
(speak daffy)

;; make another dog
(define k9 (make-dog "k9" 109928392283747))
;; speak k9
(speak k9) ;; oops k9 shouldn't bark!

;; add a mixin espeacially for k9 to ovrride speak
(add-method k9 speak (lambda () 
(print name 'is 'old age 'says "Yes Master" 
'alot 'and 'lives 'in 'a 'tardis)))
;; make k9 speak
(speak k9) 

;; who are you!
(print "spot is a dog" (isa spot <dog>))
(print "spot is an animal" (isa spot <animal>))
(print "spot is an object" (isa spot <object>))
(print "daffy is a duck" (isa daffy <duck>))
(print "daffy is an animal" (isa daffy <animal>))
(print "daffy is a dog" (isa daffy <dog>)) ;; #f

Thinking in OOP

When new to Scheme there are various Object Orientated Programming practices that are hard to get rid of. For example classes, objects, encapsulation, overloading, polymorphism, methods (that can call each other within the same class), keyword arguments with default values, etc. None of this exists in Scheme directly. However, it constantly surprises me how everything can be built with great ease in Scheme. For example keyword arguments can be implemented like this:

;; a Scheme function supporting keyword arguments with default values

(define (play-synth . f)
    (let ((freq (cond
                 ((assq 'freq f) => cdr)
                 (else 440) ; default value for freq
                 ))
          (phase (cond
               ((assq 'phase f) => cdr)
               (else 0)  ; default value for phase
               ))
          (amp (cond
               ((assq 'amp f) => cdr)
               (else 0.5)  ; default value for amp
               )))       
      (print "freq is: " freq " &  phase is : " phase " &  amp is : " amp)))

(play-synth '(freq . 333))
(play-synth '(freq . 333) '(phase . 0.1))
(play-synth '(freq . 333) '(phase . 0.1) '(amp . 0.999))

Classes can also be made of which objects can be created:

;; a Scheme function supporting keyword arguments with default values


;; a Scheme function that works as a class, instantiating an object

 (define (synth)
   (let ((node-id 0) (synthdef "oo")) 
     (define new 
        (lambda (synthd . args)
         (set! synthdef synthd)
         (apply print "new synth created with these args : " args)
         node-id)) 
     (define run 
       (lambda () 
           (print "run the synth" )
           ))
     (define free 
       (lambda () 
           (print "free the synth" )
           ))
     (define set 
       (lambda (message . args) 
           (apply print "set synth args : " message args)
           ))
     (define get-node-id 
       (lambda () 
          node-id))
     (lambda (message . args) 
       (apply (case message
                  ((new) new) 
                  ((run) run) 
                  ((grain) grain) 
                  ((free) free) 
                  ((set) set) 
                  ((get-node-id) get-node-id) 
                  ((set-node-id) set-node-id) 
                  (else (print "method not found!"))) 
              args))))

;; We play the synth like this:

(define a (synth))
(a 'new "impromptutest" "freq" 1111)
(a 'set "freq" (random 211 1888) "amp" (random))
(a 'free)


And finally the question of polymorphism:

;; a Scheme function supporting keyword arguments with default values


;; I came up with this type of polymorphism

(define uu
   (lambda (one two . (three . args))
       (if (string? one)
           (apply print one two three args)
           (apply print two one three args ))))

(uu "one" 11 "two" 22 "three" 33 "four" 44)
(uu 11 "one" "two" 22 "three" 33 "four" 44 "five")


;; but I found the following here:
;; http://sourceware.org/ml/guile/1999-08/msg00015.html


(define (make-polymorph)
  (define (make-polyentry predlis func)
    (vector (length predlis) predlis func))
  (define (poly->argn poly) (vector-ref poly 0))
  (define (poly->pred poly) (vector-ref poly 1))
  (define (poly->func poly) (vector-ref poly 2))

  (let ((polylis '()))
    (lambda args
      (define (add predlis func)
	(set! polylis (cons (make-polyentry predlis func)
			    polylis)))
      (define (sigmatch? predlis arglis)
	(cond
	 ((or (null? predlis) (null? arglis))
	  #t)
	 (((car predlis) (car arglis))
	  (sigmatch? (cdr predlis) (cdr arglis)))
	 (else
	  #f)))
      (define (find-func argn arglis)
	(let loop ((lis polylis))
	  (cond
	   ((null? lis)
	    (error "No signatures match"))
	   ((and (= (poly->argn (car lis)) argn)
		 (sigmatch? (poly->pred (car lis)) arglis))
	    (poly->func (car lis)))
	   (else
	    (loop (cdr lis))))))
      (if (null? args)
	  add
	  (apply (find-func (length args) args) args)))))

(define-syntax define-polymorphx
  (syntax-rules ()
    ((_ name)
     (define name (make-polymorph)))
    ((_ name ((pred1 arg1) ...) expr ...)
     ((name) (list pred1 ...) (lambda (arg1 ...) expr ...)))))

(define-polymorph synth)
(define-polymorph synth ((string? arg)) (print " string arg is : " arg))
(define-polymorph synth ((integer? arg)) (print " int arg is : " arg))
(define-polymorph synth ((rational? arg)) (print " rational arg is : " arg))
(define-polymorph synth ((list? arg)) (print " list arg is : " arg))

(synth "freq")
(synth 222)
(synth 3/2)
(synth '(123 23))
(synth 3.23)

So everything possible in Scheme. Andrew Sorensen even posted a super effective object system on the list which I will post in the next blog since it deserves a special attention. The most interesting thing for me, going through all these structures of thinking and trying to implement them in Scheme, is the realisation that they are actually not needed.

I have now finished a SuperCollider Synth client for Impromptu (I will post here about that as well). This allows for a total control over SC synth rather than AUs. During that work, I realised that I should follow the flow of Scheme and not strive to implement the above structures into my client. And things became extremely easy! Although most SC clients tend to build up a strong Synth, Node, Bus, Buffer and Group structures, those are actually not needed in the way Scheme flows. I have learned to think programming increasingly in the form of dynamics rather than form, of movement rather than objects, of verbs rather than nouns. I’m not sure if this makes much sense, but I would be interested in any literature that would point at how different programming languages enable different structures of thinking. Tips anyone?

I once started a blog post about the strangeness in thinking recursively, but never posted it. I was wondering if thinking in recursions would require practice, a special mind set or if it simply was ineffective compared to iterations.

I did find the two filter-out-of-a-list functions in Impromptu and SuperCollider very different ways of sorting items out of a list. For some reason I found the SC function a very natural way of thinking, but the Scheme one not. I wonder if it would have been the other way around if I had learned Scheme first?

Scheme function:

(define ixi:filter 
   (lambda (lst item)
   (if (null? lst)
       '()
       (if (= item (car lst))
               (ixi:filter (cdr lst) item)
           (cons (car lst) (ixi:filter (cdr lst) item))))))

(ixi:filter '(22 33 44 33 22 11 222 33 44 33 11) 33)

SuperCollider function:

~filter = {arg list, item;
	var newarr = [];
	list.do({arg listitem; 
		if(listitem != item, {newarr = newarr.add(listitem)}) 
	});
	newarr;
}

~filter.value( [22, 33, 44, 33, 22, 11, 222, 33, 44, 33, 11], 33)

I’ve been working in Impromptu lately, and I can’t see the problem anymore…. What happened? Where did it click and when? This reminds me of the zen koan:

Before a man studies Zen, a mountain is a mountain
after he gets insights, a mountain is not a mountain
When he really understands, a mountain is a mountain.

Not that I’ve reached any deep understanding of Scheme, but at least I must have grasped recursions.

Benchmarking is a good way to see if one’s code is effective. The various programming solutions can be compared by taking the time it takes for the interpreter to compute the function. Benchmarking normally works in the way that the system time is taken before performing the function, then the function is applied, and finally the time is taken again, subsequently subtracted by the first time.

Here is the benchmark function of SuperCollider:

	bench { arg print = true;
		var dt;
		var t0 = Main.elapsedTime;
		this.value;
		dt = Main.elapsedTime - t0;
		if (print) { Post << "time to run: " << dt << " seconds.\n"; }
		^dt
	}

I thought it would be good to have such a function available when developing in Impromptu. By putting the function in a .scm file in the Application Support/Impromptu folder, it would be read on startup and be accessible at all times. The idea to create this benchmark function came when pondering what is the most effective way of populating lists (see last post). So before we start, here are the functions that I will benchmark:

; descending list
(define populateD
  (lambda (n)
     (if (= 0 n)
         '()
         (cons n (populateD (- n 1))))))

(populateD 52)

; ascending list
(define populateA
    (lambda (n end)
        (if (= n end)
            '()
            (cons n (populateA (+ n 1) end) ))))

(populateA 0 22)

; ascending list using iteration (dotimes)
(define populateI
   (lambda (n)
      (define counter 0)
      (define ls '())
      (dotimes (i n)
          (set! counter (+ counter 1))
          (set! ls (cons (- n i) ls)))
      ls))

(populateI 22)

; ascending list using an "inner recursion" through let
(define populateR
    (lambda (n)
       (let loop ((cnt 0))
          (if (> cnt n)
              '()
              (cons cnt (loop (+ cnt 1) ))))))

(populateR 33)

; and finally ascending list using recursion using apply
(define populateRA
   (lambda (n . args)
      (if (< n 0)
          args
          (apply populateRA (- n 1) (cons n args)))))

(populateRA 22)

I started my experiment in creating benchmarking code by replicating the SuperCollider function:

 (begin
     (define a (now))
     (populateA 0 4222)
     (define b (now))
         (print "benchmark time : " (- b a)))

This works, but the problem is that (now) gives times in samples since Impromptu started, and it does so in sample blocks, resulting in benchmark times of 0, 256, 512, 768, etc. (i.e., with block size of 256 samples). Using (clock) solves this issue (although I didn’t know of clock until it was pointed out on the mailing list. The Impromptu Function List document does not have any reference to clock). Next step was to use clock and put it into a function:

(define bench2
  (lambda()
   (begin
     (define t1 (clock))
     (populateA 0 22)
     (define t2 (clock))
         (print "benchmark time : " (- t2 t1)))))

(bench2)

All good. But obviously a the function to be benchmarked should not be hardcoded into the bench function: it should be passed to it:

(define bench
   (lambda (func)
      (begin
             (define t1 (clock))
             func
             (define t2 (clock))
             (print "benchmark time : " (- t2 t1)))))
   
(bench (populateA 0 22))

Here I stumbled into an interesting difference between SuperCollider and Impromptu, elegantly explained by Andrew Sorensen in his reply to my mail: “What you’re striking is the difference between call-by-value and call-by-name (often called lazy evaluation – as per Haskell). Scheme is call-by-value, which in brief means that it evaluates any argument expressions before applying their values to the given function. When you try to apply the function bench i.e. (bench (reverse (populateR 220))) the expresion (reverse (populateR 220)) first gets evaluated and it’s result (i.e. a list) is then sent as an argument to bench.”

Lazy evaluation is probably not the right description of what is happening in the SC bench function. There the function itself is passed and evaluated in the right place. (between the two time samples).

Anyway, Thorin suggested the following bench function:

(define profile 
   (lambda (iternum func . args)
  (let ((*time* (clock)))
     (dotimes (i iternum)
        (apply func args))
  (set! *time* (- (clock) *time*))
  (print "benchmark time: " *time*))))

(profile 100 populateA 0 22)

and here the function is evaluated x amount of times and finally printing the time it takes for it to perform. This works fine in principle, but as Thorin acknowledges, there is a certain overhead in using (dotimes) and (apply) x number of times. This can be seen if we compare the two benchmarks of bench2 and profile:

(bench2)   ; "benchmark time : " 0.000076
(profile 1 populateA 0 22)   ; "benchmark time: " 0.000867

This is therefore not suitable if I want to compare the performance of Impromptu versus other languages. For that I need the most simple benchmark function possible, ideally mirroring the SuperCollider function. Andrew suggested the following:

(define bench
   (lambda (func . args)
      (let ((t (clock))
            (res (apply func args)))
         (print "benchmark time :" (- (clock) t))
         res)))

which works well, except it is still using (apply) and it won’t work with nested function calls, such as (reverse (populateA 22)). This note from Andrew explains what happens well: “the populateR expression (an atom is still an expression) is still evaluated, but it is evaluating the symbol which is bound to a function – whose evaluation returns the bound function (i.e. does not apply the function). If we put (bench (populateR 10)) we are instead evaluating the expression (populateR 10) and then passing the result of evaluating that expression to bench. (of course 10 also gets evaluated before being applied to popluateR but numbers evaluate to themselves – as do strings and characters).”

Andrew then suggests making a macro (something I will study next) for this task:

(define-macro (benchm expr)
   `(let ((t (clock))
          (res ,expr))
       (print "benchmark time:" (- (clock) t))
       res))

(benchm (reverse (populateR 220)))
(benchm (populateI 220))

(map (lambda (i) (print 'myprint: i) i) (benchm (populateR 10)))

Voila! We’ve got our benchmark function, so let’s do some testing of the populateList functions above:

(benchm (populateD 220)) ; "benchmark time:" 0.000321
(benchm (populateA 0 220)) ; "benchmark time:" 0.000350
(benchm (populateI 220)) ; "benchmark time:" 0.001155
(benchm (populateR 220)) ; "benchmark time:" 0.000332
(benchm (populateRA 220)) ; "benchmark time:" 0.000377

This is obviously not very scientific but gives some indication, although the resulting times vary quite a lot. It is clear that the iterative function that uses (dotimes) is twice slower than the others. In general the (populateR) function is the fastest one, probably because it is not using (apply) as the (populateRA) function.

Finally, Impromptu has some inbuilt functions for list operations so I tried to benchmark one that does what populateR does:

(benchm (make-list-with-proc 22 
          (let ((n 0)) 
                 (lambda () (set! n (+ n 1)) n)))) ; "benchmark time:" 0.000556

Interestingly, this is a bit slower than the methods above. I checked if it is a macro, and it’s not. Finally I found it in the Impromptu.scm file in the Impromptu.app Resources.

Here is how that function looks:

(define (make-list-with-proc lth func)
   (if (< lth 1)
       '()
       (let loop ((i 0)
                  (lst '()))
          (if (>= i lth)
              (reverse lst)
              (loop (+ i 1) (cons (func i) lst))))))

It is doing the loop, and it is evaluating the function that is passed to it as well. I think this explains why it is slightly slower.

Anyway, performing a bit of testing of SC, Python and Impromptu looks like this:

bench{
	a = [];
	22.do({arg i; a = a.add(i) });
}

time to run: 6.3839999995707e-05 seconds.

0.000063839 seconds

Python code might look like this:

import time

def bench() :
	b = time.clock()
	a = []
	for i in range(22):
		a.append(i)
	c = time.clock()
	print "benchmark time : " + str(c-b)

bench()

which returns:
benchmark time : 6.2e-05

i.e. 0.000062

So Impromptu seems to be around 10 times slower than Python and SC for these types of operations. Obviously this comparison can be flawed as I am no benchmarking specialist, but this is what meets the eye at first glance.

Furthermore, with an array of 22000 it takes Python 0.0062 seconds and SuperCollider 0.00972 seconds. In Impromptu, the benchmark time using make-list-with-proc takes 0.1139 seconds, and using the populateR method 0.1197 seconds.

Trying to populate an array of, say, 130000 items is problematic in Impromptu. It crashes the language. I’m not sure why, but before it crashes it posts lots of these messages:

:ERROR: position:(0) in function “(null)”
illegal function
Trace:

These problems that I’m stumbling into are hardly problems for music making in Impromptu. If one schedules musical events ahead of time, functions should be able to finish before the event, and one is not likely to make an array of 130000 items, except if wanting to work on sample operations. I am curious about these “shortcomings” of Impromptu though.

On Recursion

Coming from the OOP paradigm (Smalltalk, SuperCollider, Python and Java), the first thing one notices when studying Scheme is the use of recursions. Recursions can be used in those languages as well, but Scheme explicitly encourages it and is built around it. Or, as Jean-Pierre Hebert states “iteration in Scheme is expressed more clearly and succinctly via recursion. Recursion is more general and eliminates the need for the variable assignments required by many other languages’ iteration constructs, resulting in code that is more reliable and easier to follow.”

Recursion is what it says on the tin, i.e. a function that recursively calls itself. Here is a recursive SuperCollider function:

f = {arg n;
    if(n==0, {
        1
    }, {
        (n * f.value(n-1))
    });
}

f.value(5)

and a Python function (thanks Enrike):

def factorial(n) :
   print "entering factorial level (n) %i" % n
   if n == 1 :
       print "deepest level of recursion reached! n is now : %s" % n
       return 1
   else :
       r = factorial(n - 1)
       x = n * r
       print " n is: %s and r is:  %s" % (n, r)
       return x

level = 5
print "top level function returns %i when n is %i" % (factorial(level), level)

Recursion can be a hard nut to crack, but in reality it is quite simple, as the Python printing shows.

The Scheme function for the above would be

(define factorial 
  (lambda (n)
     (if (= n 0)
         1
         (* n (factorial (- n 1))))))

(factorial 5)

Nice and easy in principle. But what if one wants to populate a list with items from 0 to n?

Here I stumbled into problems. It was easy to populate it in descending numbers:

(define populateD
  (lambda (n)
     (if (= 0 n)
         '()
         (cons n (populateD (- n 1))))))

(populateD 5)

But immediately it’s not clear how to do that in ascending order. One could pass an extra initial argument for the counter and then finish the recursion when counter reaches the end:

(define populateA
    (lambda (n end)
        (if (= n end) 
            '()
            (cons n (populateA (+ n 1) end) ))))

(populateA 0 22)

or simply using the inbuilt Scheme function:

(reverse populateR 5))

I don’t find that very elegant. Could this not be done inside the function? And why the need for an extra argument? So some digging around and some help from the very helpful Impromptu mailing list made this an interesting excursion into recursion.

Firstly, for this type of task my intuition is to iterate. And Scheme allows for iteration (as opposed to a purely functional language as Haskell, for example). Here is the iteration function that I came up with:

(define populateI
   (lambda (n)
      (define counter 0)
      (define ls '())
      (dotimes (i n)
          (set! counter (+ counter 1))
          (set! ls (cons (- n i) ls)))
      ls))

(populateI 22)

This looked good to me and I was happy to have found the solution to my conceptual problems, but then I read this: “Purely functional languages do not support iteration AT ALL. This is because iteration requires mutable state.  In your populateI example set! *changes* the values bound to both “counter” and “ls”.  In other words set! mutates “counter” and “ls” introducing mutable state into your program.  Mutable state is not allowed in purely functional languages – such as Haskell.  Scheme sits pragmatically in the middle (Haskellers would say it sits imperfectly in the middle :-).

Mutation in Scheme is supported through calls such as set!, set-car!, vector-set! etc..  You’ll notice that these all have a “!” trailing them – this is to say WARNING – these calls mutate state.  In general Scheme programmers try to be purely functional (stateless) as often as is practicable (unlike Haskell programmers who insist on being functional even when it isn’t practicable” (Sorensen’s mail to Impromtu list)

So a warning: mutable states ahead! (an interesting discussion I came across related to this can be found here). It is not a recommended practice, but I wonder what it means in terms of speed and optimisation (see my next post).

Now, I suspect that the populateI function above is not very elegant. The two defines and the mutable state set! functions are not the idea of Scheme. Related topic is how to set a variable in a function that does is not reset on every iteration. It turns out that this is possible (note the use of “let” in the function):

(define cccc
    (lambda (n)
       (let loop ((cnt 0))
          (print cnt)
          (if (> cnt n) 
              cnt
              (loop (+ cnt 1) )))))

(cccc 33)

Here the cnt is set to 0 in the beginning, but in every recursion it is increasing.

This allows for a recursive populate function that can add numbers to the list in ascending order:

(define populateR
    (lambda (n)
       (let loop ((cnt 0))
          (print cnt)
          (if (> cnt n) 
              '()
              (cons cnt (loop (+ cnt 1) ))))))

(populateR 33)

Finally, one can populate this list without an inner loop by using the apply function and passing the growing list into the function repeatedly (through lambda (n . args)). So here we go, no mutable state, no state at all actually, just a recursion that returns the list in ascending order.

(define populateR
   (lambda (n . args)
      (if (< n 0) 
          args
          (apply populateR (- n 1) (cons n args)))))


(populateR 22)

From these “tricks” above, I am now able to write any list manipulation functions that I want. Some of these are obviously already in Scheme, but some not.

; finding the index of an item in the array
(define ixi:indexof
   (lambda (item ls)
      (if (= item (car ls))
      0
      (+ 1 (ixi:indexof item (cdr ls))))))

(ixi:indexof 54 '(1 2 3 4 54 5 6 6 5 4 43 3 3 )) ; returns 4


; make a list with random numbers (using IF)
(define ixi:randomlist
   (lambda (size low high)
      (if
         (zero? size) 
         '()
         (cons (random low high) (ixi:randomlist (- size 1) low high)))))

(ixi:randomlist 15 5 500)


(define ixi:listsum
   (lambda (ls)
      (if (null? ls)
          0
          (+ (car ls) (ixi:listsum (cdr ls))))))

(ixi:listsum '(11 22 33 44))


(define ixi:listmeans
   (lambda (ls)
      (rational->real (/ (ixi:listsum ls) (length ls)))))

(ixi:listmeans '(11 22 33 44))



; remove an item from a list
(define ixi:remove
  (lambda (item ls)
    (cond
      ((null? ls) '())
      ((equal? (car ls) item) (ixi:remove item (cdr ls)))
      (else (cons (car ls) (ixi:remove item (cdr ls)))))))

(ixi:remove "thor" '("loki" "mirra" "birta" "thor"))
(ixi:remove 333 '(111 333 222 333 444 555 666 777))


(define ixi:multiplylistbyn
   (lambda (n ls)
      (if (null? ls)
          '()
          (cons (* (car ls) n) (ixi:multiplylistbyn n (cdr ls))))))

(ixi:multiplylistbyn 11 '(1 2 3 4))


; some list functions
(list-position 55 '(33 34 55 66 77)) ; 2
(list-tail '(22 33 44 55) 3) ; (55)
(list-ref '(22 333 44 555 666) 2) ; index into list


; the problem with looping is now sorted with dotimes
(define test
    (lambda (str times wait)
       (dotimes (i times)
          (print str i)
          (sys:wait (+ (now) (* *second* wait))))))

(test "hello there" 10 0.5)
(test "testing one two" 10 0.1)

; these run in sequence, first the first, then the second, etc. : )
(begin (test "first" 10 0.1)
       (test "second" 10 0.1)
       (test "third" 10 0.1))

I feel that I am now able to do much of what I would need for algorithmic music composition using Impromptu. I feel that I’m at a stage where I can really start to compose music, although my code will look silly and unoptimised for some time to come. Next topics of study are the uses of map, apply and then soon the use of macros.

Back from NIME

So, little activity here for the last month. Academic duties and paper writing took over all my free time, in addition to a conference trip to Australia. (NIME). Even if activities on these pages have not been many, there was much Impromptu going on down under (Impromptu is the indigenous Australian Music Programming Language). We had some good coding sessions on the Sunshine Coast north of Brisbane (where, impressively, Nick Collins and Andrew Sorensen made a SuperCollider Unit Generator that can be live coded from Impromptu : ) and then an Impromptu workshop at NIME in Sydney.

I also performed in a live coding gig which was a Club Night at NIME. There Andrew Brown, Andrew Sorensen, myself, Anthony Ptak and Mark Havrilyv and Mei-Ling Dubrau all coded away in quite a successful evening. It is clear how live coding is maturing as a performance form, as the tools become more advanced and skills improve. The gig in the Excelicior Club was good, with some impressive performances and the audience was very receptive to the music.

I’ve now come to understand the incredible strength of Impromptu as an environment for multimedia work. Video (live or files), images, sound files, synthesis, Audio Unit control, MIDI and OSC I/O, plus hooking into Objective C for all serial or other types of communication. As I understand the environment now, it could be schematised in the following graph:

Moreover, I’m getting a rough conception of Scheme as a language with very little syntax or inbuilt functions. In reality Scheme is not a language but an abstract syntax tree in which you write your own language. This excites me. Also, the fact that there is no real difference between data and the language seems quite interesting in terms of realtime alterations of code through live coding. The language can be modified the same way as one modifies data on the fly.

An example (I’m exploring this now) is array manipulations. I’m used to languages where I can get at items in arrays, set items at specified index, etc. In Scheme this seems more laborious. Everything is based on pairs (a . b) and then one has to make arrays or lists by combining those. To create an array a= [2,4,24] where one could access a[1] is not that straight forward in basic Scheme. This has to be built on top of Scheme. The vocabulary for accessing the pairs is quite archaic as well:

; cons = construct a pair from two objects
; car = contents of address register
; cdr = contents of decrement register

(car returns the first item of the pair, whilst cdr returns the second item, or the remainder of the list).

You then build your array/list manipulations from these basic functions. At the moment this seems rather limited and I wonder why more advanced array manipulations are not part of the language. So next thing for me is now to see what kind of libraries exist out there (list manipulation lib?), and/or build these methods myself. Obviously Impromptu comes with few of these, such as replace-all

(define oxo ‘(a b c c (d c b a)))
(replace-all oxo ‘((c . x) (d . y) (a . 333)))

but I’m not seeing straight getters and setters right now. Perhaps I’ll have to dig more.

Anyway, I had a go at writing my own access function.


(define at
   (lambda (array index cnt)
      (print cnt)
      (cond
             ((= cnt index) (car array))
              (else (at (cdr array) index (+ cnt 1))))))

(at '(11 22 33 44 55) 3 0)

The problem here is that I have to pass the initial counter (0). I can’t see the solution immediately. I tried overloading the function, but I’m not sure that is possible in Scheme. At least various versions of this won’t work:

(lambda (array index (if (null? cnt) 0))

(I shouldn’t really be blogging (bragging? ha ha ha) about something that I haven’t found a solution for, but then again, it is nice to document where I’m at at this moment in time. In any case I’m sure there are some “at” list methods in Scheme anyway).

Follow

Get every new post delivered to your Inbox.