asyncapi / shape-up-process

This repo contains pitches and the current cycle bets. More info about the Shape Up process: https://basecamp.com/shapeup
https://shapeup.asyncapi.io
11 stars 8 forks source link

Investigate correlations between languages #31

Closed magicmatatjahu closed 3 years ago

magicmatatjahu commented 4 years ago

Part of https://github.com/asyncapi/shape-up-process/issues/21 issue:

Check correlations between languages - similarities and differences in how a given language handles similar tasks and their overall syntax of what is allowed and what is not.

Things to check:

Try to create structure in language (by class/function/structure) from this json schema:

{
  type: 'object',
  properties: {
    displayName: {
      type: 'string'
    },
    email: {
      type: 'string',
      format: 'email'
    },
    createdAt: {
      type: 'string',
      format: 'date-time'
    }
  }
}

Try to create from above schema data model (using React or pure json) something like:

<Class name="SomeClass">
  <Property static private name="email" type="string" value="someEmail">
    <Annotation name="JsonProperty" value="email" />
  </Property>
  <Method public name="someMethod">
    <Argument type="string" name="displayName" />
    <Argument type="string" name="email" />
    <Body>
      return email == displayName;
    </Body>
  </Method>
</Class>

and check differences between languages.

@magicmatatjahu languages:

@jonaslagoni languages:

asyncapi-bot commented 4 years ago

Hey! You've labeled this issue as a Scope. Remember you can use the following command to inform about its progress:

/progress <percentage> [message]

or

/progress <percentage>

A mutiline message.
It supports **Markdown**.
Example:
/progress 40 We're still figuring out how to implement this. We have an idea but it is not yet confirmed it will work.
/progress 50

A few notes:

* We got this figured out :tada:
* We're going to use [this library](#link-to-website) to avoid losing time implementing this algorithm.
* We decided to go for the quickest solution and will improve it if we got time at the end of the cycle.

:weight_lifting_woman: See the progress on the Shape Up Dashboard.

jonaslagoni commented 3 years ago

C

Used the time to familiarize myself with C# and outlined some of the important features/syntaxes it has to offer for later references.

General keywords:

C# required syntax:

Packages

Uses namespace to wrap functionality, which can also be nested as deep as you want.

Imports

C# uses directive keyword using <<namespace>>; however it is not required to use, since fully qualified names are also possible. You can also alias a namespace by using Project = PC.MyCompany.Project;. To access the resources within alias namespaces they use the operator :: i.e. forwpf::Point where forwpf is an alias for System.Windows.

OBS

using keyword is also used in statements similar to try/catch.

There are some requirements to use the directive keyword:

C# also supports external namespaces uses something called extern alias GridV1;

Interfaces

Are supported and delcares methods to be implemented in class or struct.

interface IEquatable<T>
{
    bool Equals(T obj);
}

Classes

Both class and struct are supported, where struct is objects are passed by value and objects of classes are passed by reference. Nested classes are supported.

//[access modifier] - [class] - [identifier]
public class Customer
{
   // Fields, properties, methods and events go here...
}

Inheritance

Is done using the syntax class B: A {} where A is another class which B inherits from.

Attributes

Attributes, fields, variables are being used to describe the same thing. Declared in classes

Variables use special keywords such as:

Methods

Declared in class, struct or interface and supports the following modifiers (visibility modifiers excluded):

To pass arguments by reference C# uses either out or ref. The difference between the two is: ref tells the compiler that the object is initialized before entering the function, while out tells the compiler that the object will be initialized inside the function. It is required when using out that it is initialized within the method.

So while ref is two-ways, out is out-only.

Encapsulation

Two types of getter and setters are available Automatic Properties and the normal encapsulation such as:

private string email;
public string Email
{
    get
    {
        return email;
    }
    set {
        this.email = value;
    }
}

Annotations

Annotations, also called attributes in C#, can be placed on most declarations to add metadata. Attributes can have 3 types of parameters:

... positional, unnamed, or named. Any positional parameters must be specified in a certain order and cannot be omitted. Named parameters are optional and can be specified in any order. Positional parameters are specified first.

This means the following are equivalent:

[DllImport("user32.dll")]
[DllImport("user32.dll", SetLastError=false, ExactSpelling=false)]
[DllImport("user32.dll", ExactSpelling=false, SetLastError=false)]

Multiple attributes can be described as the following example for parameters:

using System.Runtime.InteropServices;
void MethodA([In][Out] ref double x) { }
void MethodB([Out][In] ref double x) { }
void MethodC([In, Out] ref double x) { }

They can also be targeted to certain targets using the form:

[target : attribute-list]

Visibility

Basic Types

C# is a strongly typed language, beside the built in types provides the following type variants:

jonaslagoni commented 3 years ago

/progress 5 Finished research regarding C#, next up C++.

jonaslagoni commented 3 years ago

C++

Used the time to familiarize myself with C++ and outlined some of the important features/syntaxes it has to offer for later references.

General keywords:

C++ required syntax:

