Erlang modules and functions

Modules

Modules are (what you might expect) a way to group together functions.

We declare a module with the first line

-module(Name).

This is always the first line of a module and the Name is an atom (an atom being a literal/constant).

Next up in our module, we’ll be exporting functions along with their arity (arity being an integer representing the number of arguments that can be passed to the function). So for example, if we have add with two arguments we would export it like this

-export([add/2]).

We can export multiple functions within the -export for example

-export([add/2, delete/2, multiply/2, divide/2]).

So let’s put those pieces together and we get a math module something like this

-module(math).
-export([add/2, subtract/2, multiply/2, divide/2]).

add(A, B) -> 
    A + B.

subtract(A, B) -> 
    A - B.

multiply(A, B) -> 
    A * B.

divide(A, B) -> 
    A / B.

Functions

So we’ve seen some example of functions, let’s look at the syntax.

Functions take the form Name(Args) -> Body. where each expression in the body ends with a comma, so for example

start() -> 
    S = "Hello World",
    io:fwrite(S).

Publishing Dart packages to pub.dev

Dart packages can be published to pub.dev and it’s a pretty simple process.

  • 1. Sign up to create an account on pub.dev
  • 2. Don’t worry about the Publisher, you can create and upload packages without this

Next up, let’s assume you don’t yet have a package yet – check out Creating packages to learn about the expected project layout of folders/files. We can simply run the following Dart command to create the template for us

dart create -t package-simple mypackage

Obviously change the mypackage name to that of your package.

Before we look into the requirements for publishing, let’s just state that pub.dev (when you publish your package) runs some analysis across your package and assigns you PUB POINTS – for example to follow Dart file conventions you’re expected to supply a valid pubspec.yaml, a valid README.md and a valid CHANGELOG.md.

  • Update the pubspec.yaml description field – ensure it has 60-180 characters
  • The package created by the Dart CLI gives a good starting point for the README.md and CHANGELOG.md – don’t forget to update the CHANGELOG.md to match each version
  • Implement your code in the lib/src folder – files should use lowercase, snakecase, or you’ll lose PUB POINTS
  • Ensure you’re lib .dart file has
    library mypackage;
    

    Replacing mypackage with your package name if you change the package name at any point during development.

  • Add your tests to the test folder
  • Add an example to the example folder
  • Ensure your code has doc comments to your lib .dart file as well as to the lib/src files, i.e.
    /// My Doc Comments
    

Assuming you’ve got everything in place, run

dart format .

to adhere to Dart formatting preferences. You’ll lose PUB POINTS if you publish with format issues.

Ensure your tests all run successfully using

dart test

Run the publish process in dry-run mode using

dart pub publish --dry-run

When ready, run

dart pub publish

You can exclude files from publishing by prefixing the filenames with a ., any directory with the name packages is ignored. Finally, files and directories within .pubignore or .gitignore are also excluded.

If I recall when publishing, you may be asked to log into pub.dev – just follow the publish command’s prompts and if all went well you’ll have the package upload to pub.dev.

Remember, once you’ve published a package it cannot be deleted, but you can mark them as discontinued. So make sure you have your package named as your want it to be (at the very least) before you publish it.

Ensure versions are correct and updated each time you publish, along with CHANGELOG.md updates to show what happened for each new version.

Finally pub.dev will run it’s analyse process and then supply a PUB POINT score out of 130 and will give information on what’s missing or needs attention to get a 130/130 score – for example, my current package needs a couple of files formatting correctly and an example as I didn’t use the package template but wrote mine from scratch.

Lastly, try to get your README.md etc. right before publishing as you’ll have to increment the version of your app just to change the README.md (yes I learned the hard way).

NLog embedding runtime variables

Sometimes you’ll want to set your NLog configuration up to output the log files to an environment, i.e. %APPDATA%/MyApp/Logging/UAT – but you’ll only know the selected environment at runtime.

This is where we can embed ${var:NAME}, where NAME is a key to your variable. In our case this will be ENV for environment.

So for example, let’s assume we have this fileName in your NLog configuration like this

fileName="${specialfolder:folder=ApplicationData}/MyApp/Logging/${var:ENV}/logfile_{$shortdate}.log

As you can see from the above, we’ve included the built-in specialfolder and shortdate variables. For the ${var:ENV} we need to supply using “env” variable in our code, for example

