Constants in EK9

The constant is described in this section, these are declarations of the built in types. They are designed to be very constrained and limited in their use. As with most of EK9; the language has a preconceived idea of how and where each construct can and should be used.

As a developer, if you want to make use of the classes or records as single global variables; then you must use components and use dependency injection. You cannot use your own defined types as EK9 constants - only the built-in types can be constants.

While initially you may find this inconvenient, you will find over time this approach reduces software coupling. This is because it reduces the direct linkage to a specific class/record/component.

The idea of constants in EK9 is really limited to very fixed concepts/values or defaults.
The most important characteristic of the 'constant' is that is is immutable, i.e. it has a constant value that cannot be changed.

This means that a constant (or indeed a 'literal' value) can be passed into a function or a method with a specific value; if that method or function is not marked as pure it may alter the value passed in. This will in no way alter the 'literal' or 'constant' that was provided as a parameter.
There will be no 'exception' or 'immutability' error issued at either compile time or runtime. In effect 'constant' and 'literal' values are copied as they are passed into a function or method - turning them into variables. This preserves the immutability of the 'constant'/'literal' and the pass by reference nature of arguments for methods and functions.

The same logic applies if a 'constant' or a 'literal' value is returned from a method or a function. Its value is copied before the return. This again preserves the immutability of the 'constant'/'literal'.

Below are some examples of constants.

Constant Declarations

#!ek9
defines module introduction

  defines constant

    limitNumberOfRetries <- true

    updateErrorLog <- false

    delimiter <- ':'

    author <- "Frank Jones"

    maxNumberOfRetries <- 10

    minTemperatureAllowed <- -2

    Pi <- 3.142

    bitMask <- 0b01110001

    noon <- 12:00

    justAfterNoon <- 12:00:01

    //ISO 8601 i.e P[n]Y[n]M[n]DT[n]H[n]M[n]S
    maxRenewalDuration <- P3Y2M6DT12H15M6S

    twoYears <- P2Y

    twoMonths <- P2M

    minusTwoDays <- P-2D

    twoHours <- PT2H

    minusTwoHours <- PT-2H

    twoMinutes <- PT2M

    timeoutPeriod <- 250ms

    newMillennium <- 2000-01-01

    ek9CreatedDateTime <- 2018-01-31T01:30:00-05:00

    maxPayment <- 300000#USD

    defaultColour <- #AB6F2B

    twoMeters <- 2m

    matchSteves <- /[S|s]te(?:ven?|phen)/

    //A comment
    //And another
//EOF

Please look in the section on built in types for details on each of the above types, but as you can see constants can only be literals (this is by design). Constants are designed to be very simple and to be used to hold fixed final constant values.

Use of Constants

If you create another ek9 source file using the same module as the constants; you can just use the constants directly as shown below.

Same Module

#!ek9
defines module introduction
  defines function
  
    areaOfCircle()
      -> diameter as Float
      <- result as Float: PI * (diameter/2)^2
        
    circumferenceOfCircle()
      -> diameter as Float
      <- result as Float: PI * diameter
//EOF

As you can see PI can just be used directly (as you would expect).

Different Module

While not really related directly to constants the examples below give an explanation of how different modules and constructs within those modules can be accessed. But is most likely you will define a range of constants in a module and then use those throughout other modules. There are very significant advantages in doing this to reduce the proliferation and duplication of fixed values. If however you want to use PI from another module you have a couple of options; as shown below.

Option 1
#!ek9
defines module introduction.part
  defines function

    printPI()
      Stdout().println($introduction::PI)  
//EOF

The above shows how to directly reference the constant PI from the 'introduction' module; this is done from within the function 'printPI' in the 'introduction.part' module.

Option 2

If you needed to reference PI several times in your module; having to explicitly reference PI by its module would become tedious. Therefore EK9 has the concept of declaring a reference so that the constant or type can be used directly.

#!ek9
defines module introduction.part
  references
    introduction::PI
    
  defines function

    printPI()
      Stdout().println($PI)  
//EOF


TL;DR

Summary

Constants are very simple in EK9, but in general should not be overlooked. By defining a range of constants in your application you get compiler support because if you 'typo' a constant reference the EK9 compiler will spot it. If however you just use fixed values that should be the same throughout your code but 'typo' those then the compiler cannot spot that.

As always there is a balance to be found here. Some developers might consider the introduction of a constant for a fixed value that is only used once in a simple program 'over engineering'. Others might consider that values such as a 'delimiter' for file processing (as shown in the example in standard types) are a key part of the solution and as such should be 'elevated' and made more prominent.

After all, declaring a constant is not just about reuse, they are also fixed and immutable. But probably more important is the fact they are given a 'name'; hopefully a meaningful name. Designing software is also about making the solution obvious to humans, giving constants meaningful names aids this.

Balance

If you've worked on and used a range of different languages before, or worked in different industries; you may have noticed some tendencies in different developers.

In general they start off simple, but build more and more complex webs of abstractions. This sometimes gets to the point where no one can really 'see the wood for the trees'. Everything becomes a façade or an interface, higher order functions use other higher order functions to create yet more higher order functions (now where was I ?).

In short, too many levels of abstraction create cognitive load. Be sure the abstraction is worth creating.

While this is not really related directly to constants; constants are one of the starting points for the creation of abstractions. So when a developer looks at some processing - they have to jump over to that constant from where it has been used, you've just caused the developer to take another action. Be sure that is necessary, finding this balance in software development is one of the hardest things to get 'right'.

Conclusion

By using constants in a careful and thoughtful manner you will reduce the number of very simple errors and aid other developers (and yourself) in promoting those significant fixed values to a point in the code where they stand out.

Next Steps

The next section on flow control shows the basic control structures that can be used in EK9.