OBS I have not consequently defined (sometimes I have when I've noticed it) which version of C++ syntaxs are added.

A note about header files

C++ uses .h files for declarations while the implementation recide in the .cpp file. Example

// my_class.h
namespace N
{
    class my_class
    {
    public:
        void do_something();
    };
}
// my_class.cpp
#include "my_class.h" // header in local directory
#include <iostream> // header in standard library

using namespace N;
using namespace std;

void my_class::do_something()
{
    cout << "Doing something!" << endl;
}

Although some inlines are possible to do.

Packages

Uses namespace to wrap functionality, which can also be nested as deep as you want.

namespace namespace_name {
   // code declarations
}

Namespace content can be accessed using scope operator ::.

C++ 20 have introduced something called module, import and export however it is still experimental so it will be excluded.

Imports

C++ uses the following syntax to include functionaliaty from other files:

#include "path-spec"
#include <path-spec>

The angle bracket are for including external (outside project) while the quoted inherits this after trying to search for it locally in the project first.

Interfaces

Is not directly supported, however methods can be defined using the keyword vitual in classes. To declare a method to be pure virtual which MUST be implemented by sub classes has the syntax = 0;:

virtual double getVolume() = 0;

If you want to define it as optional leave out = 0;

virtual double getVolume();

Classes

Supports both normal classes and structs. Some key differences

Classes has the syntax:

[template-spec]
class [ms-decl-spec] [tag [: base-list ]]
{
   member-list
} [declarators];

Where template spec are template specifications, ms-decl-spec storage-class specification, tag is the name of the class (if ommited anonymous class is define), base-list class or struct to inherit from, member-list Class Member Overview, declarators can be used to declare one or more instances of the class.

It is possible to overload operations such as +, == for classes, read more here.

Structs has the syntax

[template-spec] 
struct [ms-decl-spec] [tag [: base-list ]]
{
   member-list
} [declarators];

The definitions for struct is the same as for class.

[Inheritance]()

Single and multi inheritance are supported.

class Derived : [virtual] [access-specifier] Base1
  [ ,[virtual] [access-specifier] Base2, . . . ]
{
   // member list
};

It is possible to declare visibility to the base class i.e. what is accessible by the derived class.

If multiple base classes are used some conditions are applied. The order of initialization by constructor i.e. if you want one base class constructor to be called first it should be first in the list of base classes. The same is applied to destructors just in reverse order.

Given two base classes has same methods and a derived class inherit from both one has to declare calls such as:

class C : public A, public B {};
C *pc = new C;
pc->B::a();

Attributes

Called variables in C++.

Keywords:

User-defined literals where introduced in C++ 11 and allows the definition of values with custom litreals such as auto d = 42.0_km

C++ are notorish for its pointers and memory leaks. Normal pointers are called raw pointers and uses the syntax:

int* p = nullptr; // declare pointer and initialize it
                  // so that it doesn't store a random address
int i = 5;
p = &i; // assign pointer to address of object
int j = *p; // dereference p to retrieve the value at its address

However in modern C++ smart pointers are introduced so programmers dont have to worry about memory leaks.

Methods

Called functions in C++ and has the syntax:

declarations return-type name ( ... parameters) { 
   statements 
}

Optional function declarations:

Encapsulation

No special encapsulation such as C# offers - Wrapping variables are ofcourse still supported.

Lambda

are a type of anonymous function object which can be created where needed and has the syntax:

[capture-closure] ( ... parameters ) specifiers exception -> return-type {
   body
}

capture-close are used to access outside variables within the body of the lambda function. parameters optionally multiple function parameters. specifiers optional keywords such as mutable, constexpr and consteval. exception provides the definition of dynamic exception specification. return-type optionally the type of value returned by the function. body the function implementation.

[Annotations]()

C++ does not have anything related to annotations.

If you want to serialize and deserialize objects into/from JSON one could implement and provide the metadata themself so in the end you could endup with something like:

Dog dog;

dog.color = "green";
dog.barkType = "whaf";
dog.weight = 30;

Json::Value jsonDog = toJson(dog); // produces {"color":"green", "barkType":"whaf", "weight": 30}
auto dog2 = fromJson<Dog>(jsonDog);

std::cout << std::boolalpha << (dog == dog2) << std::endl; // pass the test, both dog are equal!

[Visibility]()

Visibility can be defined with a multiline approach such as:

   protected:
      int width;
      int height;

Basic Types

C++ is a strongly typed language, beside the built in types provides the following type variants:

jonaslagoni commented 3 years ago

/progress 10 Finished C++ research, next to do is JavaScript.

jonaslagoni commented 3 years ago

JavaScript

JS (JavaScript) are an interpreted language meaning that JS code are interpreted at run time. Most browsers however uses a JIT compiler which compiles JS to bytecode before its being executed.

Just to have a quick rundown of the JS module formats which are CommonJS (CJS), Asynchronous Module Definition (AMD), Universal Module Definition (UMD) and then there is native JS also refered to as ECMAScript where the implemented and used versions are ES5 (ECMAScript 5 (2009)) and ES6 (ECMAScript 2015). ES6 is however fully backward compatible with ES5. ES6 is supported in most browsers.

If you have to use the code in Node.JS and write in any other module formats a transpiler has to be used to convert it into CJS which is what Node.JS understands.

Strict mode is something that can be included by adding 'use strict'; which enables a stricker syntax validation, increased performance and some silent errors will be thrown.

Modules

I will be giving 2 examples for modules and imports in CJS and ES6 formats since these are the most used (by us).

ES6 uses export keyword while CJS uses module.exports or exports keyword.

ES6

export default function doSomething(n) {
  // do something
}
export function doSomething(n) {
  // do something
}

CJS

//exporting
module.exports = function doSomething(n) {
  // do something
}
exports.doSomething = (n) {
  // do something
}

Imports

ES6 introduces an import keyword while CJS uses require keyword.

ES6

// Everything
import module from 'module_name'
//Parts of the module
import { doSomething } from 'module_name'

CJS

// Everything
var module = require('module_name');
//Parts of the module
var { doSomething } = require('module_name');

Interfaces

Interfaces are not support and there is no way to declare a class must implement some function(s).

Classes

ES6 introduced the class and extend keyword to JS built upon prototypes. Just to quickly mention prototypes, it is the old way of defining inheritance between objects (and still needed for Internet Explorer solutions).

Extend does introduce inheritance however but multiple inheritance is not supported, therefore mix-ins where introduced.

Static methods and properties are also supported and works the same way as in other languages where the class does not need to be initialized to access it.

class Square extends SomeBaseClass {
  constructor(length) {
    // Here, it calls the parent class' constructor with lengths
    super(length, length);
  }
  //Static property
  static displayName = "Square";

  //Encapsulation
  get area() {
    return this.height * this.width;
  }
}

Mix-ins example:

let calculatorMixin = Base => class extends Base {
  calc() { }
};
let randomizerMixin = Base => class extends Base {
  randomize() { }
};
class Foo { }
class Bar extends calculatorMixin(randomizerMixin(Foo)) { }

Encapsulation

It is possible to declare functions as get and set property methods which allows for something like the following:

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
  // Getter
  get area() {
    return this.height * this.width;
  }
}
const square = new Rectangle(10, 10);
console.log(square.area); // 100

Attributes

Attributes are stated with either const, let or var which affects the scope of the variable as well as what is allowed to change.

Let

Not possible to re-declare variables.