LogManager.Configurations.Variables["env"] = GetCurrentEnvironment()

Use Powershell to change file date/time stamps

Ever wanted to change the creation date/time on a file on Windows – Powershell offers the following (change Filename.ext to your filename)

$(Get-Item Filename.ext).creationtime
$(Get-Item Filename.ext).lastwritetime
$(Get-Item Filename.ext).lastaccesstime

Want to change these? You can supply the date in a string like this

$(Get-Item Filename.ext).creationtime = "15 March 2022 19:00:00"
$(Get-Item Filename.ext).lastwritetime = "15 March 2022 19:00:00"
$(Get-Item Filename.ext).lastaccesstime = "15 March 2022 19:00:00"

or if you like you’ve got

$(Get-Item Filename.ext).creationtime = $(Get-Date)
$(Get-Item Filename.ext).creationtime = $(Get-Date "15/03/2022 19:00:00")

Note: I’m on a en-GB machine hence using dd/MM/yyyy format – ofcourse changes this to your culture format.

Stopping my Ubuntu server from hibernating

I’m not sure what’s happened, but after a recent upgrade to my Ubuntu server – when I tried to connect to the server (which I use as both a dev server and NAS), I couldn’t log into via ssh but when I attached a monitor and keyboard to the server I was able to use it and the ssh started working. However after a period of inactivity the server seemingly went offline, pressing the power switch briefly bought it back online. So some how it had gone sleep/hibernate mode – not very useful thing for a server.

Here’s some steps I went through to eventually get this working again…

Check the logs

Checking the logs is always a useful first step, so let’s take a look at the syslog

nano /var/log/syslog

or use

tail -f /var/log/syslog

I spotted log entries saying the machine was going to sleep.

Checking and then disabling sleep mode

Now use systemctl to confirm the status of the sleep.target, i.e. is it enabled

sudo systemctl status sleep.target

To disable sleep, we probably want to disable all options that hibernate/sleep so we can disable the lot using

sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target

If for some reason you wish to re-enable these, just run

sudo systemctl unmask sleep.target suspend.target hibernate.target hybrid-sleep.target

I’m still none the wiser as to how hibernate got enabled – but at least I’ve learned how to check and disable it.

Dart web server

I know, when you think Dart you possibly think Flutter, i.e. mobile UI. However Dart is not limited to mobile. Let’s create a simple webserver using shelf 1.2.0.

First off, create a folder for our code and in there create a file with the name pubspec.yaml. This is used to store our dependencies. In this file we have the following

name: server

environment:
  sdk: ">=2.12.0 <3.0.0"

dependencies:
  shelf: ^1.2.0
  shelf_router: ^1.0.0
  shelf_static: ^1.0.0

The ‘name’ is the name of your app – I am not being very inspiring calling mine server.

Now we’ll just copy and paste the example from Shelf. So create a file named server.dart (the name doesn’t matter, again, I was uninspiring in my choice).

So server.dart should contain the following (I’ve changed from localhost to 0.0.0.0 to access the site from another computer as this code is hosted on a headless server).

import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as shelf_io;

void main() async {
  var handler =
      const Pipeline().addMiddleware(logRequests()).addHandler(_echoRequest);

  var server = await shelf_io.serve(handler, '0.0.0.0', 8080);

  // Enable content compression
  server.autoCompress = true;

  print('Serving at http://${server.address.host}:${server.port}');
}

Response _echoRequest(Request request) =>
    Response.ok('Request for "${request.url}"');

Next we’ll want to update/get the packages, so run

dart pub get

Finally let’s run the server using

dart server.dart

If all went well a server is run up on port 8080 and accessible via your preferred web browser. This simple example can be accessed using the following (for example)

http://192.168.1.1:8080/hello

hello is just used to get a response from the server, which will look like this Request for “hello”.

Routing

The above gives us a baseline to work from. We’re going to want to route requests to alternate functions, currently requests simple go to the _echoRequest function.

We’ve actually already added the shelf_router to our pubspec, so just import the package and add the Router. We’re going to removing the handler and the _echoRequest function, so our main function looks like this now

import 'package:shelf_router/shelf_router.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as shelf_io;

