Concurnas
Paradigm | Multi-paradigm: concurrent, functional, imperative, object-oriented, reactive |
---|---|
Designed by | Jason Tatton |
Developer | Concurnas Ltd. |
First appeared | 4 December 2019 |
Stable release | 1.14.020
/ 8 March 2020[1] |
Typing discipline | Inferred, static, strong, structural |
Implementation language | Concurnas, Java, C |
Platform | JVM |
License | MIT.[2] |
Filename extensions | .conc |
Website | concurnas |
Influenced by | |
Clojure, Erlang, Go, Java, Kotlin, Python, Scala |
Search Concurnas on Amazon.
Concurnas (/ˈkɒ̃kɛrnɑːs/) is a statically typed, compiled, general-purpose programming language with type inference which presents the syntax of a dynamically typed language and functionality of a strong static type system. Concurnas is an optionally concise language allowing the code author to introduce verbosity to suit the audience reading the code.
Concurnas has been designed as a high performance language for building reliable, scalable, high performance concurrent, distributed and parallel systems. Concurnas source code is compiled to Java bytecode, enabling it to be executed upon the Java virtual machine (JVM). Concurnas is designed to be compatible with Java, and other programming languages which target the JVM[3]. Like other JVM languages it gains access to the Java standard libraries. Concurnas is a multi paradigm language which aims to provide the most used features of the imperative, functional, object-oriented and emerging reactive programming paradigms. Like most C family languages it uses a curly-brace syntax, but with optional semi colons. It embraces modern software engineering techniques such as null safety and dependency injection.
Concurnas offers a simplified means of performing concurrent computing which avoids the need to manage threads, Critical sections, Locks and shared memory as seen in traditional programming languages such as Java. This is achieved by performing computation in light weight thread like containers known as isolates. Isolates cannot directly share memory between one another except for through special types known as refs. Refs have the special property that they can be watched for changes by isolates, thus enabling reactive computing.
The language provides first class citizen support for GPU computing - with up-front syntactical and semantic checks taking place at compilation time. Code can be written in the style of idiomatic Concurnas code for subsequent execution upon the GPU[4].
A collection of mechanisms for creating Domain Specific Languages are provided; Extension Functions, Operator overloading, expression lists. It also supports third party language code being embedded within code via language extensions.
History[edit]
The design of Concurnas was started in 2017 by Jason Tatton[5][6].
Concurnas Ltd was established in 2018 as a commercial entity to support the development and maintenance of the language[7]. The first public release of Concurnas was on the 4th of December 2019[8].
Platforms, license and Distribution[edit]
Concurnas runs on the Java platform (Java virtual machine) and is compatible with existing Java programs. When targeting the GPU, Concurnas leverages OpenCL in order to provide support which is portable across GPU providers.
The Concurnas software distribution, including compiler, runtime and libraries, is released under the MIT license.[2]
The binaries for Concurnas are available at the Concurnas website, github and are available as part of the SDKMAN! platform.
Design Objectives[edit]
Concurnas has been designed with five core objectives[9]:
- Provide the syntax dynamically typed language with the performance and type safety of a statically typed language.
- To make concurrent and GPU programming more accessible for developers.
- To enable researchers and practitioners to be productive in the same language[10]
- Incorporate recent trends in modern software engineering such as nullability within its type system.
- To support domain-specific language development and permit embedding of other languages within Concurnas source code.
Features[edit]
Concurnas largely has the same compilation model as Java, Kotlin and Scala. Namely two separate phases, 1). Compilation into portable Java Bytecode, 2). Subsequent execution of said bytecode dynamically. This two phase approach allows syntactic, type and semantic checks to be performed up front, eliminating most errors before they have a chance to manifest at runtime.
Concurnas is able to run in a scriptable manner via its provision of REPL support[11]. In this mode the aforementioned two phase approach of compilation and execution is squashed into one.
Modern Programming[edit]
- Optional
val
andvar
may be used when declaring variables to indicate a new re-assignable and non re-assignable variable respectfully. Omitting eitherval
orvar
will default a new variable declaration to be re-assignable. - Semicolons are optional; the newline character is used to implicitly denote the end of a line of code. Semicolons may be used in addition to this or to separate logical lines of code which are defined on the same line.
- Parameter and return types follow, as in Pascal, rather than precede as in C.
- Curly-brace's are used to denote blocks for method, function and some statement bodies.
- All blocks can return values.
result = {10+20} cond = if(something()){ "this" } else{ "that" } plusOne = for(a in [1, 2, 3, 4]){ a+1 } def powTwo(a int) => a**2
- Types are largely optional. Type inference is used heavily to negate the need to explicitly define types. This is with the exception of method/function parameters which must be typed.
- Optionally concise, there are multiple ways to define the same function all with varying degrees of verbosity. e.g. the following function definitions are all equivalent:
def plus(a int, b int) int { return a + b } def plus(a int, b int){//infer return type return a + b } def plus(a int, b int){ a + b//implicit return } def plus(a int, b int) => a + b//compact function definition
- Unchecked exceptions. Means to throw and catch exceptions is provided but usage is optional.
- Ranges.
0 to 10
will produce an iterable sequence from0
to10
inclusive,0 to 10 step 2
will step this sequence in steps of2
.0 to
will produce an infinite sequence. - Functions and methods. Functions are defined in top level code, methods are structured identically but are nested within class definitions. Methods also have access to their host class state via the
this
andsuper
keywords. Functions may define default parametersdef doPLus(a int, b = 10) => a + b
, varargs:def sum(items int...) => ret = 0; for(x in items){ret += x}; ret
and when functions are invoked, parameters may be explicitly named:doPLus(a = 99)
.
Concurrent and Reactive Programming[edit]
Concurnas offers an alternative to the thread and lock based model of concurrent execution presented by language such as Java[12]. It presents Isolates, which are light weight threads that operate within their own memory state and are unable to natively share state between one another. This avoiding of state sharing means that state cannot be accidentally shared. Isolates are, at runtime, multiplexed on to underlying hardware threads and utilize continuation-passing style (CPS) in order to pause and resume execution of concurrent state. Isolates are is similar to Fibers proposed for Java in project Loom, this model greatly reduces the memory footprint of concurrent execution[13]. Implicitly or explicitly, all execution within Concurnas is performed using isolates. Isolates may be explicitly spawned by using the bang operator !
.
An example of spawning two simple isolates to perform calculation in parallel:
def gcd(x int, y int){//greatest common divisor of two integers
while(y){
(x, y) = (y, x mod y)
}
x
}
calc1 = gcd(8, 20)!//run this calculation in an isolate
calc2 = gcd(6, 45)!//run this calculation in a separate isolate
calc3 = calc1 if calc1 > calc2 else calc2
The last line of code, which depends upon the results of the first and section spawned isolate calls to gcd
can only proceed once the values of those calculations are known.
An example illustrating isolation of state between isolates:
n = 10
nplusone = { n += 1; n }!//perform this calculation in an isolate, a copy of n is made
nminusone = { n -= 1; n }!//another isolate, on a separate copy of n
//nplusone == 11
//nminusone == 9
In the above, the two spawned isolates will never interfere with one another's view of the variable n
, since this dependent variable is copied into the isolates. All dependent mutable state is copied into isolates before execution except for refs and actors since these provide their own concurrency control.
In addition to isolates, Concurnas provides refs in order share state between isolates in a non deterministic manner. Refs are implicitly returned from isolate blocks spawned using the bang operator !
. Ref types are denoted by postfixing a type with a colon :
. When a ref type is used where a non ref type is expected, it will be dereferenced. Refs may be updated one or many times by one or many refs on a non deterministic basis.
aRef int://this is a ref of type int
{
aRef = 10
}!
//If no value has yet been set execution will pause on the next line...
result int = aRef//De-reference aRef and set the value to result
In the above example the ref, aRef
, is finally dereferenced once it has been set by the spawned isolate. If it has not been set at this point execution will be paused until the concurrently executing isolate has set a value to it. An alternative to this approach is to use the await
keyword with optional guard condition. The await keyword will await one or more refs being set before proceeding with execution of the isolate which has defined it.
This will pause execution of a dependent isolate until state has been set. An example using the await keyword and a guard condition:
execount int: = 0
for(a in 1 to 10){//some complex calculation..
execount++
}!//spawn an isolate to execute the above concurrently.
await(execount; execount==10)
//^ pause further execution until execount == 10
The above will pause at the await
keyword until the guard operating on execount
(execount==10
) has been satisfied.
Reactive Programming[edit]
A feature of Refs is that they permit watching of changes. Thus when one or many ref's value changes an isolate may be awoken to react as appropriate to that change. This enables reactive computing. The every
and onchange
statements are used to support this reactive programming. The body of a every
or onchange
statement is executed every time a watched ref is updated. every
and onchange
statements, run as isolates and are able to return refs themselves. every
and onchange
statements differ in that every
statements will trigger on inception whereas onchange
statements will only trigger after creation if a watched ref is changed.
An example of an every
statement:
aval int://no initial value
bval int: = 22 //initial value known
sumvals int: = every(aval, bval) => aval + bval
//after initializing above, execution is allowed to carry on...
aval = 100
bval = 200
Above sumvals
will hold a stream of results of the every
statement evaluation of aval + bval
.
Compact Reactive programming may be achieved by using the compact forms of every
and onchange
:
a int:
b int:
c <= a + b //same as: c int: = every(a, b) => a + b
d <- a + b //same as: c int: = onchange(a, b) => a + b
Transactions[edit]
Concurnas supports Software transactional memory through optimistic locking. In this way refs can be atomically updated. Transactions in Concurnas are semantically similar to Transactions in Clojure. Care should be applied when using this functionality as, in the case of ref contention, the transaction will be repeated (hence the optimism) until a clean transaction can be completed[14]. An example:
acc1 int: = 100
acc2 int: = 100
trans{//update acc1 and acc2 atomically
acc1 -= 10
acc2 += 10
}
Temporal Programming[edit]
The isolate, ref and reactive programming model lend themselves to naturally supporting temporal programming. In Concurnas this is supported via the com.concurnas.lang.pulsar
library. This library produces refs which are updated at time intervals specified by their creating call by a Pulsar
instance.
An example of an event occurring in 10 seconds:
from com.concurnas.lang.pulsar import Pulsar, RealtimePulsar
pulsar = new RealtimePulsar()
oneOffTrigger := pulsar.after(java.time.Duration.ofSeconds(10))//schedule event for 10 seconds time...
every(oneOffTrigger){
System.out.println("Current time: " + oneOffTrigger)
}
The above will output the current time in after 10 seconds to Standard output (stdout).
Actors[edit]
Concurnas provides first class citizen support for the Actor model of compuataion. Each actor runs within its own dedicated isolate, code run within Concurnas Actors is always runs in a single threaded manner. This guarantees determinism of state within an Actor. Since actors implement their own concurrency control they may be freely shared between isolates without copying required. Actors are defined in the same way as normal classes, they may also be created of existing classes.
An example of a dedicated actor and an actor of an existing class:
actor MyActor{
-count = 0
def increment(){
count++
}
}
myCounter = new MyActor()//created like a normal instance object
actorOfList = actor java.util.ArrayList<String>()
Distributed Computing[edit]
Concurnas has language level support for distributed computing. This leverages the isolate and ref model of computation. Remote execution is provided by appending a reference to a remote executor to the right hand side of the bang operator !
. Code spawned remotely will have all its dependencies, in terms of state and code automatically passed to its remote execution site. A number of helper methods are provided to assist in the failure cases occurable during remote execution of code.
An example connecting to a remote execution site and executing code remotely:
rm = Remote('localhost', port = 42001)
//execute code remotely, returning an answer ref
answer int: = {10+10}!(rm.onfailRetry())
The above remote execution of the code {10+10}
occurs asynchronously, as such the calling program may conduct other execution whilst this is occurring.
GPU Programming[edit]
Concurnas allows developers to run code on Graphical Processing Units (GPU's) by writing largely idiomatic Concurnas code. This code is checked as part of the normal build process before being transpiled into OpenCL compatible C code[15]. Entry points for execution upon the GPU are called kernels. Concurnas supports the full memory model of GPU computation exposed by OpenCL including local memory. GPU's are treated like co-processors which can perform concurrent execution of code - as such aspects of the GPU computing process are supported via the ref and isolate model of concurrent computation exposed within Concurnas.
An example kernel for performing simple matrix multiplication:
gpukernel 2 matMult(wA int, wB int, global in matA float[2], global in matB float[2], global out result float[2]) {
globalRow = get_global_id(0) // Row ID
globalCol = get_global_id(1) // Col ID
rescell = 0f;
for (k = 0; k < wA; ++k) {//matrices are flattened to vectors on the gpu...
rescell += matA[globalCol * wA + k] * matB[k * wB + globalRow];
}
// Write element to output matrix
result[globalCol * wA + globalRow] = rescell;
}
Type system[edit]
Concurnas draws a distinction between primitive and Object types. The primitive types supported are scalars of: int, long, short, float, double, char, boolean, byte
. All other types are considered Object types, including n-dimensional arrays. Support is provided for implicit boxing and unboxing from a primitive type to an Object types and vice versa. e.g. anInt Integer = 99
and anInt int = Integer(99)
.
All object types are instances of classes. All classes are a subtypes of both java.lang.Object
and com.concurnas.CObject
(with CObject
being a subtype of Object
). In all Objects being a subtype of CObject
they are afforded a default toBoolean
method that can be used in order to convert a type to boolean representation enabling the following pattern of code: avar = MyClass(); something = 12 if avar else 99
.
Additional typing support is provided for actors (myactor actor MyClass = new actor MyClass()
), ref's (myref int: = 9; anotherRef := 88
), method references (def plus(a int, b int){ a+b }; myplus (int, int) int = plus&
) and tuples (aTuple (int, boolean) = 12, false
). These are also considered subtypes of Object
.
Classes and traits may define generic types. These support: Upper type bounds, covariance and contravariance and wildcards.
Concurnas supports multitypes, these are useful when defining functions which differ only in their input/output parameter types, they save the code author the need to copy-paste to multiple versions of the same code. For example: def plus10(what int|long|float) => what + 10
will produce three versions of plus10
differing only in their input parameter signature.
Nullability for Object types is built into the type system, post fixing an Object type with a question mark ?
: myvar String?
declares that the type can hold a null
value, omitting the ?
indicates that it may not hold a null
value.
Other aspects of Concurnas which can be considered part of its type system concerns include: Annotations, enumerations and traits.
Null safety[edit]
Concurnas is a nullnull safe language, that is to say, it incorporates the concept of nullability into its type system:[16]. A null value may not be assigned to a non-null type (which is the default in Concurnas). The following code resolves to a compilation error: myNullableString String = null
, rather since we are assigning null to myNullableString
, this would need its type declared as being nullable. This can be achieved by declaring the type as: myNullableString String? = null
.
If a nullable type is used in an unsafe manner, for instance, myNullableString.length()
this is flagged as a compilation error. Concurnas offers the following operators for instances working with nullable state is required:
?.
(safe call operator) - Can be used to safely access a field or invoke a method of a possibly nullable type. If the object is null, the method will not be called and the expression will return null, this of course implies that it will itself return a nullable type. For example:myNullableString?.length()
.?:
(Elvis operator) - Operates in the same manner as the safe call operator but returns a specified value instead of null. For example:noLongerNull String = myNullableString?:"its null"
.??
(No Null assertion) - Will throw a null pointer exception if its left hand size expression resolves to null. It essentially turns off null safety. For example:myNullableString??.length()
.
Functional Programming[edit]
In addition to supporting the object-oriented, reactive and imperative programing paradigms Concurnas also supports incorporates some aspects of functional programming. Together, these features afford Concurnas programmers the ability to mix aspects of the functional programming paradigm into their code where appropriate.
Features include:
- Type inference - Type inference is supported for variables, return values from functions and generics (based on usage). The use of type inference means that code written is often indistinguishable from Python code[17]
myvar = 66//myvar must be int def plus(a int, b int){//return type is int return a + b } //usage based generics: class Holder<X>{ ~x X? } h = Holder() h.x = "hi"//h is inferred to be of generic type: Holder<String>
- Nested functions - Functions can be nested within other functions:
def myfunction(toA int){ def rangeTo(toAdd int){//nested function toAdd + n for n in 0 to toA } rangeTo(4)..addAll(rangeTo(6)) }
- Lambdas - Lambdas enable functions to be created in a concise manner. They automatically capture any variables that are lexically available in the environment (e.g. a function or top level code) in which they are defined:
trait Doer<X>{ def invoke(a X) X } def SAMmap<X>(inputs list<X>, func Doer) { func(x) for x in inputs } mapped = SAMmap([1, 2, 3], a => a+10)//a => a+10 is the lambda
- Function references - Functions may be referenced and passed around a program like data:
def plus(a int, b int) => a+b plus10 (int) int = plus&(10, int) plus10(10)
- Currying - Currying support is similar to function references:
def providePartial() (int) int { def plus(b int) => b + 10 plus& } providePartial()(10)
- Pattern matching - Inspired by languages such as Scala and Haskell[18], pattern matching provides a succinct alternative to if-then-else blocks and switch statements found in traditional programming languages such as Java or Python. An example for matching Dog instance objects, integers and unknown input:
class Dog(-yearOfBirth int) def matcher(an Object){ match(an){ Dog(yearOfBirth < 1970) => "Dog. Born: {an.yearOfBirth}" Dog => "A Dog" int; < 10 => "small number" int => "another number" x => "unknown input" } } res = matcher(x) for x in [Dog(1829), Dog(2010), "oops", 43, 5]
- Tuples - Tuples offer an alternative to defining classes exclusively for the purpose of packaging data together. Tuples can be decomposed, which makes for some succinct algorithms, for instance, in finding the greatest common denominator of two integers:
def gcd(x int, y int){ while(y){ (x, y) = (y, x mod y) } x }
- Lazy evaluation - Normal expression evaluation is eager, i.e. at the point of declaration as opposed to when their resultant values are needed. However, in declaring a variable
lazy
, its assignment expression will only be evaluated the first time said variable is required. For example:
a = 100 lazy myvar = {a=400; 22}//assign, with side effect of re-assigning a def makeArray(lazy operate int) => [a operate a] res = makeArray(myvar:) //res returns: [100 22 400]
Working with Data[edit]
First class citizen support for working with common data structures including: arrays, matrices, lists and maps is provided.
Creating data structures[edit]
anArray = [1 2 3 4 5]
aMatrix = [1 2 3; 4 5 6; 7 8 9]
aList = [1, 2, 3, 4, 5]
aMap = {1 -> "one", 2 -> "two"}
Working with data structures[edit]
appended = [ anArray 34 ]//concatinate an element to an array
mat2 = [aMatrix aMatrix]//concatinate two matricies
inMap = 1 in aMap//checking for the prescence of a key
del mymap[1]//removing an element from a map
aSublist = inList[0 ... 2] //sublist of a list
inList = 1 in aList//checking for the prescence of an element
del aList[1]//removing an element from a list
List comprehension[edit]
Python style list comprehension is provided for all iterable data structures and maps. This offers an alternative to the conventional "foreach" loop. Optional guards are supported. An example iterating through each element of a list:
myList = [12, 15, 18]
ret = i+10 for i in myList if i mod 2 == 0
Vectorization[edit]
The language offers first class citizen support for vectorization. In essence this permits operations to be performed, optionally in place, on each element of an n-dimensional array or iterable data structure. Thus an array myarray = [1 2 3 4 5]
can have the value 10
added to each element to create a new array as follows: newarray = myarray^ + 10
, or this may be performed in place: myarray^^ + 10
. Operations may also be chained together without the creation of an intermediate temporary data structure, for instance applying x**2 + 10
to each element of a matrix: mat = [1 2 3; 4 5 6; 7 8 9]
as follows: (mat^**2)^+10
. Where possible, vectorization is an implicit operation, thus the previous example may be shortened to just: mat**2+10
.
The support for Vectorization does not take advantage of Single Instruction, Multiple Data (SIMD) instructions at the CPU microprocessor level since these are not currently supported by the JVM upon which Concurnas runs.
Object-oriented Programming[edit]
Concurnas is an impure object-oriented language in the sense that every value except primitive types are instance objects. Both state and behavior (which may apply to that state) can be described using classes and traits, instance objects are formed from concrete classes. Conventional inheritance and abstract (non-concrete) classes are supported. Mixin style composition of classes can be achieved through the use of traits (traits are also non-concrete). First class citizen support for dependency injection is provided.
All concrete classes have an implicit equality checker, hashcode representation and copy mechanism implicitly generated. Together these methods allow instance objects to be compared by value, stored in data structures which utilize hashcodes and deep copied without having to write extra code.
Class state (fields) and methods may be declared public
, package
, protected
or private
(defaulting to protected
) offering varying degrees of encapsulation.
When creating classes or traits, fields may be prefixed with a ~
, a -
or a +
these will generate for the field; a getter and a setter, just a getter or just a setter respectfully. When setting or getting fields of instance objects, this can be achieved by referencing the field of interest as if it were directly accessible, routing to the appropriate getter or setter will be implicitly performed. Thus the following: myObject.aField = 99
is equivalent to: myObject.setAField(99)
where myObject
is an instance of class: class MyClass(+aField int)
.
A simple class and its usage:
abstract class NamedEntity(~name String)//NamedEntity is a non concrete, abstract class
class BookCharacter(name String, -yob int) < NamedEntity(name)
/* BookCharacter is a subclass of NamedEntity
* It has a default constructor created for it of signature: (String, int) BookCharacter
* It has a getter and setter for name and a getter only for yob
*/
p1 = new BookCharacter("Toad", 1970)
p2 = p1@//perform deep copy
assert p1 == p2 //equivilent to p1.equals(p2) - using auto gennerated equality method
assert not(p1 &== p2)//&== checks for equality by reference
origname = p1.name //equivilent to: origname = p1.getName()
p1.name = "Ratty" //equivilent to: p1.setName("Ratty")
Traits[edit]
Traits offer three core solutions:
- State. Traits can hold internal state.
trait Logger{ -msgCount = 0 def log(msg String) => System.out.println("{msgCount++}: {msg}") } class UsesLogger ~ Logger{ def foo(){ log("called foo") } }
- Diamond pattern. The diamond pattern can be solved through the use of traits. A concrete class which makes use of traits exhibiting this behavior must disambiguate their inherited methods as follows:
abstract class AbstractBarClass{ def bar() => "version AbstractBarClass" } trait A{ def bar() => "version A" } trait B{ def bar() => "version B" } class FooClass < AbstractBarClass with B, A{ override def bar() => "" + [super[AbstractBarClass].bar(), super[A].bar(), super[B].bar()] }
- In the above concrete class
FooClass
(all the others are non concrete), we are forced to disambiguate the definition ofbar
as this is composed within the class with a conflicting definition from trait'sA
andB
. We choose to invoke the other versions ofbar
.
- Trait stacking. Traits may be stacked, their super class reference is established at the point of declaration within a concrete class, not statically at their initial point of declaration, enabling the following:
abstract class Operator{ def operate(a int) int => a } open class ID < Operator{ override def operate(a int) => a } trait PlusOne < Operator{ override def operate(a int) => super.operate(a)+1 } trait Square < Operator{ override def operate(a int) => super.operate(a)**2 } trait MinusOne < Operator{ override def operate(a int) => super.operate(a)-1 } trait DivTwo < Operator{ override def operate(a int) => super.operate(a)/2 } x = new ID ~ PlusOne, Square, MinusOne, DivTwo // (((x/2) - 1) ** 2) + 1 y = new ID ~ DivTwo, MinusOne, Square, PlusOne // reverse operator application z = new ID ~ DivTwo, MinusOne, Square, PlusOne{ override def operate(a int) => super.operate(a)+1000 }//reverse operator application with additional operation
Enumerations[edit]
Concurnas offers enumeration support, these are very similar to those provided in Java. An example:
enum Suits{CLUBS, DIAMONDS, SPADES, HEARTS}
//usage:
s = Suits.DIAMONDS
Annotations[edit]
Concurnas offers Annotation support, these are very similar to those provided in Java. An example:
annotation NameCount(name String, count = 0)
//usage:
@NameCount("aName")
def myFunction() => 16
Object providers[edit]
Object providers in Concurnas provide first class citizen support for dependency injection. provider
's provide classes (as denoted through the provide
keyword). provider
's must also define the dependencies of their provided classes. provider
's may be created and used like normal objects, with their methods corresponding to provided types.
An example provider with its usage:
trait MessageGetter {public def getMessage() String}
trait MessageSender{public def sendMessage(msg String) void}
inject class MessageProcessor(obtainer MessageGetter, sender MessageSender){
//the default constructor for MessageProcessor is marked as being injectable
public def processMessage(){
this.sender.sendMessage(this.obtainer.getMessage())
}
}
class SimpleMG ~ MessageGetter {
def getMessage() String => 'A message'
}
class MessagePrinter ~ MessageSender{
def sendMessage(msg String) void => System.out.println(msg)
}
provider MPProvider{
provide MessageProcessor //provide objects of this type
MessageGetter => new SimpleMG()//dependency satisfaction for MessageProcessor
MessageSender => new MessagePrinter()//dependency satisfaction for MessageProcessor
}
//to be used as follows:
mpProvider = new MPProvider()
mp = mpProvider.MessageProcessor()
//mp is a MessageProcessor instance with all dependencies satisfied
Domain Specific Language Development[edit]
Three core features of Concurnas enable Domain Specific Language (DSL) implementation:
- Operator overloading. Operator overloading allows the developer to use instance objects with the standard basic operators provided by Concurnas (
+
,-
,/
etc.)[19]. For example:
class Complex(real double, imag double){ override toString() => ip = "+" if imag >== 0 else ""; "{real}{ip}{imag}i" def +(\comp Complex) => Complex(\comp.real + this.real, \comp.imag + this.imag) def -(\comp Complex) => Complex(this.real-\comp.real, this.imag-\comp.imag) }
- The
Complex
class may not be used as follows: res = Complex(1.3, 9.2) + Complex(14.3, 9.2)//res ==> 15.6+18.4i
- Extension Functions. Similar to Kotlin, Concurnas allows the developer to create methods which can operate upon classes without having to formally extend said class or introduce a wrapper class around it. Extension functions have access to all publicly accessible state and methods. After declaration they may be invoked in the same way as normal methods can be on instance objects of their target class. Extension functions may be defined for classes or primitive types and they may implement operator overloading:
def String repeat(times int){//on an object type sb = StringBuilder(); while(times-- > 0){ sb.append(this) } sb.toString() } res = "hi".repeat(5)//res = hihihihihi //on a primative type: def int addtwice(a int) => this + a * 2 res = 12 addtwice 24//res ==> 60 //implementing operator overloading def String *(times int){ sb = StringBuilder(); while(times-- > 0){ sb.append(this) } sb.toString() } res = "hi" * 5//res = hihihihihi
- Expression Lists. These permit expressions to be written with implicit use of the dot operator and/or parentheses. For example:
class OpOnMe(-value int){ def inc() => this.value++;; } OpOnMe 12 inc inc getValue //above is equivilent to: new OpOnMe(12)..inc()..inc().getValue()
Language Extensions[edit]
Language Extensions permit code authored in other languages to be embedded within Concurnas code.[20]. The operates in much the same way as Language Integrated Query's (LINQ) within the Microsoft .NET Framework. Language extension's are expected to output valid Concurnas code, which enables them to be incorporated within of the normal compilation process of Concurnas code. This code is permitted to interact with the variables, classes and methods in lexical scope and it may create output variables, classes and functions/methods. This permits tight coupling between the host Concurnas code and said language extension code.
from com.mycompany.myproduct.langs using mylisp, mySQL, myAPL
class Person(name String, yearOfBirth int)
people list<Person>;
//populate a list of people...
//select from the list of people variable defined above...
millennials = mySQL||select name from people where yearOfBirth between 1980 and 2000||
//create a new function, fact for performing factorial
myAPL||fact{×/⍳⍵}||
fact(10)//use of function defined above. Returns: 3628800
In the above example we see that the SQL like code is able to refer to an instance object (a list of Person
objects) declared within scope of its declaration. Likewise, the myAPL
implementation is able to create a function, fact
, which is lexically accessible post declaration by the host Concurnas code.
Tools[edit]
Concurnas currently has Syntax Highlighting support for Sublime Text, Atom. IDE support for VS Code and Jupyter notebook are "coming soon"[21]
Javascript libraries for syntax highlighting include: Prism.js.
Concurnas comes packaged with REPL support[22]. It can be run from the command line post installation as follows:
C:\concurnas-1.14.017>conc
Welcome to Concurnas 1.14.020 (Java HotSpot(TM) 64-Bit Server VM, Java 11.0.5).
Currently running in REPL mode. For help type: /help
conc> 10+10
$0 ==> 20
conc>
References[edit]
- ↑ "Concurnas 1.14.020 released". Retrieved 2020-03-08.
- ↑ 2.0 2.1 "LICENSE file". 2020-03-11. Retrieved 2020-03-11 – via GitHub.
- ↑ "Concurnas: The New Language on the JVM for Concurrent and GPU Computing". 2020-05-26.
- ↑ "Concurnas : un langage pour la JVM, dédié à la programmation parallèle et distribuée". 2020-02-25. Retrieved 2020-03-25.
- ↑ "Concurnas Ltd Leadership". 2020-03-08. Retrieved 2020-03-08.
- ↑ "What an algorithmic trader does and why I quit to create my own programming language". 2020-02-18. Retrieved 2020-03-08.
- ↑ "Concurnas Ltd as a Company". 2020-03-08. Retrieved 2020-03-08.
- ↑ "Concurnas 1.13.108 released". 2020-03-08. Retrieved 2020-03-08.
- ↑ "Introducing new JVM language Concurnas". 2020-03-05. Retrieved 2020-03-11.
- ↑ "Ex-JP Morgan Trader Tackles Complex Computing with New Programming Language". 2020-02-26. Retrieved 2020-03-11.
- ↑ "Concurnas: The Concurnas REPL". 2020-03-08. Retrieved 2020-03-08.
- ↑ "Concurrent programming with Concurnas". Retrieved 2020-06-22.
- ↑ "Project Loom: Fibers and Continuations for the Java Virtual Machine". Retrieved 2020-03-08.
- ↑ "Concurnas: Concurrent Programming". Retrieved 2020-03-08.
- ↑ "Concurnas: GPU/Parallel programming". Retrieved 2020-03-08.
- ↑ "Concurnas: Null safety". Retrieved 2020-03-08.
- ↑ "Concurnas language taps JVM for high-performance apps". 2020-02-21. Retrieved 2020-03-08.
- ↑ "Concurnas vs Java Part 2". Retrieved 2020-03-08.
- ↑ "Concurnas: Operators". Retrieved 2020-03-08.
- ↑ "Concurnas: Language Extensions". Retrieved 2020-03-08.
- ↑ "Download Concurnas". Retrieved 2020-03-08.
- ↑ "Concurnas: The Concurnas REPL". 2020-03-08. Retrieved 2020-03-08.
This article "Concurnas" is from Wikipedia. The list of its authors can be seen in its historical and/or the page Edithistory:Concurnas. Articles copied from Draft Namespace on Wikipedia could be seen on the Draft Namespace of Wikipedia and not main one.
- Programming languages
- Concurrent programming languages
- Functional languages
- Java programming language family
- JVM programming languages
- Object-oriented programming languages
- Pattern matching programming languages
- Reactive programming languages
- Extensible syntax programming languages
- High-level programming languages
- Programming languages created in 2019
- Concurnas
- Scripting languages
- Software using the MIT license
- Statically typed programming languages
- 2019 software
- Cross-platform free software
- Free compilers and interpreters
- Source-to-source compilers