'use strict';
let bar = "bar1";
let bar = "bar2"; // SyntaxError: Identifier 'bar' has already been declared

If let is used in a global scope its not created as a property on the global object.

let bar = "Bar"; // globally scoped
console.log(window.bar); // undefined

Accessing a let variable before initialized throws a reference error.

console.log(foo); // ReferenceError
let foo = "Foo";
console.log(foo); // Foo

Var

Can be re-declared without any issues

'use strict';
var foo = "foo1";
var foo = "foo2"; // No problem, 'foo' is replaced.

If var is used in a global scope it is created as a property on the global object.

var foo = "Foo";  // globally scoped
console.log(window.foo); // Foo

Accessing a var that is not yet initialized just returns undefined.

console.log(foo); // undefined
var foo = "Foo";
console.log(foo); // Foo

Const

Inherits all the same properties as let with the exception of it cannot be reassigned.

const PI = 3.141592653589793;
PI = 3.14;      // This will give an error
PI = PI + 10;   // This will also give an error 

Methods

In JavaScript methods are called functions and are First-class functions meaning they are treated as any other variables such as:

const sayHello = function() {
   return "Hello, ";
}
function greeting(helloMessage, name) {
  console.log(helloMessage() + name);
}
// Pass `sayHello` as an argument to `greeting` function
greeting(sayHello, "JavaScript!");

When a function is returned by another function that first functions is called a Higher-Order function. The returned function can be invoked directly when returned as so:

function sayHello() {
   return function() {
      console.log("Hello!");
   }
}
sayHello()();

Annotations

There is no annotations/metadata supported in the JS.

Visibility

There are no visibility modifiers supported in prior ES6. However scope introduces some sort of visibility such as the following:

function Person(name) {
  let age = 20; // this is private
  this.name = name; // this is public

  this.greet = function () {
    // here we can access both name and age
    console.log(`name: ${this.name}, age: ${age}`);
  };
}

let joe = new Person('Joe');
joe.greet();
// here we can access name but not age

ES6 introduced private visibility with the following class syntax (note not supported by all browsers/Node.JS and versions):

class Rectangle {
  #height = 0;
  #width;
  constructor(height, width) {
    this.#height = height;
    this.#width = width;
  }
}

Basic Types

JS is a loosely typed language meaning although variables have types underneath those types can be changed on the fly.

let foo = 42;    // foo is now a number
foo     = 'bar'; // foo is now a string
foo     = true;  // foo is now a boolean

The data types includes:

jonaslagoni commented 3 years ago

/progress 25 finished Javascript, next Haskell.

magicmatatjahu commented 3 years ago

Java

General keywords:

Java required syntax:

Packages

Packages are used in Java in order to prevent naming conflicts, to control access, to make searching/locating and usage of classes, interfaces, enumerations and annotations easier, etc.

A Package can be defined as a grouping of related types (classes, interfaces, enumerations and annotations) providing access protection and namespace management.

Conventions for packages in Java:

Example of package's declaration:

package com.somepackage;

public class SomeClass {}

Imports

Java has an import statement that allows you to import an entire package, or use only certain classes and interfaces defined in the package.

The general form of import statement is:

import com.somepackage.ClassName; // To import a certain class only
import com.somepackage.*;         // To import the whole package

public class SomeClass {}

Modules

To describe...

Classes

A Java class can contain the following building blocks:

Constructors

Java supports overloading of constructors, throwing exception in constructor, calling a constructor from an another constructor etc. More info and examples are here

Inheritance

In Java inheritance is declared using the extends keyword:

class Car extends Vehicle {}

Java doesn’t allow multiple inheritance to avoid the ambiguity caused by it.

final class Car {}

A final class cannot be extended. In other words, you cannot inherit from a final class in Java.

Interfaces

A Java interface is a bit like a Java class, except a Java interface can only contain method signatures and fields. A Java interface is not intended to contain implementations of the methods, only the signature (name, parameters and exceptions) of the method. However, it is possible to provide default implememntations of a method in a Java interface, to make the implementation of the interface easier for classes implementing the interface.

Class can implements interface by syntax:

class Car implements Engine, Tires {}

Implementation of multiple interfaces is allowed.

Properties in interface is similar to static fields in class (it can be used). Method needs to be implemented by some class before accesing. Method can be also static and this modifier treats method as callable outside of implmentation in class.

Interfaces in Java have a lot of features, so better will be read this document than describing them here.

Methods

The Java method signature looks like:

method signature

The void return type states that a method is a procedure.

Can be declared in class or in interface and supports the following modifiers:

Method parameters

A Java method parameter can be declared as final, just like a variable. The value of a final parameter cannot be changed. That is, if the parameter is a reference to an object, the reference cannot be changed, but values inside the object can still be changed. Example:

public void writeText(final String text1, final String text2) {
    System.out.print(text1);
    System.out.print(text2);
}

text1 and text2 parameters cannot be reassigned inside the method.

Exception declarations

If an error occurs inside a method, the method may throw an exception. Exceptions have to be declared in the method declaration throws {Exception}:

public String ret(String _string) throws MyException {
    if(_string == null) {
        throw new MyException("_string was null");
    }
    return _string;
}

Encapsulation

Encapsulation in Java can be done using setXXX and getXXX methods for properties in class. Example:

public class SomeClass {
   private int age;

   public int getAge() {
      return age;
   }

   public void setAge(int newAge) {
      age = newAge;
   }
}

Lambda

are a type of anonymous function which can be created where needed and has the syntax:

<function's parameters> -> <body of function>

Example:

(Integer x, Long y) -> System.out.println(x * y)

Asynchronous syntax

Java method cannot be async by modifier and also the word await cannot be used inside it. However, there are implementations of asynchronicity in standard libraries, which are based on OOP.

Annotations

Java annotations are used to provide metadata for Java code. Being metadata, Java annotations do not directly affect the execution of code, although some types of annotations can actually be used for that purpose.

Java annotations are typically used for the following purposes:

Annotation has form:

@<AnnotationName>(...parameters) target{class, method, property}

Example of using the annotation:

@SomeAnnotation(
  name="Jakob",
  age=37,
)
public class SomeClass {}

More info, how to create annotations and used them is here.

Visibility

