Constructs - do we need more?
Functions and Classes just aren't enough
14 min read
In my previous post I covered immutability and touched on the first construct; the constant.
EK9 also has functions, dynamic functions, classes, dynamic classes and interfaces/traits. These are all unsurprising as they are presented in most languages. But in EK9 functions and classes are equal constructs much like they are in C++.
But are constants, functions and classes enough?
APIs - Frameworks - Pre-processors
Let's take a quick look at what developers actually use more widely beyond just the programming language of choice.
It's pretty obvious what an API or library is. It is some form of prebuilt functionality that you as the developer decide to call. The developer is in the driving seat -- so to speak.
The framework is also fairly easy to understand. In this case the developer creates some piece of functionality and registers it via some existing software through an API, library or configuration file. But here is the twist, the framework does the actual calling of the developers code. So the framework is in the driving seat of making the call (yes the developer did configure when to make the call - but the framework does the calling).
So what about pre-processors like those for sass, svelte, etc. Indeed what about post-processors for slimming down the final sets of artefacts, packaging etc.
Now these processors take code to output in one format and transform it, in some cases they are a quite simple but in others they are more like a compiler.
But in general they all push the final use/checks/assembly to runtime.
But now the front-end has almost moved into being compiled. Initially through the use of pre-processors, but now more frameworks are adding in specifics that require pre-processing. More importantly the resulting artefacts require (post-processing and packaging).
Web Applications are becoming more like thick clients of old, it is just that the browser is the deployment mechanism. Newer Web Applications are tending to thin out the back-end and put more and more logic in the front-end.
Will this trend end? There was a reason the N-tier architecture evolved. Thicker client solutions come with a range of benefits but also a range of drawbacks.
With more of a component model; CSS becomes an issue, because it was designed to work with a page layout model not really a component model. This is designed right into CSS it has a very strong mechanism to be able to address elements via the structure of a page. It also has the .class mechanism to short cut all that.
This then causes alternate CSS approaches to be adopted (like tailwind for example). When you think about it; it is the whole page the user sees. So there are some aspects of styling that could be at a component level; but there is a significant amount that should be at the whole page/site level.
Why have I discussed these different approaches? The main reason is to highlight evolution and the constant struggle to find a viable solution to specific problems.
The most interesting thing here for me is the adoption of an Object Oriented Approach to UI delivery to the browser. But several of these new frameworks have lost the separation of concerns when it comes to mixing object structure, markup and CSS presentation.
So while gaining some componentisation functionality, the separation between the HTML markup and code and the further separation of look via CSS has been sacrificed to some extent.
It is interesting to note that Apache Wicket (which dates back to 2005) has a sort of OO approach with a separation between HTML and Java but is server side rendering (with Ajax hooks).
Evolution is not restricted to the front-end; the back-end has also undergone some change. If we focus on Java and Spring for a moment; it becomes quite clear that a sort of 'meta-language' of Component, Service, Config, etc has; in effect, created another language. This is in stark contradiction to what Spring set out to do. Originally its aim was to be unobtrusive and wire together Objects through configuration files.
But I digress, the main point of Spring and any IOC/DI framework is to add capability that is missing from the programming language itself. You can make the argument that's what Annotations are for, but you are building a framework and additional syntax/semantics that are not really compile time safe. It is only at runtime you actually find out if your wiring was right or not. Those Annotations are also very specific to the API/Framework.
You see this same sort of Annotation driven approach in frameworks like Spring WebServices.
What appears to have happened since the 1960's that different sorts of patterns/constructs have just naturally evolved out of different programming languages when they have been applied to specific problems. During the late 1980's the new construct of the class emerged. Further innovations occur in the 1990's with markup (specifically HTML). This is followed quite quickly by the pattern of separation of concerns with respect to markup and how/where that marked up content is presented (CSS). We also see a move to virtual machines with Java (though the USDC p-system and others pre-dates this). Innovation continues with markup in the form of JSON and the Semantic Web.
In general we can see languages like Smalltalk, Haskell, Erlang, Lisp for example have some following in certain domains; but remain somewhat niche. But concepts and ideas from those languages leak out and get incorporated into design patterns or other languages over time.
You may think that the above history and summaries of front-end and back-end are just ramblings. But there is a common theme in all of them; the choice of programming language has not allowed a developer to solve a problem without significant additional APIs, Frameworks and Processors.
In some cases the constructs are constraints of simple types but in others they are Aggregates used in conjunction with a design pattern or role.
Additionally; certain technologies such as Browsers, Java and C# have provided mechanisms that enable developers to mitigate omissions of constructs in programming languages.
I'll give some examples to try and make this a little clearer.
An enumerated type is normally based on some simple primitive type (an Integer for example). But this construct was not always baked in. The original K&R C language did not have enumerations. They sort of evolved out over time, most languages (but not all) now support Enumerations.
But why are they needed? What purpose to they perform? Won't just a finite set of ints just do the job just as well?
So really an enumeration boils down to some sort of constraint on a primitive type when it comes to delivering Enumerations.
ADA has the idea of subtypes/derived types. This again is a form of strong typing.
Why would these be useful in a program? They could be used to represent minimum and maximum values for a person age for example. Defining a constraint on Integer to be greater than 16 and less than 91 for some business reason.
The main benefit of this is type safety, but also it is possible to have an operator of ++ on a variable and accidentally transition over a boundary. When this boundary is transited an exception could be thrown or the value could just become undefined.
EK9 has constrained types; specifically look at EmployeeId and Name in the example code. These provide strong typing. The Tag Cloud shown on the link above also highlights the value of strong typing. It helps to quickly convey meaning.
Classes sort of evolved out of structs with function pointers attached to them, then became a solution for reuse through inheritance.
These programming concepts evolved out of abstract classes and enabled developers to create different polymorphic views of the same object but in a type safe way.
In general quite a few programs are written using data transfer objects, in C/C++/C# these would be called structs. Other languages call them records. EK9 uses the term record. In EK9 these have constructors, public fields and optional operators. Their main purpose is to be a data transfer object.
The Java programming language has now also evolved to include records. So this is another concrete example of how languages move and evolve different constructs over time.
But this is also quite significant and many Object Oriented theorists would decry these records as anaemic classes. Again this is an example of how patterns/trends and accepted ways of working change over time (I'll skip the Singleton debacle).
Most languages provide APIs for internationalisation and managing of localisation of messages for the end user. But EK9 takes this important area one step further by providing a text construct explicitly designed to deal with internationalisation and localisation. It does also provide API calls in the form of a Locale.
But the text construct has been designed to:
- Deal with small or large amounts of text
- Be component driven
- Be parametrisable with strong typing
- Provide compiler support for missing messages
- Be flexible in the number and size of files that contain application text
You may consider this a little excessive, but if you have ever developed a large component based application for multiple spoken languages you'll immediately see the value and need for this construct.
IOC and DI
Inversion of Control and Dependency Injection are two mechanism used widely in development. But in general they are provided through some form of meta language or external configuration mechanism. Spring is an example of this. EK9 is no where near as comprehensive as Spring it has been designed not to be. It bakes IOC and DI right into the language; through the use of Applications/Components and Dependency Injection.
Both IOC and DI can be very useful, but can also lead to massive complexity. EK9 looks to cut through the excesses and provide a minimal but valuable construct to support this design technique.
But what should we apply IOC/DI and Aspect to? In EK9 you can only apply them to components.
Because that is the most appropriate level to apply them. If you can apply them to Classes then IOC/DI is abused and the code base becomes too interlinked through excessive injection. In short it's just too easy.
By ensuring only components can be injected, it is possible to ensure a more appropriate layering of code.
Through the use of IOC and DI EK9 also includes a minimal aspect oriented mechanism for cross cutting concerns. Many theorists now avoid aspect oriented programming stating that it is brittle, buggy or limits refactoring.
EK9 has deliberately limited the excesses of AOP, in EK9 there is no matching methods by name or anything like that. It's an all or nothing thing on a component.
While not strictly a construct in the traditional sense; EK9 pulls away from the object oriented approach of streaming and pipelining through fluent interfaces or method chaining. It firmly moves in the direction of functional programming and provides Pipelines.
If you are familiar with Unix pipes and pipeline processing then this is for you; but in a programming language.
You may ask; why create more syntax and constructs for something that can be delivered via Classes/Objects? The main reasons are simple syntax, semantics and consistency. The concept of streaming objects/data/functions through a processing pipeline is significant and compelling. The only reason languages such as Java/C# employ objects and a fluent interface is because that's all they have available.
EK9 Pipelines provide and very clear, concise and elegant natural construct for developers.
As mentioned previously Spring WebServices employs Annotations for implementation of web services. Many other languages have frameworks that support web services. But EK9 Web services bake this essential construct right into the language. This service construct dovetails with the text construct.
Like TCP; the HTTP protocol is here to stay. It is now very unlikely it will be replaced by anything else. It may evolve and change but unlike some technologies (RMI and CORBA for example), HTTP is here to stay in the mainstream.
As such EK9 builds it right into the language as a powerful construct.
The last construct EK9 introduces is packaging. You may think that final packaging, versioning and releasing of software should be external to the language. After all most languages don't include packaging but depend on tools like npm, ant, ivy, maven, cradle, make, imake, cmake, pip etc, etc).
Here lies the problem if we're honest. Why so many package managers and why does each language tend to add at least one new one?
In general it's because each language has features/limits/constraints that require a different approach. Unfortunately all these package managers then invent their own specific package syntax and file format!
So let's just accept that as a new language EK9 will probably need some type of package manager mechanism and rather than use an external format - just build it into the language itself.
Tools like make start off simple, but building software is now so much more than simple steps from taking source -> object -> binaries. Dependency management, packaging, publishing and version management are now critical aspects in software development.
The EK9 command line tool has a very wide range of options and lots of functionality to be able to support:
- Dependency Resolution
- Package Management
EK9 has gone from a couple of simple constructs and included a wide range of additional ideas. You may consider this is going too far; take a look at the example, you might see the ideas fit quite well together.
These conclusions I'm drawing here may well be wrong. But I have to wonder why we have some many different frameworks, APIs and build tools if our programming languages are complete.
When I look at most new programming languages such as Golang, Rust, Dart, Kotlin, Swift, etc; I see improvements here and there; but no real radical shift or innovation. I don't intend this to be a criticism, they each address the target issues they are attempting to resolve.
I'm attempting to resolve the target issue I think exist (maybe only I think these are real issues to be addressed).
Importantly, when getting started with EK9 you only really need to use a handful of these different constructs, but you can employ more of them as you progress.
There are quite a few examples for you to look at to get a feel of the language. These range from very simple to complete applications.
A different approach
In the next post I'll cover the syntax of EK9 and why it was adopted.
Superficially EK9 syntax looks very similar to Python rather than C, C++, C#, Java, Dart, Kotlin, Go, Rust or Swift. This is not due to my Python background (my background is C, C++ and Java). But my aversion to punctuation and slight dyslexia make it much easier for me to read a sort of Python syntax.
Why not just use Python then? There are some flaws in its approach and its lack of strong typing means I find it useful for small utilities, but not for full large scale application development.
So I have attempted to merge aspects of many languages into one I like. You may consider this egocentric. I can understand you might think that.
When creating something you like yourself; it maybe that only you like it; that's fine by me. But I'm hoping a few other people will see value and utility in this new language.
So next post is all about syntax. It will probably look quite strange to you at first. But I've looked at quite a few theoretical papers on how humans comprehend text via layout. I have attempted to fold some of those ideas into the layout and structure.