Sophia Lang Weekly - 00

· 913 words · 5 minute read

Tip

This series will be a continuous somewhat weekly newsletter about my progress, changes and anything around the sophia programming language. I will try to publish one post of the series containing the changes and ideas for every week I have time to work on the project, so expect around 2-3 a month, mostly on Sundays. These posts will be short, concise. Basic knowledge of interpreter and programming language design is assumed.

This weeks changes include improvements to the performance of certain benchmarks, clearer errors for illegal index operations, reworked index syntax for arrays and objects (with shortcomings) and a big planned feature.

Performance improvements ##

This and last week we saw a lot of drastic performance improvements and there is already a post going into detail on several optimizations on my blog:

Excerpts from Improving programming language performance:

How I improved my programming language runtime […] for a specific benchmark by reducing its execution time by 7.03 times or 703%. The benchmark script previously took 22.2 seconds. I reduced it to 3.3s!

[…]

Our slightly less easy to implement changes decreased the runtime by 3.795s from 5.803s to 2.009s in comparison to the not optimised state - that is really good, really really good, we are talking a 65.38% runtime decrease.

Deep array and index access ##

Previously the parser did not support nested array or object index notation such as [object.field.field] or [array.index.field] both required a wrapper variable:

 1(let person {
 2    name: "anon"
 3    bank: {
 4        money: 2500
 5        institute: {
 6            name: "western union"
 7        }
 8    }
 9    age: 25
10})
11
12;; (put [person.bank.money]) <-- parser error
13
14(let bankOfPerson [person.bank])
15(put [bankOfPerson.money]) ;; 2500
16
17(let bankOfPersonInstititue [person.institute])
18(put [bankOfPerson.name]) ;; western union

Having reworked the parser for nested array and index notation the following is now supported:

1(put [person.bank.money]) ;; 2500
2(put [person.bank.institute.name]) ;; "western union"

Reworked array and object index notation ##

I really disliked the way I implemented indexing into arrays or objects, therefore I reworked the notation. Previously accessing array or object contents was done inside of []:

 1(let person {
 2    name: "anon"
 3    bank: {
 4        money: 2500
 5        institute: {
 6            name: "western union"
 7        }
 8    }
 9    age: 25
10})
11
12(put [person.bank.money]) ;; 2500
13(put [person.bank.institute.name]) ;; "western union"
14
15(let arr person 1 2 3 4 5)
16(put [arr.0.name]) ;; "anon"

The improved way is obviously inspired by C, Go, JavaScript, etc:

 1(let person {
 2    name: "anon"
 3    bank: {
 4        money: 2500
 5        institute: {
 6            name: "western union"
 7        }
 8    }
 9    age: 25
10})
11
12(put person.bank.money) ;; 2500
13(put person.bank.institute.name) ;; "western union"
14
15(let arr person 1 2 3 4 5)
16(put arr.0.name) ;; "anon"

This removes the need for writing even more braces in a brace dominated environment :^).

More precise illegal index error feedback ##

While reworking the index notation I also improved the way errors for indexing are displayed. Previously the expr.Index AST node would simply complain about not containing logic for indexing into the given type.

old-index-feedback

Now it includes more precise feedback:

new-index-feedback

New implementation example ##

This week i added the computation of the binomial coefficient to the examples:

 1(fun bin (_ n k)
 2    (if (gt k n)
 3        (return -1))
 4    (if (and (eq k n) (eq k 0))
 5        (return 1))
 6
 7    ;; Due to the symmetry of the binomial coefficient with regard to k and n −
 8	;; k, calculation may be optimised by setting the upper limit of the
 9	;; product above to the smaller of k and n − k, see
10	;; https://en.wikipedia.org/wiki/Binomial_coefficient#Computing_the_value_of_binomial_coefficients
11    (let kn (- n k))
12    (if (lt kn k)
13        (let k kn))
14
15    ;; see https://en.wikipedia.org/wiki/Binomial_coefficient#Multiplicative_formula
16    (let r 1)
17    (for (_ i) k
18        (let r
19            (/ (* r (- n i)) (+ i 1)))
20        )
21    r)
22
23(put (bin 1 1)) ;; 1
24(put (bin 6 3)) ;; 20
25(put (bin 49 6)) ;; 20
26(put (bin 20 15)) ;; 15504

It makes use of a symmetry of k and n-k, thus reducing the amount of iterations required to compute the coefficient. Implementation inspired by statlib.BinomialCoefficient.

Planned feature: modules ##

My plan for the sophia language is to not include any object orientation due to the fact that I am a vivid Java hater (unless its the right tool for the job - which it rarely is). I also do not want to allow myself to attach functions onto objects. My idea is to create modules in a rust like fashion (multiple in one file are allowed) and to define static functions in these. The resulting syntax will look something like:

 1;; custom module
 2(module person
 3    (fun str (_ p) (++ "person: " p.name)
 4
 5    (module extract ;; modules can be nested
 6        (fun name (_ p) p.name)
 7    )
 8)
 9
10;; using a custom module
11(use person)
12(let pers { name: "anon" })
13(put (person::string pers))
14
15;; using nested modules
16(use person::extract)
17(put (person::extract::name pers))

I also want to mirror go’s extensive standard library into the sophia language via modules and static functions:

1(use strings)
2;; results in ["192", "168", "0", "217"]
3(put (strings::split "192.168.0.217" "."))
4
5(use fmt)
6(fmt::printf "Hello %q" "traveler")
7(put (fmt::sprintf "Hello %q" "Space"))

I also long for a better type system in the sophia language, so maybe thats an idea.