Data Types

Java types can be divided to two kind:

More info is here.

Beside the built in types provides the following type variants:

NOTE: Java has also special keywords for multithreading programming like volatile etc, but there it isn't explained. For more information, please read this.

magicmatatjahu commented 3 years ago

/progress 30 finished Java, next Python.

magicmatatjahu commented 3 years ago

Python

Resources:

Python required syntax:

Modules and Packages

Modules and packages in Python allows to hierarchy code inside project. Difference between modules and packages can be described by describing packages:

Packages allow for a hierarchical structuring of the module namespace using dot notation. In the same way that modules help avoid collisions between global variable names, packages help avoid collisions between module names.

Modules are created by corresponding .py filenames and packages by corresponding directories names. So in hierarchy like:

dir -|
     |- module1.py
     |- subdir -|
                |- module2.py  

dir and dir.subdir are packages and module1 and module2 are modules.

More info about modules/packages is here.

Imports

Module contents are made available to the caller with the import statement. The import statement takes many different forms, shown below (with examples):

# FOR MODULES

# here `import` does not make the module contents directly accessible to the caller. Each module has its own private `symbol table`, which serves as the global `symbol table` for all objects defined in the module. Thus, a module creates a separate namespace.
import <module_name>[, <module_name> ...]
import mod, anothermod
mod.s # access to objects from `mod` module by `.` char - a module creates a separate namespace

from <module_name> import <name(s)>
from mod import s, foo

# This will place the names of all objects from <module_name> into the local symbol table, with the exception of any that begin with the underscore (_) character.
from <module_name> import *
s # this same as mod.s

from <module_name> import <name> as <alt_name>[, <name> as <alt_name> …]
from mod import s as string, a as alist
string # this same as mod.s

import <module_name> as <alt_name>
import mod as my_module
my_module.s # this same as mod.s

# module contents can be imported from within a function definition. In that case, the import does not occur until the function is called
def bar():
  from mod import foo
  foo('corge')

# FOR PACKAGES
# Packages very similar to modules but there we import modules as single objects

from <package_name> import <modules_name>[, <module_name> ...]
from <package_name> import <module_name> as <alt_name>

from pkg import mod2 as quux
from pkg.mod3 import *

More info about imports is here.

Functions

The usual syntax for defining a Python function is as follows:

def <function_name>([<parameters>]):
  <statement(s)>

where:

To Python's functions we can also pass arguments by Keyword Arguments, like:

some_function(qty=6, item='bananas', price=1.74)

More info about functions in Python is here. There is described Positional Arguments etc. very interesting features.

Classes

All class definitions start with the class keyword, which is followed by the name of the class and a colon. Any code that is indented below the class definition is considered part of the class’s body"

class Person:
  pass

The body of the Person class consists of a single statement: the pass keyword. pass is often used as a placeholder indicating where code will eventually go. It allows you to run this code without Python throwing an error.

class Person:
  # Class attribute/fields
  sex = "m"

  def __init__(self, name, age):
    self.name = name
    self.age = age

  def speak(self, sound):
    return f"{self.name} says {sound}"

# initialization of class
person = Person("John", 33)

__init__ function is a constructor of class and self represents created instance (like this in other languages).

Inheritance

Inheritance in Python is make by correlation between classes parent class -> child class. To create a child class, new class should be created with its own name and with the name of the parent class in parentheses. Example:

class Woman(Person):
  # Override `speak` method - change the returned string
  def speak(self, sound):
    return f"{self.name} woman says: {sound}"

In python we can access the parent class from inside a method of a child class by using super():

class Woman(Person):
  def speak(self, sound="Hello"):
    return super().speak(sound)

Interfaces

Interfaces in Python are handled differently than in most other languages, and they can vary in their design complexity. It is created like child class, so in example:

class Woman(Person):
  pass

Person class is an interface.

More info about it (with complex examples) is here

Methods

Instance methods are functions that are defined inside a class and can only be called from an instance of that class. Just like __init__, an instance method’s first parameter is always self.

class Person:
  # Class attribute/fields
  sex = "m"

  def __init__(self, name, age):
    self.name = name
    self.age = age

  # Instance method
  def description(self):
    return f"{self.name} is {self.age} years old"

  # Another instance method
  def speak(self, sound):
    return f"{self.name} says {sound}"

# invokation of method
miles = Person("Miles", 56)
miles.description()

Dunder methods

Methods like __init__ and __str__ (called when used is print function on given instance) are called dunder methods because they begin and end with double underscores. There are many dunder methods that can be used to customize classes in Python. Examples and usage of dunder methods can be found here.

Decorators

Python hasn't annotations like Java, but decorators. It doesn't collect metadata (but they can - by custom logic inside decorator - very similar to decorators in Typescript), but wraps a function, modifying its behavior. Example:

def my_decorator(func):
    def wrapper():
        print("Something is happening before the function is called.")
        func()
        print("Something is happening after the function is called.")
    return wrapper

@my_decorator
def say_something():
    print("Hello!")

say_something()
# Something is happening before the function is called.
# Hello!
# Something is happening after the function is called.

Decorators works also for classes and there it also wraps a class, modifying its behavior. Example:

from dataclasses import dataclass

@dataclass
class DataClassCard:
    rank: str
    suit: str

We should investigate more deeper dataclasses package, because it'll help us to create models for Python (like in QuickType). More info is here.

More info about decorators and their behaviours is here.

Typings/Function Annotations

Python (from v3.6) has a type system. It's very similar to TS type system so I don't want to described it here. More info is here.

Syntax for typings:

value: Type

Visibility

Python has no such visibility restrictions. All attributes and methods are visible to everyone. Anyone who wishes to use knowledge about the implementation of an object can do so. That can result in more efficient code. It can also result in a crash if the implementation changes. The language does nothing to prohibit programmers from "breaking encapsulation."

However, Python does provide name mangling to help hide names in classes. If you begin the name of an attribute of a class with two underscores, and you don't end it with any underscores, it is automatically rewritten to include the class name. Here's an example:

class XX:
  def __init__(self):
    self.__x=0

z=XX()
dir(z) # ['_XX__x']

