Golang. A simple concept of a constructor

In Go it does not exist the concept of a constructor like in other languages. A struct is a very flexible construct that can be defined in many ways. When working with structs it is very important to take into consideration fields zero values and how these values may impact the code. In many cases, it is a good practice to define constructors, especially when certain values are not valid.

package main

type Box struct {
	Height int
	Width  int
}

// It's a kind of constructor
func NewBox(height int, width int) (*Box, error) {
	if height <= 0 || width <= 0 {
		return nil, errors.New("params must be greater than zero")
	}

	return &Box{height, width}, nil
}

func main() {
	b, err := NewBox(1, 2)
	if err != nil {
		...
	}
}

JavaEE/WildFly/vscode. Debugging a web application in the vscode in conjunction with the WildFly server

As an experiment, I tried to debug a simple web application running on the WildFly server in vscode. Of course, vscode does not have the same completeness as IntelliJ IDEA in terms of features, but it can be considered as a kind of alternative. So, in order to debug your application, start the WildFly server in debug mode, specifying the port to which vscode will connect (in my case, port 8787 is specified):

standalone.sh --debug 8787
After that add the configuration:

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "java",
            "name": "Debug (Attach)",
            "projectName": "your_project_name",
            "request": "attach",
            "hostName": "your_host_name",
            "port": "debugging_port"
        }
    ]
}
In my case the configuration looks like this:

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "java",
            "name": "Debug (Attach)",
            "projectName": "webapp",
            "request": "attach",
            "hostName": "localhost",
            "port": 8787
        }
    ]
}
Next, start debugging by pressing F5 Now everything is ready, so you can set breakpoints and debug your application. Happy debugging 🖖

Ruby. Iterating through a collection within specified a window

Rust has a method - windows (or equivalent - chunks)

  fn main() {
    let ints = [0, 1, 2, 3, 4, 5, 6, 7, 8];
    let slice = &ints;

    for el in slice.windows(3) {
      println!("window {:?}", el)
    }
  }
that allows you to iterate through a slice and read elements within specified a window. Below is the implementation of similar functionality in Ruby:

module Enumerable
  def windows(n)
    raise ArgumentError.new("Expected a value greater than 0") if n <= 0

    return to_enum(:windows, n) unless block_given?
    return nil if self.size == 0

    (0..(self.size/n)).each do |i|
      l = i * n
      yield self[l...(l + n)]
    end
  end
  
  alias_method :chunks, :windows
end

irb(main):014:0> [0, 1, 2, 3, 4, 5, 6, 7, 8].windows(3).each { |el| puts el.join(',') }
0,1,2
3,4,5
6,7,8