void main() async {

  var app = Router();

  app.get('/hello', (Request request) {
    return Response.ok('hello-world');
  });

  app.get('/user/<user>', (Request request, String user) {
    return Response.ok('hello $user');
  });

  var server = await shelf_io.serve(app, '0.0.0.0', 8080);

  server.autoCompress = true;

  print('Serving at http://${server.address.host}:${server.port}');
}

Note: This code is basically a copy of the example on shelf_router.

Installing Dart on Ubuntu 20.x

Details to install Dart on Ubuntu are listed on get-dart. I’m recreating here solely for my own reference.

sudo apt-get update
sudo apt-get install apt-transport-https
wget -qO- https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo gpg --dearmor -o /usr/share/keyrings/dart.gpg
echo 'deb [signed-by=/usr/share/keyrings/dart.gpg arch=amd64] https://storage.googleapis.com/download.dartlang.org/linux/debian stable main' | sudo tee /etc/apt/sources.list.d/dart_stable.list

Next install the Dart DSK

sudo apt-get update
sudo apt-get install dart

Export the path

export PATH="$PATH:/usr/lib/dart/bin"

Then add the PATH to your ~/.profile or ~/.bashrc, for example

echo 'export PATH="$PATH:/usr/lib/dart/bin"' >> ~/.profile

Now let’s see if it all worked, run

dart --version

If all went well you’ll see something like Dart SDK version: 2.16.1 (stable) (Unknown timestamp) on “linux_x64”.

Optionals, optionals everywhere

