torsdag, august 17, 2023

So, Nim (the programming language)

So what's up with Nim?

Nim is the only modern programming language that has understood the lessons coming out of the last 70 years of trials in the computational arena. There, I said it...

Nim grasps the essence of the concepts which has, at one time - or another been elevated to paradigm status and reduces them to useful tools. The OO and Functional paradigms are both dissected and anything superfluous removed with surgical precision.

Nim OO:

Since Nim modules provide encapsulation on a granular level it can dispense with the class oriented tyranny of managed languages like C# and Java. As well: with Uniform Function Call Syntax (UFCS) member functions are no longer relevant.

So what's left?

Well, an Object of course. Nims Objects are by default value types (structs) with unordered constructor semantics, encapsulation and optional inheritance (extend Root Obj). This is quite different from a Java Object, which can always only be a reference Object. This was such a problem for Java that recently a Record type with value semantics was introduced to that language. This of course as the realization dawned that modern CPU caches become completely inefficient when they're forced to pointer chase  - and that enforced reference semantics in OO relies on the v-table lookup - another abhorrently inefficient mechanism. 

Nims default dispatch is static (compiletime). To get multiple dynamic dispatch (runtime) one must use Object references (garbage collected pointer semantics) and explicitly stated methods. That's right: Nim identifies the actual purpose of a method (to provide runtime polymorphism via inheritance) and then demands that you use the keyword "method" to explicitly state your intent.

Functional Nim:

In Nim functions are first class citizens: a Nim function can take other functions as parameters and return functions as Closures all using arrow syntax (if you like). If you've ever written an anonymous class in Java to satisfy some interface simply to pass a callback - you've seen the pits of ritual hell and understand on a visceral level the deep burning pain inflicted by nested verbosity. Again, this was such a problem that Java had to come up with the "functional interface", which very simple definition is: an interface allowing only a single method. Surely, I have no business pointing out what a Freudian slip the concept of a "functional interface" constitutes.

Anyways - with the dissing Java business to one side (the OO bad side) -: the interest in functional was spawned by the nesecity for parallel due to the multicore solution to the death of Moores law. The hype cycle then predictably proceeded to elevate Functional to deity status - all the while now trashing the once upon a time equally deified OO "paradigm". But aside from the convenience of first class functions, closures and generic Filter, Map, Reduce what was the appeal of functional?

Well, the only truly relevant Functional proposition is the concept of purity. Where as the Class is the flawed instrument of OO encapsulation - that doesn't work: the lowly function and its derivative Closure is the Functional ditto that actually - functions. 

A function is considered pure when two conditions are met: 

1) given a specific input it must always return the same output, so that: it cannot rely on any state outside its scope that is not certifiably invariant. 

2) under no condition - what so ever - must the inner workings of the function - ever - result in any state change of any kind outside the scope of the function - ever! 

When these conditions are compiletime enforced you are guarantied to have no data-races no matter how many of these things you spawn in parallel. When you write a Nim function these guaranties are compiletime enforced.

So where are we at?

var c = 1

const d = 1

proc impureAdder(a:int):int = a+c

func pureAdder(a:int):int = a+d

method multipleDynamicDispatchAdder(a:Nr,b:AnotherNr):int = a.val + b.val


Likewise:

#invariant identifier - expression resolved at compiltime.

const a = [1,2,3,4,5,5].filterIt(it mod 2 == 0) 

#invariant identifier - expression resolved at runtime

let b = newHttpClient.getContent url 

#runtime variable initialization

var c = 1 

Do you know any language that make these distinctions? Probably not. Because they're all busy praying at the alter of some purism or other - driving themselves insane wandering the age old labyrinthic halls of pure mind and impure matter. Pragmatism is the rarest of commodities when dealing with human beings...

Per