The attribute __x in class XX was renamed to _XX__x. It doesn't prevent anyone from accessing it, but it does make it more unpleasant, which should serve to discourage casual use. Just as important, this keeps the attributes used privately by one class separate from those used by another.

Data Types

Example Data Type
x = "Hello World" str
x = 20 int
x = 20.5 float
x = 1j complex
x = ["apple", "banana", "cherry"] list
x = ("apple", "banana", "cherry") tuple
x = range(6) range
x = {"name" : "John", "age" : 36} dict
x = {"apple", "banana", "cherry"} set
x = frozenset({"apple", "banana", "cherry"}) frozenset
x = True bool
x = b"Hello" bytes
x = bytearray(5) bytearray
x = memoryview(bytes(5)) memoryview
def say_hello() function (first-class objects)
x = Foo() class

The other builded (composite/based on function) types could be find here.

jonaslagoni commented 3 years ago

Haskell

Haskell is a functional programming language using lazy evaluation with a mathematical syntax such as:

f :: int -> int
f x = x^2 + 3
-- which resembles a mathematical function
f(x) = x^2 + 3

"Literature":

Constraints:

Modules and imports

Haskell (as is described on beginning of investigation) is a functional language and everything is denoted as an expression, hence a Module can be called as a collection of similar or related types of functions.

More info about modules and imports is here. Imports private entities for testing - https://stackoverflow.com/a/14379426/6803886.

Modules

Syntax for Module:

module modid [exports] where body

where:

Example:

module Queue( module Stack(..), enqueue, dequeue ) where  

import Stack  

Above Queue module exports Stack module (which is imported) and enqueue, dequeue entities/functions. import Stack is a body of module. Very important:

Imports

Syntax for imports:

import [qualified] modid [as modid] [impspec] 

where impspec is:
( import1 , … , importn [ , ] )          where n >= 0
hiding ( import1 , … , importn [ , ] )   where n >= 0

The entities exported by a module may be brought into scope in another module with an import declaration at the beginning of the module. The import declaration names the module to be imported and optionally specifies the entities to be imported. A single module may be imported by more than one import declaration. Imported names serve as top level declarations: they scope over the entire body of the module but may be shadowed by local non-top-level bindings.

The effect of multiple import declarations is strictly cumulative: an entity is in scope if it is imported by any of the import declarations in a module. The ordering of import declarations is irrelevant.

Lexically, the terminal symbols as, qualified and hiding are each a varid rather than a reservedid. They have special significance only in the context of an import declaration; they may also be used as variables.

qualified keyword means that imported entities must by preceded by name of imported module like {moduleName}.{entity}. hiding hides entities from imported module. as keyword is a alias for imported module.

Some examples

Module A exports x and y:

Import declaration Names brought into scope
import A x, y, A.x, A.y
import A() (nothing)
import A(x) x, A.x
import qualified A A.x, A.y
import qualified A() (nothing)
import qualified A(x) A.x
import A hiding () x, y, A.x, A.y
import A hiding (x) y, A.y
import qualified A hiding () A.x, A.y
import qualified A hiding (x) A.y
import A as B x, y, B.x, B.y
import A as B(x) x, B.x
import qualified A as B B.x, B.y

Interfaces

Haskell hasn't got interface keyword, however class syntax works in similar way (in comparison to Java's interface).

Classes

Haskell has also class keyword but classes in Haskell works diffrerentially with comparison Java or C++. They allow us to declare which types are instances of which class, and to provide definitions of the overloaded operations/functions associated with a class - something like a interfaces rather than class in e.g. Java - classes are like generic interfaces.

For example:

class Eq a where 
  (==)                  :: a -> a -> Bool

Here Eq is the name of the class being defined, and == is the single operation (function) in the class. This declaration may be read "a" type a is an instance of the class "Eq" if there is an (overloaded) operation ==, of the appropriate type, defined on it..

And then we have a instances (implementation of class) of Eq class:

instance Eq Integer where 
  x == y                =  x `integerEq` y

instance Eq Float where
  x == y                =  x `floatEq` y

Methods

Methods in Haskell inside the class works in this same way as normal functions but can handle (extend) context of class: a.

class Eq a where 
  (==)                  :: a -> a -> Bool

instance Eq Integer where 
  x == y                =  x `integerEq` y

== is a method, the function integerEq happens to be the primitive function that compares integers for equality, but in general any valid expression is allowed on the right-hand side, just as for any other function definition. The overall declaration is essentially saying: The type "Integer" is an instance of the class "Eq:, and here is the definition of the method corresponding to the operation ==.

Inheritance

Haskell also supports a notion of class extension. For example, we can define a class Ord which inherits all of the operations in Eq, but in addition has a set of itself functions:

class  (Eq a) => Ord a  where
  (<), (<=), (>=), (>)  :: a -> a -> Bool
  max, min              :: a -> a -> a

In the context: Eq is a superclass of Ord (conversely, Ord is a subclass of Eq), and any type which is an instance of Ord must also be an instance of Eq (like in Java or other OOP languages).

Haskell also permits multiple inheritance, since classes may have more than one superclass. For example, the declaration:

class (Eq a, Show a) => C a where ...

creates a class C which inherits operations from both Eq and Show.

Attributes

In Haskell we cannot define attributes inside class, we must make first data (something like struct in C/C++) type and then implements (create instance) class for this type, like:

class Person a where
  firstName :: a -> String
  lastName :: a -> String
  age :: a -> Int
  getFullName :: a -> String

data Employee = Employee
  { employeeFirstName :: String
  , employeeLastName :: String
  , employeeAge :: Int
  , company :: String
  , email :: String
  , salary :: Int
  }

instance Person Employee where
  firstName = employeeFirstName
  lastName = employeeLastName
  age = employeeAge
  getFullName e = employeeFirstName e ++ " " ++ employeeLastName e

Encapsulation

Haskell hasn't encapsulation inside class (setter/getter).

Functions

Called functions in Haskell are defined with a type expression before the actual function definition are expressed.

The type expression is declared on the form:

function-name :: parameter1-type -> parameter2-type -> ... -> return-type

The function is defined on the form:

function-name parameter1 parameter2 ... = definition

Functions are generatlly called using a prefix notation:

return-value = function-name argument1 argument2 ...