Swift loves it’s optionals (or nullables if you come from a C# background).

Whilst optionals may lead to fewer unhandled null reference exceptions or the likes they also lead to different ways of unwrapping values.

Declaring variables

First off let’s look at declaring a string type, we might write

let s: String = "Hello"

This is not an optional declaration and hence requires an initial value, i.e. remove the = “Hello” and the compiler complains or try to set to nil and the compiler complains – because this variables is expected to be non-nil. If we want to declare a type as possibly being nil then we need to mark it as such using the optional syntax.

Declaring optionals

let s: String? = "Hello"

The ? on a variable/value declares a type of optional string. We can also declare options in a long hand form

let s: String = Optional.some("Hello")

Equally, the following are equivalent

let s1: String?
let s2: String? = Optional.none

Basically we’re saying a “some” value or “none” value or nil may be assigned to this variable.

One thing to note regarding optionals is that to get the actual value assigned, you have to “unwrap” it from the optional. Basically think in terms of your variable is wrapped in an Optional type and to access it, you need to get (unwrap) the value out of that type.

Implicitly unwrapped optional

Interestingly Swift also has implicitly unwrapped optionals. Whereas an optional may have a value, no value or nil, an implicitly unwrapped optional may only contain a value or nil. Here’s the syntax for implicit optionals (note the trailing !)

let s: String!

The general use of an implicitly unwrapped optional is where maybe you declare a value initially but know it will be set to a value before use and will not be nil again.

Obviously we can still check if a value is nil or not, but the compiler will assume you know what you’re doing so if you’re using a value that’s nil the compiler will not warn you.

Note: If you come from a language like C# or Java, then in the example above, just think that this is more like the way you’d declare Strings in those languages and ofcourse you have the same issues regarding ensuring the value is non-nil/null in those languages.

Why not just using implicitly unwrapped optionals as they seem simple? The main reason is the same as why C# introduced nullable reference types. If you want to do things yourself, no problem, but if you want the compiler to highlight potential misused of variables, i.e. have the compiler warn if it looks like you might be using a nil. Then you’re best to only use implicitly unwrapped optionals sparingly and stick to the optional equivalent.

If you’re a little confused by implicitly unwrapped and optionals, let’s look at this code

var s1: String?
var s2: String!

print(s1.length)
print(s2.length)

Neither variable has been set to a value, hence both are none or nil. If we compile this code the Swift compiler will complain with

error: value of optional type ‘String?’ must be unwrapped to refer to member ‘length’ of wrapped base type ‘String’
print(s1.length)

Because the compiler has our back.

Now commenting out print(s1.length) and recompiling, all looks good to the compiler, but ofcourse when we run this code it’ll blow up with an error such as

Unexpectedly found nil while implicitly unwrapping an Optional value

Basically the compiler see’s the implicitly unwrapped optional essentially ignore it as we (the developer) told it that we know what we’re doing.

Unwrapping an optional as part of a conditional test

We’ll often wish to check if an optional is nil or not and then unwrap it to get the actual underlying value. Using the following syntax does both steps in one go

if let optionalValue = optionalValue {
   // do something with the unwrapped optionalValue
}

Swift also supports the guard statement which is basically another condition operator so we can also unwrap in the same way as above, but with a guard, i.e.

guard let optionalValue = optionalValue else {
   // failed to unwrap
   return
}

// do something with the unwrapped optionalValue

Switch

We can use a switch statement on optionals as well, for example

let s: String? = "Hello"
switch s {
   case .some(let s): print("SOME \(s)")
   case .none: print ("NONE")
}

Using map with optionals

We can use map with optionals

let x: String? = "Hello"
let y = x.map { $0 + "World" }
// y is a String?

Unwrapping with !

In some situations you may know an optional value is not nil in which case you simply wish to unwrap the value without any conditional code in place, that’s when you used the ! operator to force unwrap – basically the developer is saying this is a valid value so just unwrap it. Ofcourse, if the developer is wrong then this will have unintended consequences.

The syntax is as follows

let optionalValue: String? = "Hello"

print(optionalValue!)

Optional chaining

We can use the optional chaining ?. operator on types, for example

let optionalValue: String? = "Hello"
print(s?.count)

The nil coalescing operator

Along with the optional chaining operator ?. we also have the nil coalescing operator

let optionalValue: String? = "Hello"
print(optionalValue ?? "NONE")

init?

I’d been wondering why, when creating some types in Swift I was seeing a requirement to unwrap the type. After all, surely if I created a types it’s not nil and hence why does it need to be optional?

Well Swift does actually allow us to declare a type’s init as optional, for example

struct Email {
   var address: String

   init?(address: String) {
      // not really much of a validation step
      // but in the real world we could ensure
      // the address matches an email regex
      guard address.count > 0 else {
         return nil
     }
   }
}

This is pretty useful as it means if we initialize a type with invalid values, we can simple return a nil optional object instead of the alternative of throwing an exception or the need for a two phase initialization.

URLSession.shared.dataTask and HTTP errors such as 404

The URLSession.shared.dataTask completionHandler passes us three pieces of information. Data, the response from an HTTP request and an error.

Transport errors are returned within the error object but HTTP status codes which we’d possible class as errors are supplied in the response object, so I wanted both transport and HTTP status errors to be returned as an Error.

Note: I’m not using URLSession.shared.data as it’s not currently supported on Linux, from what I can tell.

The code below creates a new dataTask method that is async and returns a Result<Data?, Error>. Let’s first look at new dataTask method without the status code handling

private func dataTask(with request: URLRequest) async -> Result<Data?, Error> {
   await withCheckedContinuation { continuation in
      URLSession.shared.dataTask(with: request) { data, response, error in
         if let error = error {
            continuation.resume(returning: .failure(error))
            return
         }
         continuation.resume(returning: .success(data))
    }.resume()
}

So this creates a continuation which is used as a promise/future/task by calling the continuation resume method with either a success or failure.

As already mentioned, this will not return 404’s (for example) as errors. So let’s simply add code to create a failure when certain HTTP status codes appear. We might want to differentiate between the two types of error, i.e. transport layer and HTTP, so first off let’s create our Error subclass, which looks like this

public enum HTTPError: Error {
    case transportError(Error)
    case httpError(Int)
}

This will allow us to pass back the Error from the URLSession.shared.dataTask completion handler for transport errors and the code from the HTTP status for HTTP errors.

Now we’ll change the earlier code to look like this

private func dataTask(with request: URLRequest) async -> Result<Data?, Error> {
   await withCheckedContinuation { continuation in
      URLSession.shared.dataTask(with: request) { data, response, error in
         if let error = error {
            continuation.resume(returning: .failure(HTTPError.transportError(error)))
            return
         }

         let resp = response as! HTTPURLResponse
         let status = resp.statusCode
         guard (200...299).contains(status) else {
            continuation.resume(returning: .failure(HTTPError.httpError(status)))
            return
         }

         continuation.resume(returning: .success(data))
    }.resume()
}

Now we can check the Result<Data?, Error> for the error and see what type of error it is, for the example below I actually throw the error for the caller to catch

let result = await dataTask(url: url!, httpMethod: "POST", httpBody: httpBody)
if case .failure(let error) = result  {
   throw error            
}