irb(main):015:0> [0, 1, 2, 3, 4, 5, 6, 7, 8].windows(0).each { |el| puts el.join(',') }
(irb):3:in `windows': Expected a value greater than 0 (ArgumentError)

irb(main):016:0> windows = [0, 1, 2, 3, 4, 5, 6, 7, 8].windows(5)
=> #
irb(main):017:0> windows.each { |el| puts el.join(',') }
0,1,2,3,4
5,6,7,8

Why algorithms (in particular, I/O algorithms) are important

Suppose we have a two-dimensional array, which is a map with given heights at different points. Let's calculate the average elevation. To do this, you can go through either each row:
sum = 0
for i = 0 to m - 1 do
  for j = 0 to m - 1 do
    sum = sum + A[i, j]
avg = sum / m^2
or each column:
sum = 0
for j = 0 to m - 1 do
  for i = 0 to m - 1 do
    sum = sum + A[i, j]
avg = sum / m^2
Then summarize all heights and divide by the number of points on the map. Do you think there is any difference between these methods? It would seem not, but in fact, there is. The first method (traversing all rows) turns out to be more productive than the second method (traversing the columns). The whole thing is related to the organization of the array in memory and access to each cell. I tried to compare on Java. That's what I did.

// Computing the average elevation
class Avg {
  public static void main(String[] argv) {
    int n = 1000; // 1000 * 1000 = 10^6
    int[][] matrix = new int[n][n];

    fillMatrix(matrix, n, 1);

    long start = System.nanoTime();
    int avg = avgRowByRow(matrix);
    System.out.println(String.format(
      "avg (Row By Row) = %s,
      it takes %s nano-seconds",
      avg,
      System.nanoTime() - start
    ));

    start = System.nanoTime();
    avg = avgColumnByColumn(matrix);
    System.out.println(String.format(
      "avg (Column By Column) = %s
      it takes %s nano-seconds",
      avg,
      System.nanoTime() - start
    ));
  }

  private static void fillMatrix(int[][] matrix, int n, int default_value) {
    for (int i = 0; i < n; i++) {
      for (int j = 0; j < n; j++) {
        matrix[i][j] = default_value;
      }
    }
  }

  private static int avgRowByRow(int[][] matrix) {
    int sum = 0, m = matrix.length;

    for (int i = 0; i < m; i++) {
      for (int j = 0; j < m; j++) {
        sum = sum + matrix[i][j];
      }
    }

    return sum / m / m; // sum / (m * m)
  }

  private static int avgColumnByColumn(int[][] matrix) {
    int sum = 0, m = matrix.length;

    for (int j = 0; j < m; j++) {
      for (int i = 0; i < m; i++) {
        sum = sum + matrix[i][j];
      }
    }

    return sum / m / m; // sum / (m * m)
  }
}
Then I tried to run it a couple of times and got the following result. The result can be said to be obvious:
avg (Row By Row)       = 1, it takes 3822568 nano-seconds
avg (Column By Column) = 1, it takes 5646746 nano-seconds

avg (Row By Row)       = 1, it takes 3902352 nano-seconds
avg (Column By Column) = 1, it takes 5467638 nano-seconds

avg (Row By Row)       = 1, it takes 3939734 nano-seconds
avg (Column By Column) = 1, it takes 5077092 nano-seconds

avg (Row By Row)       = 1, it takes 3736544 nano-seconds
avg (Column By Column) = 1, it takes 4950847 nano-seconds

avg (Row By Row)       = 1, it takes 3900019 nano-seconds
avg (Column By Column) = 1, it takes 5009578 nano-seconds

avg (Row By Row)       = 1, it takes 3954720 nano-seconds
avg (Column By Column) = 1, it takes 5109380 nano-seconds

avg (Row By Row)       = 1, it takes 3824056 nano-seconds
avg (Column By Column) = 1, it takes 5276105 nano-seconds

API Documentation. Multiple examples in Swagger

When describing the API documentation, sometimes there is a need to add several examples of responses. This is necessary for the possibility of a more complete description of the API documentation, since this API documentation is a kind of contract between the backend and the frontend. If you still use Swagger v.2.0 then you face the limitation - adding multiple examples is not supported. To get around this limitation, you need to go to the OpenAPI v. 3.0.0 specification. Then you will be able to add examples as follows.

- One example:

responses:
  "200":
    description: user creation successful
    content:
      application/json:
        example:
          token: fEt4IouUyRrqlx80treEWwq8
- A few examples:

responses:
  "422":
    description: user creation failed
    content:
      application/json:
        schema:
          properties:
            response:
              $ref: "#/components/schemas/ErrorResponse"
        examples:
          "Missing parameters":
            value:
              error: 'error-1'
              message: 'error-message-1'
          "Validation failed":
            value:
              error: 'error-2'
              message: 'error-message-2'


The differences between Swagger v.2.0 and the OpenAPI v. 3.0.0 specification are generally not so big:

    Swagger v.2.0     |  OpenAPI v.3.0.0
----------------------|-------------------
    info              |    info
----------------------|-------------------
   host               |
   basePath           |    servers
   schemes            |
----------------------|-------------------
   security           |    security
----------------------|-------------------
   paths              |   paths
----------------------|-------------------
   externalDocs       |   externalDocs
----------------------|-------------------
   tags               |   tags
----------------------|-------------------
                      |
  securityDefinitions |
                      |  components:
----------------------|    parameters
   produces           |    responses
----------------------|    examples
   consumes           |    requestBodies
----------------------|    headers
   definitions        |    links
   parameters         |    callbacks
   responses          |    securitySchemes
                      |

So the transition itself should not be difficult.