However some functions such as + are considered an infix functions and can be called using 1 + 2 which is the same as the prefix notation (+) 1 2. Normal prefix functions can use infix notation by wrapping the function in `somefunction`.

List operations

List comprehension is way to generate certain values i.e. below code will generate a list of squared values for values between 1 and 10. It can consists of generators, guards, local bindings, and targets.

squares = [x * x | x <- [1..10]]

Some list operators.

Pattern matching

Pattern matching is a technique which allows one to specify multiple declarations based on parameter values. Pattern matching are matched from top to bottom. An example of such can be seen in the factorial implementation below:

fact :: Int -> Int 
fact 0 = 1 
fact n = n * fact (n-1) 
isEmpty (1:_) = "List is not empty!" 
isEmpty [] = "List is empty!"

Guards

Similar to pattern matching guarding is a bit different in how it matches expressions and similar to if-else statements. Guards are matched from top to bottom.

fact :: Integer -> Integer 
fact n | n == 0 = 1 
       | n /= 0 = n * fact (n-1) 

otherwise keyword can be used to describe a result for all other values then the ones that have been defined.

Local binding using Where and Let In

where and let do the same but its syntax is different:

rootsWhere :: (Float, Float, Float) -> (Float, Float)  
rootsLet :: (Float, Float, Float) -> (Float, Float)  
rootsWhere (a,b,c) = 
    (x1, x2) 
    where 
        x1 = e + sqrt d / (2 * a) 
        x2 = e - sqrt d / (2 * a) 
    d = b * b - 4 * a * c  
    e = - b / (2 * a)  
rootsLet (a,b,c) = 
    let d = b * b - 4 * a * c  
       e = - b / (2 * a) 
       x1 = e + sqrt d / (2 * a) 
       x2 = e - sqrt d / (2 * a)
    in (x1, x2)
main = do 
   putStrLn "The roots of our Polynomial equation are:" 
   print (rootsWhere(1,-8,6))
   print (rootsLet(1,-8,6))

Case expression

It is also possible to use a case statement similar to patern matching.

describeList :: [a] -> String  
describeList xs = "The list is " ++ case xs of [] -> "empty."  
    [x] -> "a singleton list."
    xs -> "a longer list."

otherwise keyword can be used to describe a result for all other values then the ones that have been defined.

Annotations

Haskell hasn't annotations like in Java or C#.

Visibility

Visibility is not something that can explicit be sat as other languages such as private, protected etc, instead its an inherit property based on the context of the code.

Basic Types

Every value has a strict type.

Structured types:

magicmatatjahu commented 3 years ago

/progress 35 finished Python, I'm helping Jonas in investigation of Haskell.

magicmatatjahu commented 3 years ago

/progress 50 finished Haskell

magicmatatjahu commented 3 years ago

GO

Resources:

Packages and modules

Go programs are organized into packages. A package is a collection of source files in the same directory that are compiled together. Functions, types, variables, and constants defined in one source file are visible to all other source files within the same package.

A repository contains one or more modules. A module is a collection of related Go packages that are released together. A Go repository typically contains only one module, located at the root of the repository. A file named go.mod there declares the module path: the import path prefix for all packages within the module. The module contains the packages in the directory containing its go.mod file as well as subdirectories of that directory, up to the next subdirectory containing another go.mod file (if any).

Imports

In GoLang we import packages using import statement:

import "fmt"

func main() {
  fmt.Println("Go is great!")
}

In importing like in above example import "fmt" we treat the package name (fmt) as alias to exported entities from fmt package - we can use Println function writing fmt.Println.

When importing multiple packages, its most common to wrap them with import ( ) rather than having to type out import everytime:

import (
  "fmt"
  "bytes"
)

We can use also custom aliases for imported packages:

import (
  f "fmt"
  "bytes"
)

func main() {
  f.Println("Go is great!")
}

For more information about imports (especially dot and blank imports) check this document.

Structs

Unlike traditional Object-Oriented Programming, Go does not have class-object architecture. Rather, GoLang have structures that hold complex data structures.

A struct type is nothing but a schema containing the blueprint of a data a structure will hold:

type StructName struct {
  // exported
  Field1 fieldType1
  // not exported
  field2 fieldType2
}

GoLang's structs also support as field function:

type StructName struct {
  Function func (string) string 
}

More info about structs (promoted fields, anonymous fields etc.) is here.

Methods

Go programming language supports special types of functions called methods. In method declaration syntax, a "receiver" is present to represent the container of the function. This receiver can be used to call a function using "." operator. Syntax:

func (receiver receiver_data_type) function_name([parameter list] = [parameter_name parameter_type, ...]) [return_type]{
  /* function body*/
}

Example:

/* define a circle */
type Circle struct {
   x,y,radius float64
}

/* define a method for circle */
func(circle Circle) area() float64 {
   return math.Pi * circle.radius * circle.radius
}

func main(){
   circle := Circle{x:0, y:0, radius:5}
   fmt.Printf("Circle area: %f", circle.area())
}

Constructor

GoLang's struct hasn't native constructor like in Java. Common practice in GoLang's community is creating function with New* suffix for creating instance of given type:

/* define a circle */
type Circle struct {
   x,y,radius float64
}

func NewCircle(x,y,radius float64) Circle {
  return Circle{x,y,radius}
}

Inheritance

There aren't inheritance like in Java or C# (by extend keyword syntax). However in GoLang we can use compisition and use anonymous fields in struct definition, like in example:

// declaring first  
// base struct  
type first struct{ 
    // declaring struct variable 
    base_one string 
} 

// declaring second 
// base struct 
type second struct{ 
    // declaring struct variable 
    base_two string 
} 

// function to return 
// first struct variable 
func (f first) printBase1() string{ 
    // returns a string 
    // of first struct         
    return f.base_one 
} 

// function to return 
// second struct variable 
func (s second) printBase2() string{ 
    // returns a string 
    // of first struct 
    return s.base_two 
} 

// child struct which 
// embeds (inherits) both base structs 
type child struct{ 
    // anonymous fields, 
    // struct embedding 
    // of multiple structs 
    first 
    second 
} 

More info about inheritance like in OOP languages is here.

Encapsulation

GoLang haven't native getters and setters, but there is possibility to create for each field in struct coresponding getter and setter:

/* define a circle */
type Circle struct {
   x float64
}

/* Getter */
func(circle Circle) GetX() float64 {
   return circle.x
}

/* Setter */
func(circle Circle) SetX(x float64) {
  circle.x = x
}

Annotations/Tags

There aren't annotations like in Java or C#. However GoLang has tags inside structs.

Go struct tags are annotations that appear after the type in a Go struct declaration. Each tag is composed of short strings associated with some corresponding value:

type User struct {
  Name string `example:"name"`
}

example:"name" is a tag.

Go tags is commonly used to define coresponding json/yaml definition:

type User struct {
  Name          string    `json:"name"`
  Password      string    `json:"password"`
  CreatedAt     time.Time `json:"createdAt"`
}

Reflections from the reflect package are used to read data from tags and process them.

Visibility

Unlike other program languages like Java that use access modifiers such as public, private, or protected to specify scope, Go determines if an item is exported and unexported through how it is declared. Exporting an item in this case makes it visible outside the current package. If it’s not exported, it is only visible and usable from within the package it was defined.

This external visibility is controlled by capitalizing the first letter of the item declared. All declarations, such as Types, Variables, Constants, Functions, etc., that start with a capital letter are visible outside the current package.

// exported
var Greeting string

// not exported
func hello(name string) string {
    return fmt.Sprintf(Greeting, name)
}

Basic Types

In Go language, the type is divided into four categories which are as follows:

  1. Basic type: Numbers, strings, and booleans come under this category. -> https://www.tutorialspoint.com/go/go_data_types.htm
  2. Aggregate type: Array and structs come under this category.
  3. Reference type: Pointers, slices, maps, functions, and channels come under this category.
  4. Interface type.
magicmatatjahu commented 3 years ago

/progress 60 finished Go

magicmatatjahu commented 3 years ago

Implementation in React

The following examples are only pure examples (mocks) and most likely our implementation won't look like this. They are used to show language differences and how they could be implemented as template using React.

Schema

{
  type: 'object',
  properties: {
    displayName: {
      type: 'string'
    },
    email: {
      type: 'string',
      format: 'email'
    }
  }
}

Languages

C

<Namespace name="temp">
  <Class name={commonModel.uid()}>
    {
      Object.entries(commonModel.properties()).map((propertyName, property) => {
        return (
          <Property 
            property={property} 
            private 
            name={propertyName}
          >
            <Metadata>
              [Serializable]
            </Metadata>
            <SetAccessor public />
            <GetAccessor public />
          </Property>
        )
      })
    }
    {/* Custom constructor beside default constructor */}
    <Constructor>
      <Argument type="string" name="displayName" />
      <Argument type="string" name="email" />
      <Body>
        this.displayName = displayName;
        this.email = email;
      </Body>
    </Constructor>
    {/* Add some custom method */}
    <Method public name="someMethod" returnType="bool">
      <Argument type="string" name="displayName" />
      <Argument type="string" name="email" />
      <Body>
        return email == displayName;
      </Body>
    </Method>
  </Class>
</Namespace>

Must have to be implemented: <Namespace>, <Import> (or <Imports>/<Using>), <Class>, <Constructor>, <Interface>, <Property>, <Method>, <Argument> (or <Parameter>), <Enum>, <Annotation> (or <Metadata>), <Setter> (or <SetAccessor>), <Getter> (or <GetAccessor>), <Body>. Getter and Setter could be include in <Accessors> component. String inside Method component could be treated as Body component.

Nice to have: <Struct>, <Lambda>, <Extern>.

Java

<Class name={commonModel.uid()}>
  {
    Object.entries(commonModel.properties()).map((propertyName, property) => {
      return (
        <>
          <Property 
            property={property} 
            private 
            name={propertyName}
          >
            <Annotation name="JsonProperty" value="email" />
          </Property>
          <SetAccessor public property={property} name={propertyName} />
          <GetAccessor public property={property} name={propertyName} />
        </>
      )
    })
  }
  {/* Custom constructor beside default constructor */}
  <Constructor>
    <Argument type="string" name="displayName" />
    <Argument type="string" name="email" />
    <Body>
      this.displayName = displayName;
      this.email = email;
    </Body>
  </Constructor>
  {/* Add some custom method */}
  <Method public name="someMethod" returnType="boolean">
    <Argument type="string" name="displayName" />
    <Argument type="string" name="email" />
    <Body>
      return email == displayName;
    </Body>
  </Method>
</Class>

Components

Must have to be implemented: <Package>, <Import> (or <Imports>), <Class>, <Constructor>, <Interface>, <Property>, <Method>, <Argument> (as parameter in method/constructor), <Enum>, <Annotation>, <Setter> (or <SetAccessor>), <Getter> (or <GetAccessor>), <Body>. Getter and Setter could be include in <Accessors> component. String inside Method component could be treated as Body component.

Nice to have: <Lambda>, <Record>.

Python

<Class name={commonModel.uid()}>
  <Decorator name="dataclass" />
  {
    Object.entries(commonModel.properties()).map((propertyName, property) => {
      return (
        <Property 
          property={property} 
          name={propertyName}
        />
      )
    })
  }
  {/* Class don't need constructor when it using dataclass decorator */}
  {/* Add some custom method */}
  <Method name="someMethod" returnType="bool">
    <Argument type="string" name="displayName" />
    <Argument type="string" name="email" />
    <Body>
      return email == displayName;
    </Body>
  </Method>
</Class>

Components

Must have to be implemented: <Import> (or <Imports>), <Class>, <Constructor> (dunder __init__ method), <Property>, <Method>, <Argument> (as parameter in method/constructor), <Decorator>, <Setter> (or <SetAccessor>), <Getter> (or <GetAccessor>), <Function>, <Body>. Getter and Setter could be include in <Accessors> component. String inside Method component could be treated as Body component. <Enum> (from enum package like class Days(enum.Enum)) - more info is here,

Nice to have: <Interface> (this same as <Class> in Python case), <DunderMethod> (or maybe use normal <Method>?).

JavaScript

<Class name={commonModel.uid()}>
  <Constructor>
    <Argument name="displayName" />
    <Argument name="email" />
    <Body>
      this.displayName = displayName;
      this.email = email;
    </Body>
  </Constructor>
  {/* Add some custom method */}
  <Method name="someMethod">
    <Argument name="displayName" />
    <Argument name="email" />
    <Body>
      return email === displayName;
    </Body>
  </Method>
</Class>

Components

Must have to be implemented: <Module> (for CommonJS), <Import> (or <Imports>), <Class>, <Constructor>, <Method>, <Argument> (as parameter in method/constructor), <Setter> (or <SetAccessor>), <Getter> (or <GetAccessor>), <Function>, <Body>, <Property>. Getter and Setter could be include in <Accessors> component. String inside Method component could be treated as Body component.

Nice to have: <Variable> (as standalone variable), then <Object>, <Array> etc..., <Lambda>

For Typescript also: <Interface>, <Type>, <Decorator>, <Enum>, <Declare>.

C++

<Namespace name="temp">
  <Class name={commonModel.uid()}>
    {
      Object.entries(commonModel.properties()).map((propertyName, property) => {
        return (
          <Property 
            property={property} 
            private 
            name={propertyName}
          >
            <SetAccessor public />
            <GetAccessor public />
          </Property>
        )
      })
    }
    {/* Custom constructor beside default constructor */}
    <Constructor>
      <Argument type="string" name="displayName" />
      <Argument type="string" name="email" />
      <Body>
        this.displayName = displayName;
        this.email = email;
      </Body>
    </Constructor>
    {/* Add some custom method */}
    <Method public name="someMethod" returnType="bool">
      <Argument type="string" name="displayName" />
      <Argument type="string" name="email" />
      <Body>
        return email == displayName;
      </Body>
    </Method>
  </Class>
</Namespace>

Components

Must have to be implemented: <Namespace>, <Import> (or <Imports>), <Class>, <Interface> (like <Class> with virtual methods), <Enum>, <Struct>, <Constructor>, <Destructor> <Method>, <Argument> (as parameter in method/constructor), <Setter> (or <SetAccessor>), <Getter> (or <GetAccessor>), <Function>, <Body>, <Property>. Getter and Setter could be include in <Accessors> component. String inside Method component could be treated as Body component.

Nice to have: <Using>, <Union>, <Lambda>.

Haskell

<>
  <Data name={commonModel.uid()}>
    {
      Object.entries(commonModel.properties()).map((propertyName, property) => {
        return (
          <Property 
            property={property} 
            name={propertyName}
          />
        )
      })
    }
  </Data>
  <Instance class="ToJSON" data={commonModel.uid()}>
    ... implementation of toJSON function
  </Instance>
  <Instance class="FromJSON" data={commonModel.uid()}>
    ... implementation of parseJSON function
  </Instance>
</>

Components

Must have to be implemented: <Module> <Import> (or <Imports>), <Data>, <Class>, <Instance>, <Function>, <Type>.

GO

<Struct name={commonModel.uid()}>
  {
    Object.entries(commonModel.properties()).map((propertyName, property) => {
      return (
        <>
          <Property 
            property={property} 
            private 
            name={propertyName}
          >
            <Annotation name="json" value={propertyName} />
          </Property>
          <SetAccessor public property={property} name={propertyName} />
          <GetAccessor public property={property} name={propertyName} />
        </>
      )
    })
  }
  <Constructor>
    <Argument type="string" name="displayName" />
    <Argument type="string" name="email" />
    <Body>
      return {commonModel.uid()}{`{displayName, email}`}
    </Body>
  </Constructor>
  {/* Add some custom method */}
  <Method name="someMethod" returnType="bool">
    <Argument name="displayName" type="string" />
    <Argument name="email" type="string" />
    <Body>
      return displayName
    </Body>
  </Method>
</Class>

Constructor is the same as:

<Method name={`New${commonModel.uid()}`} returnType={commonModel.uid()}>
  <Argument type="string" name="displayName" />
  <Argument type="string" name="email" />
  <Body>
    return {commonModel.uid()}{`{displayName, email}`}
  </Body>
</Method>

Components

Must have to be implemented: <Package> <Import> (or <Imports>), <Struct>, <Constructor> (as New* Method), <Method>, <Argument> (as parameter in method/constructor), <Setter> (or <SetAccessor>), <Getter> (or <GetAccessor>), <Function>, <Body>, <Enum>, <Interface>, <Type>, <Property> (for Struct), <Tag> (or <Annotation> for Struct's Property). Getter and Setter could be include in <Accessors> component. String inside Method component could be treated as Body component.

Nice to have: <Variable> (as standalone variable), <Const> (as standalone const) -> https://www.callicoder.com/golang-typed-untyped-constants/

Context

If we go with React engine as a base solution for model generation, then we should also introduce context for React components. It should works similar to React-DOM context. Let's check the example:

<NamespaceContext>
  <Namespace>
    <ClassContext>
      <Class>
        <PropertyContext>
          <Property />
        </PropertyContext>

        <MethodContext>
          <Method />
        </MethodContext>
      </Class>
    </ClassContext>
  </Namespace>
</NamespaceContext>

Then inside Property and Method components we can use contexts: NamespaceContext, ClassContext, PropertyContext (only Property can use it) and MethodContext (only Method can use it). In Class component we can use contexts: NamespaceContext, ClassContext. By this we lookup for contexts in parent components (not in children - it is not logical).

Using context in component:

import { useContext } from "@asyncapi/generator-react-sdk";

function Property({ ...props }) {
  // we can retrieve some data from ClassContext
  const { className } = useContext(ClassContext);
  // and also from NamespaceContext
  const { namespaceName } = useContext(NamespaceContext);
}

Context can store some data (because context can be written and used as normal JS class) in rendering time and then developer can change output based on stored data.

magicmatatjahu commented 3 years ago

/progress 75 Prepare implementation examples for each language using React

jonaslagoni commented 3 years ago

@magicmatatjahu what is left for this issue?

magicmatatjahu commented 3 years ago

@jonaslagoni Actually nothing, but we can close this issue before start date of second cycle, what do you think?

jonaslagoni commented 3 years ago

@magicmatatjahu if its done just close it now, the new issue has all the references to the issues 😄

magicmatatjahu commented 3 years ago

/progress 100 Finish investigation