17 changed files with 2149 additions and 33 deletions
File diff suppressed because one or more lines are too long
@ -0,0 +1,73 @@
@@ -0,0 +1,73 @@
|
||||
/* |
||||
Date: 17.V.2011 |
||||
Author: pumbur <pumbur@pumbur.net> |
||||
*/ |
||||
|
||||
.hljs { |
||||
display: block; |
||||
overflow-x: auto; |
||||
padding: 0.5em; |
||||
background: #222; |
||||
} |
||||
|
||||
.hljs, |
||||
.hljs-subst { |
||||
color: #aaa; |
||||
} |
||||
|
||||
.hljs-section { |
||||
color: #fff; |
||||
} |
||||
|
||||
.hljs-comment, |
||||
.hljs-quote, |
||||
.hljs-meta { |
||||
color: #666; |
||||
} |
||||
|
||||
.hljs-string, |
||||
.hljs-symbol, |
||||
.hljs-bullet, |
||||
.hljs-regexp { |
||||
color: #ffcc33; |
||||
} |
||||
|
||||
.hljs-number, |
||||
.hljs-addition { |
||||
color: #00cc66; |
||||
} |
||||
|
||||
.hljs-built_in, |
||||
.hljs-builtin-name, |
||||
.hljs-literal, |
||||
.hljs-type, |
||||
.hljs-template-variable, |
||||
.hljs-attribute, |
||||
.hljs-link { |
||||
color: #32aaee; |
||||
} |
||||
|
||||
.hljs-keyword, |
||||
.hljs-selector-tag, |
||||
.hljs-name, |
||||
.hljs-selector-id, |
||||
.hljs-selector-class { |
||||
color: #8866cc; |
||||
} |
||||
|
||||
.hljs-title, |
||||
.hljs-variable, |
||||
.hljs-deletion, |
||||
.hljs-template-tag { |
||||
color: #bb1166; |
||||
} |
||||
|
||||
.hljs-section, |
||||
.hljs-doctag, |
||||
.hljs-strong { |
||||
font-weight: bold; |
||||
} |
||||
|
||||
.hljs-emphasis { |
||||
font-style: italic; |
||||
} |
@ -0,0 +1,83 @@
@@ -0,0 +1,83 @@
|
||||
#+SETUPFILE: ../config.org |
||||
#+OPTIONS: toc:nil |
||||
#+EXPORT_FILE_NAME: coh.html |
||||
#+DATE: 2021-02-11 |
||||
#+TITLE: Code of Honor |
||||
#+PAGE: true |
||||
|
||||
I always try to live by my code of honor. A man is nothing without his honor. “A sin is to betray your beliefs”. |
||||
|
||||
* Honorable life |
||||
- Don’t betray your code of honor. |
||||
- Defend your Bushido way. |
||||
- Live life as a Honorable Man. |
||||
- Don’t lie. |
||||
- Honesty takes courage. |
||||
- Don’t talk without a knowledge to support it. |
||||
- “Don’t argue with the crazy guy”. |
||||
- Always follow your sense of Justice |
||||
- Feel the pain of others. |
||||
- Always put your self in people’s shoe before making any decision. |
||||
- A simple smile goes a long way. |
||||
- There’s no honor in winning by cheating. |
||||
- “If you fall down 7 times make sure to get up 8 times.” |
||||
- Protect your promises. |
||||
- “For a samurai everywhere is Japan.” |
||||
- Holding back is disrespectful to your opponent. |
||||
- Live life in your way. |
||||
- “Respect is earned, not given”, So earn it. |
||||
- “A man is much more than the job he holds and clothes he wears.” |
||||
|
||||
* Science |
||||
|
||||
#+BEGIN_QUOTE |
||||
Truth is sought for its own sake … Finding the truth is difficult, and the road to it is rough. |
||||
For the truths are plunged in obscurity … God, however, has not preserved the scientist from error |
||||
and has not safeguarded science from shortcomings and faults. If this had been the case, |
||||
scientists would not have disagreed upon any point of science… Therefore, the seeker after the |
||||
truth is not one who studies the writings of the ancients and, following his natural disposition, |
||||
puts his trust in them, but rather the one who suspects his faith in them and questions what he |
||||
gathers from them, the one who submits to argument and demonstration, and not to the sayings |
||||
of a human being whose nature is fraught with all kinds of imperfection and deficiency. |
||||
Thus the duty of the man who investigates the writings of scientists, if learning the truth is his |
||||
goal, is to make himself an enemy of all that he reads, and, applying his mind to the core and margins |
||||
of its content, attack it from every side. He should also suspect himself as he performs his critical |
||||
examination of it, so that he may avoid falling into either prejudice or leniency. |
||||
|
||||
–- [[https://en.wikipedia.org/wiki/Ibn_al-Haytham][Ibn al-Haytham]] |
||||
#+END_QUOTE |
||||
|
||||
* Software Engineering |
||||
|
||||
- Simplicity over Complexity |
||||
- Simple first, then Easy |
||||
- Abstraction, Abstraction, Abstraction |
||||
- Put your conventions layer on top of a well written abstraction |
||||
- Always choose a name for your variables which implies the variable content or usage. |
||||
- Never ever use variable names such as x, a, b, etc. |
||||
- Bugs before new features |
||||
- Docs before new features |
||||
|
||||
** Library design |
||||
Major releases can contain backward incompatible changes. It’s better to use a different code name |
||||
instead of a major version number change to indicate that there going to be backward incompatible changes. |
||||
Minor releases shouldn’t break the dependency versions. E.g: X(0.6.0) depends on Python(3.5.x), X(0.7.0) |
||||
should depends on the same version python |
||||
|
||||
** New Feature Checklist |
||||
- Is it easy to extend the feature ? |
||||
- Is it scalable ? |
||||
- Is it easy to maintain ? |
||||
- Is it well documented ? |
||||
- Does it have the best possible performance ? |
||||
- What about tests ? |
||||
- Is it following the correct coding style ? |
||||
|
||||
** Resiliency Checklist |
||||
|
||||
- Things that might fail: |
||||
+ Dependent systems |
||||
+ Network |
||||
+ External storage |
||||
+ Database |
||||
+ “The Cloud” |
@ -0,0 +1,180 @@
@@ -0,0 +1,180 @@
|
||||
#+SETUPFILE: ../../config.org |
||||
#+TAGS: Serene(s) Languages(l) |
||||
#+CATEGORY: Engineering |
||||
#+DATE: 2019-12-07 |
||||
#+TITLE: Choosing the target platform |
||||
|
||||
After wrapping my head around the [[file:rational-and-a-name.org][rational]] of [[file:my-new-programming-language.org][My new programming language]], I have |
||||
a big decision to make. Choose a platform. |
||||
|
||||
As programmers, we have a tough life when it comes to making a decision that |
||||
has direct impact on our product. I'm pretty sure you went through this process |
||||
at least once. From choosing a semantically great name for a variable to choosing |
||||
a right technology for your next billion dollar startup. It is always hard to |
||||
pick a tech stack for a new project. Especially when the new product happens to be |
||||
a new programming language. If I get my hands dirty with a wrong tech stack for |
||||
a simple web application, no big deal. I still can rewrite the whole thing and |
||||
pay a penalty. But in the case of programming languages that's not the case. Wrong |
||||
platform can easily destroy you. From the dawn of computers, many smart people |
||||
created tons of languages. But only few of them made it to the top. While there |
||||
are so many reasons for their success, going with the right platform is one |
||||
the most important ones. |
||||
|
||||
The obvious question that comes to mind when we're talking about "The platform" |
||||
is, should we build a platform from scratch or should we piggyback on others? |
||||
Creating a programming language and a virtual machine from scratch is gigantic and |
||||
bone crushing task. It needs a crazy set of skills and knowledge. Even with such |
||||
wisdom and experience people who went through it has made many mistakes and had to |
||||
constantly iterate to come up with the right implementation. The evolution of |
||||
programming languages such as [[https://en.wikipedia.org/wiki/Scheme_%28programming_language%29][Scheme]] is a good example of it (for more information |
||||
take a look at [[http://www.r6rs.org/][R6R5]]. |
||||
|
||||
Building a VM is hard and Building a fast VM is even harder. While I think creating |
||||
a programming language and a VM from scratch is really fun, but it can be really |
||||
frustrating as well. I don't want to get annoyed with myself during the process and |
||||
abandon my goal. I should ride on the shoulders of the giants to gain benefit from |
||||
their great work. I should choose a platform that helps me to move faster and iterate |
||||
through different ideas quicker. |
||||
|
||||
From a technical perspective, Starting from scratch means that I have to write a |
||||
program that includes at least a parser and a compiler. Building a compiler |
||||
is no joke. Hypothetically let's say we have a working compiler and parser, What |
||||
about use libraries and ecosystem ?? It would be really hard to convince people |
||||
to use a programming language that does not have any useful library and they have |
||||
to build everything by themselves. It might have been the case 30 years ago but |
||||
it is not the case in the modern age of programming languages anymore. |
||||
|
||||
So the idea of creating [[https://serene-lang.org][Serene]] from scratch is out of the picture. We need to find a good |
||||
platform for it. But what are the options ??? |
||||
|
||||
* Racket :Serene:Languages: |
||||
#+BEGIN_QUOTE |
||||
Racket is a general-purpose programming language as well as the world’s first ecosystem |
||||
for language-oriented programming. Make your dream language, or use one of the dozens |
||||
already available. |
||||
#+END_QUOTE |
||||
|
||||
[[http://racket-lang.org][Racket]]is a dialect of Lisp which allows us to build our own language by extending it. While |
||||
Racket is really cool and have a long list of pros and [[https://beautifulracket.com/appendix/why-racket-why-lisp.html][many reasons why to use it]] |
||||
(It's Lisp after all), it has the disadvantage that forced me to stop thinking about it |
||||
for *Serene*. As I mentioned in the [[file:rational-and-a-name.org][rational]] I'm not trying to build a toy language or |
||||
a domain specific one and Racket's ecosystem isn't as great as a battle tested and well-known |
||||
ecosystem like Java or Python (or other popular ecosystems). |
||||
|
||||
* Javascript |
||||
We're living in the age of Web and one of the big players in this era is Javascript. The number |
||||
of the programming languages that compile to Javascript is increasing rapidly. Javascript |
||||
as a language [[https://whydoesitsuck.com/why-does-javascript-suck/][sucks]] but as platform it is amazing. Lots of money and engineering effort has |
||||
been spent on improving Javascript engines. As a result Javascript is a crappy language with |
||||
well engineered engines such as [[https://v8.dev/][V8]]. |
||||
|
||||
Creating a language based on Javascript platform means that I have to be involved with the whole |
||||
transpiling scenario and deal with the fact that this new language can be used on different |
||||
browsers or on the backend. Or even on IE6 (Just kidding). I don't want to deal with all this. |
||||
I think Javascript platform can't be a good fit for what I need. So I won't go into details about |
||||
it |
||||
|
||||
* Python |
||||
Python is another famous platform form creating programming languages. Many people have built |
||||
programming languages on top of Python (Checkout [[http://norvig.com/lispy.html][Lispy]] if you're a Python fan). Python is super |
||||
popular these days and you'll see it everywhere. Creating a language on top of Python (just |
||||
like Javascript) gives me access to a rich ecosystem with huge number of libraries and a robust |
||||
ecosystem. |
||||
|
||||
But as I mentioned in the [[file:rational-and-a-name.org][rational]] I want support for built-in concurrency and parallelism. |
||||
Python isn't even good when it comes to parallelism and concurrency. I'm using python for more |
||||
than 10 years now and I'm very familiar with it. I know about all the effort to create useful |
||||
concurrency and parallelism such as asyncIO. But the fact is Python is not designed for this job. |
||||
*The GIL* is a huge problem in Python that literally prevents us from Running two pieces of code |
||||
in parallel in two kernel space thread. It is a problem for me. If you can't do a decent concurrency |
||||
and parallel execution you have no chance against modern languages like Clojure, Go, Elixir and others. |
||||
Python is fine now despite of its problems because it is good at other stuff and people accepted it |
||||
for what it is. Python is out there for about 25 years now and it has established a big community. If |
||||
Guido van Rossum created Python a year ago, I'm pretty sure that it would've failed because |
||||
it can't compete with modern languages. Don't get me wrong, I'm not trying to trash Python. |
||||
It is great and it has many good qualities but a good Concurrency and parallel execution model |
||||
ain't one of them. |
||||
|
||||
* BEAM |
||||
Erlang ecosystem is amazing, Robust and well tested. I have read a lot about it and when |
||||
ever I'm studying anything around computer science that can be related to Erlang, I ask |
||||
myself "How is Erlang doing it?". Erlang ecosystem truly had a huge impact on the world |
||||
today. |
||||
|
||||
The problem with Erlang ecosystem for me is that I always read about it and my knowledge |
||||
around it is only theoretical. Building a language on top a platform needs a good level |
||||
of practical experience on the platform as well which I don't have that. So it's obvious |
||||
that I have to pass. |
||||
|
||||
* The JVM |
||||
As much as I dislike Java (Mostly because of the syntax and the fact that it is an object |
||||
oriented language), I like JVM a lot. The JVM is battle tested, well design (Well, sort of. |
||||
But it's certainly evolving.) and fast VM. It should be the most popular VM in the world |
||||
(I'm just guessing). It is one of the world's most heavily-optimized pieces of software. |
||||
Plenty of researches have been made to make it better and better. |
||||
|
||||
The JVM has a mature ecosystem and a massive community of developers that resulted in an |
||||
unbelievable number of libraries (not the largest though, NPM is the largest artifact |
||||
repository. But it has a huge amount of useless BS as well). By targeting the JVM, |
||||
users will have an easy time adopting the new languages because of the rich tools set |
||||
provided by the Java ecosystem and all the languages that targeted JVM as well. For example, |
||||
it will be possible to use libraries written in Scala or Clojure as well. |
||||
|
||||
Long story short, I think the JVM is the right platform for me. The fact that many languages |
||||
have chosen it as their base platform shows how useful it can be. But there is a problem. |
||||
Targeting a higher level virtual machines like the JVM means that I'll have an easier job to |
||||
create a compiler. But I still have to write one. A compiler that takes the code and produces |
||||
JVM bytecode. As I mentioned earlier, writing a compiler is an enormous task and the chance |
||||
of doing it wrong with someone like me who has never built a compiler before is very high. |
||||
|
||||
* One VM to rule them all |
||||
Luckily there is a solution. I can write an interpreter in a VM that is designed to optimize |
||||
my interpreter with all that wonderful JIT compilation magic. Oracle has released a new VM |
||||
that hopes to make writing language interpreters both easy and fast. It can also leverage |
||||
the huge ecosystem of the JVM. It is an enhanced JVM that contains a new JIT compiler which |
||||
can speed up interpreters to near Java speed. The new JIT compiler is called Graal. To use |
||||
the Graal’s JIT magic we can use the Truffle library to create the interpreter. We will |
||||
annotate the interpreter and give Graal some hints on invariants and type information. |
||||
According to Graal's documents, By doing this integration effort we get significant speedups |
||||
in out interpreter without having to resort to writing a bytecode compiler. |
||||
|
||||
[[https://www.graalvm.org/][GraalVM]] is a Java VM and JDK based on HotSpot/OpenJDK, |
||||
implemented in Java. It supports different execution modes, like ahead-of-time compilation |
||||
of Java applications for fast startup and low memory footprint. |
||||
|
||||
#+BEGIN_QUOTE |
||||
GraalVM is a universal virtual machine for running applications written in JavaScript, |
||||
Python, Ruby, R, JVM-based languages like Java, Scala, Groovy, Kotlin, Clojure, and |
||||
LLVM-based languages such as C and C++. |
||||
|
||||
GraalVM removes the isolation between programming languages and enables |
||||
interoperability in a shared runtime. It can run either standalone or in |
||||
the context of OpenJDK, Node.js or Oracle Database. |
||||
#+END_QUOTE |
||||
|
||||
I copied the above paragraph from GraalVM's official website. It is truly a VM to rule |
||||
them all. |
||||
|
||||
[[https://github.com/oracle/graal/tree/master/truffle][Truffle]] library is one the key players in GraalVM. The initial results of Truffle are |
||||
super exciting. Implementations of Ruby in Truffle has a performance on the same order |
||||
of magnitude as the much bigger projects of JRuby. Just checkout [[https://chrisseaton.com/truffleruby/][Truffle Ruby]]'s |
||||
website to get amazed by it. There is a [[https://github.com/graalvm/graaljs][javascript implementation]] as well which showed |
||||
great progress as well. Lots of research has been dedicated to this topic and the result |
||||
is mind blowing. The interesting thing is that these Truffle implementations were done with |
||||
fewer people in a shorter period of time. This means you can create your own language on the JVM that |
||||
takes advantage of all it’s existing libraries, native threading, JIT compiler without |
||||
having to write your own compiler, and you get speeds that took other languages years |
||||
to achieve. |
||||
|
||||
Using GraalVM as the platform for my new language will help me to be much faster because |
||||
All I need to do is to build an [[https://en.wikipedia.org/wiki/Abstract_syntax_tree][AST]] interpreter and Graal will handle the rest. It means |
||||
that I can start by building what is important and use a very well engineered toolkit in |
||||
my advantage to get to my goal quicker and then later on replace any part that I like with |
||||
my own implementation. How cool is that??? |
||||
|
||||
But as an engineer and a wannabe scientist I'd like to see the proof with my own eyes. Not |
||||
because I don't trust academic work, Just because it feels good to experience the proof. |
||||
|
||||
So to begin with I'm going to create a dead simple Serene interpreter in Java and OpenJDK |
||||
and then build the same interpreter using Java on GraalVM using Truffle library and |
||||
compare the results and prove myself that choosing GraalVM is the right choice. |
@ -0,0 +1,49 @@
@@ -0,0 +1,49 @@
|
||||
#+SETUPFILE: ../../config.org |
||||
#+TAGS: Serene Languages |
||||
#+CATEGORY: Engineering |
||||
#+DATE: 2019-11-25 |
||||
#+TITLE: My new programming language |
||||
|
||||
As a software engineer, one of my joys in life is to learn new things. I can't describe the |
||||
pleasure of learning a new technology or stepping forward in the world of science. If you experienced |
||||
such a delight, you would know that how addictive it is. I can't satisfy my hunger for knowledge and |
||||
it might sounds like a gloat but it's truly joyful (ok mate, you love to "read", get on with it). |
||||
|
||||
Programming languages are the most common tool among programmers and software engineers (Duh!). |
||||
I believe that learning new programming languages helps us to widen our vision as engineers and |
||||
help us improve our mentality about software architecture and design. So clearly I never say "No" |
||||
to learning a new programming language and because of that I have lots of experience with different |
||||
languages. Each language taught me tons of new things and helped me enhance my skills. I've studied |
||||
many languages and have a long list of them as my "To Learn" list. |
||||
|
||||
Approximately a year ago, I was frustrated with Python and nagging to my wife about it (She always listens to |
||||
my gibberish). All of a sudden she suggested to me that "Why don't you write your own programming language ?". |
||||
That got me thinking, "Is it a good idea to do so ????". |
||||
|
||||
* My way of learning :Languages:Serene: |
||||
People have different routines for learning. I'm one of those people who likes to learn new |
||||
things by understanding how it works first. I'd like to start my learning process by understanding |
||||
the laws of the universe. In this case, "universe" is the implementation and theory behind |
||||
the thing I'm trying to learn. It might seem like crazy idea but that's how I learn better. |
||||
For instance, when I was a teenager and was learning about how to use Gnu/Linux, I was so |
||||
obsessed with internal of a Linux distribution to a degree that I decided to build my own |
||||
distribution. Of course as a teenager I was naive and dreamed about my distro ( Which I used |
||||
to call Liniera ) to become a well-known and popular distribution. Aside from my childhood dreams |
||||
I learned a lot by creating a distribution. Learned about Linux kernel, boot process, bootloaders |
||||
and tons of other complicated pieces that normally people don't get to know at first (I was using |
||||
LFS and Debian tools). So after that delightful but tough experience, I always try to build a minimal |
||||
prototype of whatever I'm trying to learn to comprehend the universe of that thing which |
||||
helped me a lot to this day. |
||||
|
||||
Based on my history, routines and the question that my wife has planted in my mind and after about a year |
||||
researching and thinking about it, Finally I realized it can be a good idea to create a new |
||||
programming language. To be honest it is not a task to be taken lightly. Whenever I created something |
||||
that wasn't out of my needs, I just failed. But this time I think creating a programming language can |
||||
massively help me to gain a better grasp of "the universe". Trying to overcome this challenge will help |
||||
me to grow and be a better engineer despite the fact that this new language may not even make it to the |
||||
list of known programming languages. I'm fine with that as long as it pushes me a step forward in my way |
||||
of life and brings me joy of wondering around in the world of science and engineering. |
||||
|
||||
I'm going to write more blog posts about my journey through this humongous task as a journal for myself and other |
||||
enthusiastic people. After all it will be a hobby of mine and not my day to day job. So I'll take my time and |
||||
move slowly but steady. |
@ -0,0 +1,52 @@
@@ -0,0 +1,52 @@
|
||||
#+SETUPFILE: ../../config.org |
||||
#+TAGS: Serene Languages |
||||
#+CATEGORY: Engineering |
||||
#+DATE: 2019-12-01 |
||||
#+TITLE: Rational and a name |
||||
|
||||
*This post is a draft and I'll finish it gradually* |
||||
|
||||
As I mentioned in [[./my-new-programming-language.org][My new programming language]], I'm creating a new programming language. |
||||
I'll try to pick up good points of different programming languages and avoid the cons |
||||
of them. One of the most important aspects of any project is to have a rational for it. |
||||
It's what I learned from [[http://clojure.org][Clojure]]'s culture. Rationals are a big deal in any clojure |
||||
developers world. As fan I'd like to start my new programming language by writing down |
||||
the rational of what I'm trying to achieve. |
||||
|
||||
* Rational (Take 1) and goals :Languages:Serene: |
||||
So far, the main reason to create a new language for me is to *learn more and |
||||
educate myself*. But it doesn't mean that I'm aiming for a toy language. I want |
||||
to create a general purpose language that solves some problems. Here is a list of |
||||
reasons that made me consider the idea of creating a new language (in no specific order): |
||||
|
||||
** Lisp is superior |
||||
I think the world needs more and more dialects of Lisp. It's the second oldest |
||||
programming language in the world and as far as I know the oldest one that is |
||||
still active. **Lisp** is elegant and amazing, but unfortunately not so many |
||||
programmers know about it. Even most of the those people who heard the name |
||||
are distracted by the "parenthesis". But they're missing the fact that there |
||||
is a good reason for all those parenthesis. Lisp is the simplest language I |
||||
know, its programs are written in its own datatypes. How simple is that??? |
||||
|
||||
You might hear that [[https://twobithistory.org/2018/10/14/lisp.html][God has created the universe in Lisp]]. |
||||
Lisp is amazing and I consider a programmer who has understood *the Lisp way*, the |
||||
[[https://twitter.com/lxsameer/status/1172220581992980480][luckiest]]. |
||||
|
||||
I'd my new language to be Lisp, because just being a Lisp brings a huge deal to the |
||||
table. |
||||
|
||||
** Simplicity |
||||
** FP is the future |
||||
** Development process |
||||
** Better core development |
||||
** Built-in Concurrency and parallelism |
||||
|
||||
* A Name |
||||
If you're a programmer, I'm pretty sure that you already experienced the terror |
||||
of trying to find a name for your project. Frankly, It's even hard to find a good |
||||
name for your variable. |
||||
|
||||
After about 10 days of searching finally my wife came up with a good name. *Serene*. |
||||
Calm and peaceful. I like it. It's simple and beautiful with a great meaning. |
||||
|
||||
I can't wait to start working on it. :P |
@ -0,0 +1,257 @@
@@ -0,0 +1,257 @@
|
||||
#+SETUPFILE: ../../config.org |
||||
#+TAGS: Serene(s) Languages(l) |
||||
#+CATEGORY: Engineering |
||||
#+DATE: 2020-01-03 |
||||
#+TITLE: Serene (simple versoin) |
||||
|
||||
As you might already know I'm working on [[file:my-new-programming-language.org][my own programming language]] |
||||
for a while now. I'm still on early stages and working on [[file:choosing-the-target-platform.org][choosing the right platform]] |
||||
for [[https://serene-lang.org][Serene]] and trying to spend time on doing enough research and make decision based |
||||
on facts, scientific papers and collected data from experiments |
||||
rather than rushing into things and end up with a mess. |
||||
I believe those languages that take their time and move slowly |
||||
but with great research, plan and design are more successful in |
||||
the long term (Thanks to *Pouya* for pointing it out). Take *Clojure* |
||||
as an example. They are taking their time, experimenting and validating |
||||
their hypothesis. As a result, Clojure is a well designed, stable |
||||
and highly backward compatible language with amazing and productive |
||||
pace. However, some other languages like Python are extremely |
||||
popular and consequently has more contributors. Dealing with all |
||||
those contributors caused Python to move faster than it should and |
||||
they ended up with some bad choices and horrible designs that |
||||
fixing them requires an humongous effort. Gradually, it becomes |
||||
harder and harder to fix those and move away from them. GIL is a good example, |
||||
instead of fixing the issue and removing the GIL, they are introducing |
||||
(at the of writing this article they added some basic support to latest |
||||
python release but far from what they want) [[https://lwn.net/Articles/754162/][something else]] to fix the original |
||||
problem but it might become a pain point itself. In order to avoid these kind |
||||
of problem as much as possible I'm trying to take my time and do as |
||||
many as experiments as I need. |
||||
|
||||
As I mentioned [[file:choosing-the-target-platform.org][earlier]] I think *GraalVM* and *Truffle* is the right answer for |
||||
Serene. But to verify my initial Idea I decided to run an experiment. |
||||
The experiment is all about implementing a Lisp in two environments. |
||||
A pure Java implementation vs a *Truffle* implementation. |
||||
|
||||
I spent several days and implementing the pure java version. The repository |
||||
of the simple version is available in the [[https://devheroes.codes/Serene/serene-simple][repo]] |
||||
This is a dummy version, but good enough lisp that I didn't paid too much attention |
||||
to the details and just created a very simple lisp with the following specification. |
||||
|
||||
#+BEGIN_QUOTE |
||||
Note: In this post whereever I use the name **Serene** for the implementation, |
||||
I'm referring to the simple version. |
||||
#+END_QUOTE |
||||
|
||||
* Data structures :Serene:Languages: |
||||
Since I tried to avoid unnecessary work, I didn't do much and implemented |
||||
just one collection type which is the most important and essential data |
||||
structure in any Lisp, the mighty List. While my final goal is to have functional |
||||
data structures, this List is not a functional one and is a simple linked |
||||
list. You can find the implementation under =serene.simple.ListNode=. |
||||
|
||||
For the number types I just added support for =Long= and =Double= |
||||
via `serene.simple.SNumber` class which acts as a dispatcher between two inner |
||||
classes. |
||||
|
||||
For Strings, boolean types and =nil=, I just used the equivalent Java data |
||||
structures directly. |
||||
|
||||
* Reader/Parser |
||||
Instead of using a parser generator or a sophisticated parser, I just created |
||||
a simple read ahead of position based parser that reads two chars and calls the |
||||
appropriate method to create the corresponding =Node=. the =serene.simple.Node= |
||||
is an abstract class that has just one important method, =eval=. The whole |
||||
purpose of the reader is to parse the code and create an AST like data structure |
||||
which each node extends the =Node= class (I should've create the interface for |
||||
it but too lazy to change it now). The =eval= method of `ListNode` is a bit |
||||
special. It calls the =eval= method on all the elements on the list |
||||
and then calls the first element as a function and pass the rest of the elements |
||||
as the arguments to that function. First rule of lisp :)) |
||||
|
||||
The =eval= method of =ListNode= contains lots more details regarding to java |
||||
interop as well which I leave it out of this blog post. |
||||
|
||||
* Scope |
||||
Scopes are simply a mapping between symbol names and values. Serene consists of two |
||||
different scopes, both implemented in =serene.simple.IScope= and extend |
||||
=serene.simple.AScope= abstract class that contains the logic for symbol |
||||
lookup and insertion. These two classes are =serene.simple.Scope= which |
||||
is the general scope and it has a parent/child type of relationship with |
||||
other instances of the same class or =serene.simple.RootScope= that is |
||||
the top level scope. Beside that, =RootScope= is pre-populated with all |
||||
the built-in functions and types. |
||||
|
||||
* Special forms |
||||
Serene's [[https://courses.cs.northwestern.edu/325/readings/special-forms.php][special forms]] are pretty limited. All of them all classes which extend |
||||
=serene.simple.SpecialForm= abstract class and inherit from =Node= indirectly. The |
||||
difference between special form evaluation and function evaluation is that in case |
||||
of special forms, Serene does not evaluate the arguments and leaves the evaluation |
||||
to the special form itself. Here is the list of Serene's special forms: |
||||
|
||||
=def=: Creates a binding between a symbol name and the given value: |
||||
|
||||
#+BEGIN_SRC lisp |
||||
(def name "serene") |
||||
#+END_SRC |
||||
|
||||
=fn=: Creates an anonymous function: |
||||
#+BEGIN_SRC lisp |
||||
(def inc (fn (x) (+ 1 x))) |
||||
#+END_SRC |
||||
|
||||
=quote=: Prevents the evaluation of the given argument and return it as it is: |
||||
|
||||
#+BEGIN_SRC lisp |
||||
(quote (1 2 3 4)) ;; => (1 2 3 4) |
||||
#+END_SRC |
||||
|
||||
=if=: Evaluates the body based on the return value of the given predicate. |
||||
|
||||
#+BEGIN_SRC lisp |
||||
(if (= x 1) |
||||
(...) ;; if x is 1 |
||||
(...)) ;; if x is not 1 |
||||
#+END_SRC |
||||
|
||||
=let=: Sets a local scope and runs its body using that scope. |
||||
|
||||
#+BEGIN_SRC lisp |
||||
(let ((x 1) |
||||
(y 2)) |
||||
(println x y)) |
||||
#+END_SRC |
||||
|
||||
=do=: Simply groups several expressions together. |
||||
|
||||
#+BEGIN_SRC lisp |
||||
(do |
||||
(println ...) |
||||
(if ....)) |
||||
#+END_SRC |
||||
|
||||
=cond=: Gets several predicates and only evaluates the body corresponding |
||||
to the first truthy predicate. |
||||
|
||||
#+BEGIN_SRC lisp |
||||
(cond |
||||
((= x 1) (body1...) |
||||
((= x 2) (body2...)) |
||||
(true (else...)))) |
||||
#+END_SRC |
||||
|
||||
* Builtin Function |
||||
All the build in function are created by extending the =serene.simple.builtin.AFn= |
||||
abstract class and follow the same =Node= convention. Here is a list of the most |
||||
important built in functions: |
||||
|
||||
=(println ....)=: Prints all the arguments on the stdout. |
||||
|
||||
=(quit)=: Quits the program. |
||||
|
||||
=(conj coll x...)=: Returns a new list by adding the given arguments. |
||||
|
||||
=(count coll)=: Returns the number of elements in the given COLL. |
||||
|
||||
=(reverse coll)=: Returns a new list which is the reverse of COLL. |
||||
|
||||
=(list 1 2 3..)=: Creates a list from the given arguments. |
||||
|
||||
=(first coll)=: Returns the first element of the given COLL. |
||||
|
||||
=(rest coll)=: Returns all the elements beside the first element of the given COLL. |
||||
|
||||
=(doc fn)=: Returns the documentation for the given symbol if any. |
||||
|
||||
=(reduce f coll initial)=: Reduces the COLL by applying F to its elements with the |
||||
INITIAL as the default value. F takes two arguments 1) the accumulation 2) the element. |
||||
|
||||
=(new Class arg1 arg2...)=: Create a new instance of the given CLASS by passing the given |
||||
arguments to its constructor. |
||||
|
||||
** Example program |
||||
Here is an example program in Serene simple version (=benchmarks/fib.srns= in the repo): |
||||
|
||||
#+BEGIN_SRC scheme |
||||
;; We have a reduce function but just in case... |
||||
(def reduce1 |
||||
(fn (f xs initial-value) |
||||
(cond |
||||
((first xs) (reduce f (rest xs) (f initial-value (first xs)))) |
||||
(true initial-value)))) |
||||
|
||||
;; A simple map function implementation in serene |
||||
(def map |
||||
(fn (f xs) |
||||
(reduce (fn (acc x) (cons acc (f x))) xs (list)))) |
||||
|
||||
(def range-list |
||||
(fn (x y init) |
||||
(if (< y x) |
||||
(do |
||||
(conj (range-list x (+ y 1) init) y)) |
||||
init))) |
||||
|
||||
(def range |
||||
(fn (x) |
||||
(range-list x 0 (list)))) |
||||
|
||||
(def fib |
||||
(fn (n) |
||||
(def fib-iter |
||||
(fn (x y z) |
||||
(if (= x 0) |
||||
z |
||||
(fib-iter (- x 1) |
||||
z |
||||
(+ y z))))) |
||||
(fib-iter n 0 1))) |
||||
|
||||
(def benchmark-fn |
||||
(fn (x) |
||||
(let ((start (now))) |
||||
(println (fib x)) |
||||
(- (now) start)))) |
||||
|
||||
|
||||
(def run-benchmark |
||||
(fn (times) |
||||
(map (fn (x) |
||||
(println "Benchmark: " x) |
||||
(println "Took: " (benchmark-fn 500))) |
||||
(range times)))) |
||||
|
||||
(run-benchmark 20) |
||||
#+END_SRC |
||||
|
||||
|
||||
* What is missing ? |
||||
Since Serene (simple) is an experimental language and I'll abandon it eventually. |
||||
I didn't want to fall into the rabbit hole and just tried to get to the point as soon as possible. |
||||
So I sacrificed lots of details. Here is a list of the most important missing |
||||
details: |
||||
|
||||
- A namespace functionality. Because creating and compiling dynamic classes |
||||
is a massive task and needs tons of work which doesn't make sense for a toy |
||||
project. |
||||
- Unified function interface. |
||||
- Requiring different namespaces. |
||||
- A sophisticated parser. My Reader implementation is really cheap that |
||||
suits a toy project. It might worth investigating on different solutions |
||||
including using a parser generator or ahead of time read implementation. |
||||
- Primitive functions in Serene. I used lots of primitive functions |
||||
from java rather than implementing them inSerene itself, mostly because |
||||
of two reasons. Lack of |
||||
macros and namespaces. |
||||
- Decent [functional] data structures. The only data structure I implemented |
||||
is list. |
||||
- Quality code. The general quality of this implementation is not great, I |
||||
sacrificed quality for time. |
||||
|
||||
* Conclusion |
||||
I'm not going to improve the simple version anymore at this stage. I'm going to run |
||||
some benchmarks and measure different aspects of the current implementation and then |
||||
I'll move to the *Truffle* version and continue experimenting. |
||||
|
||||
Please let me know if you have any comments or questions on this topic. As always |
||||
I'm available throw social media and email. |
@ -0,0 +1,116 @@
@@ -0,0 +1,116 @@
|
||||
#+SETUPFILE: ../../config.org |
||||
#+TAGS: Databases |
||||
#+OPTIONS: toc:2 |
||||
#+CATEGORY: Engineering |
||||
#+DATE: 2019-04-26 |
||||
#+TITLE: Transaction Variants |
||||
|
||||
In this post I want to talk about different variants of [[https://en.wikipedia.org/wiki/Database_transaction][database transactions]], I assume you already |
||||
know about database transactions. So, let's cut to the chase. |
||||
|
||||
* Flat Transaction :Databases: |
||||
Flat transaction are those regular transactions we know about and are the most common transactions |
||||
in the [[https://en.wikipedia.org/wiki/Database#Database_management_system][DBMS]]s Flat transactions are simple but they can not address two problems: |
||||
|
||||
- Multi stage transactions |
||||
For example, Let's say we want to book flight from City C1 to C2. Since there is no direct fly we |
||||
have to book 4 flights from, =C1 -> CA -> CB -> C2=. The process of booking each of these flights |
||||
is a transaction by itself and the whole process is a transaction too. |
||||
|
||||
- Bulk updates |
||||
Let's say we want to update billion tuples. What if the very last tuple fails to update and cause |
||||
the transaction to abort. Then we need to revert the changes made by the transaction and revert |
||||
a billion tuples which obviously is a huge task. |
||||
|
||||
* Transaction Savepoints |
||||
These transactions are similar to flat transaction with addition of one extra thing which is |
||||
save points. So any where in there transaction users case ask for a save point and again they can |
||||
rollback to a save point or rollback the entire transaction. |
||||
|
||||
#+BEGIN_SRC sql |
||||
BEGIN |
||||
READ(A) |
||||
WRITE(A) |
||||
SAVEPOINT 1 |
||||
WRITE(B) |
||||
SAVEPOINT 2 |
||||
ROLLBACK TO 1 |
||||
COMMIT |
||||
#+END_SRC |
||||
|
||||
#+BEGIN_QUOTE |
||||
Note: *These transactions only solve the multi stage transaction problem.* |
||||
#+END_QUOTE |
||||
|
||||
* Nested transactions |
||||
Nested transactions are similar to save points transactions, but instead save points these |
||||
transactions break down to smaller flat transactions. Each transaction commits separately from |
||||
other transactions. But result of the parent transaction rule them all, so if the parent |
||||
transaction fails all the nested transactions have to rollback. |
||||
|
||||
#+BEGIN_SRC sql |
||||
BEGIN |
||||
BEGIN T1 |
||||
... |
||||
COMMIT T1 |
||||
BEGIN T2 |
||||
... |
||||
COMMIT T2 |
||||
COMMIT |
||||
#+END_SRC |
||||
|
||||
#+BEGIN_QUOTE |
||||
Note: *These transactions only solve the multi stage transaction problem.* |
||||
#+END_QUOTE |
||||
|
||||
* Chained transactions |
||||
In these kind of transactions, smaller flat transaction can be applied in a chain in the way that |
||||
the result of each of them is not visible to the outside world until the end of the chain. |
||||
|
||||
In theory chained transactions should be applied in sequence but in practice in some cases we can |
||||
interleave their operations. Also Between T1 and T2 of a chained transaction, No other thread of |
||||
code shouldn't be able to make changes to those resources which T2 will operates on. |
||||
|
||||
If any transaction in the chain fails, it has nothing to do with the previous transactions in the |
||||
chain. For example: |
||||
|
||||
#+BEGIN_SRC |
||||
T1 -> T2 -> T3 |
||||
S -> S -> F |
||||
#+END_SRC |
||||
|
||||
In the chained transaction above only T3 failed and T1 and T2 are successfully committed to storage. |
||||
We don't have to roll them back. |
||||
|
||||
While chained transactions can break big transactions into smaller pieces for better parallelism but |
||||
*they only solve the multi stage transaction problem* Not the bulk update problem. |
||||
|
||||
* Compensating transactions |
||||
This type of transactions are special transactions which are designed to semantically reverse the |
||||
effect of another transaction which already committed. |
||||
|
||||
One important thing to remember about compensating transactions is that they know how to revert |
||||
the /logical/ effect of other transactions /NOT the physical/ effect. For example, If a transaction |
||||
increase a counter by one. The physical revert would be to changes the binary data that inserted |
||||
for that counter to what it was before the transaction, but the logical revert would be to |
||||
decrease the counter by one when needed. |
||||
|
||||
So basically these kind of transactions know about how to put the database in correct state before |
||||
the other transaction. |
||||
|
||||
* Saga Transactions |
||||
A saga transaction is a sequence of chained transactions =T1 - Tn= and compensating transaction |
||||
=C1 - C(n-1)= where the following guaranteed: |
||||
|
||||
- The transactions will commit in the order =T1...Tj=, =Cj...C1= (where =j < n=). |
||||
|
||||
So basically this means that a saga transaction is a seq of chained transactions which applies the |
||||
smaller transactions in order with their corresponding compensating transactions. |
||||
|
||||
In a chained transaction when ever transaction Tn aborts, the transactions before Tn stay committed, |
||||
but in saga transactions they will be rollback using compensating transactions that know how to |
||||
roll them back *logically*. |
||||
|
||||
So Saga transactions can be fix both multi-staging and bulk update problems. But the issue here is |
||||
that the compensating transactions are something that requires application level understanding of |
||||
the use case so most of the time they are implemented in the application frameworks instead of DBMSs. |
@ -0,0 +1,63 @@
@@ -0,0 +1,63 @@
|
||||
#+SETUPFILE: ../config.org |
||||
#+OPTIONS: toc:2 |
||||
#+EXPORT_FILE_NAME: faq.html |
||||
#+DATE: 2021-02-11 |
||||
#+TITLE: FAQ |
||||
#+PAGE: true |
||||
|
||||
Here is a list of questions people usually ask me. |
||||
|
||||
* General |
||||
** Why your website is like this ? |
||||
I value simplicity. The purpose of this website is to share my thoughts and research with others and not |
||||
to impress them with a shiny and heavy good looking website. |
||||
** Where can I find your CV ? |
||||
You can find my CV [[https://dl.lxsameer.com/CV/SameerRahmani.pdf][Here]], I try to keep it up to date ( Not always though :D ). |
||||
** How do you keep up with your work and research ? |
||||
I use [[https://orgmode.org/][org-mode]] to manage my life mostly, But beside that I have priorities in my life and when things come to |
||||
my way I process them based on my priorities. For example I love to play the Guitar but as long as I have time for |
||||
it and it doesn't interfier with my priorities. |
||||
|
||||
I usually partition my days into chunks of two days time and dedicate the main goal of each chunks to a cetain |
||||
project that I have to work on. It makes the context switch less painfull. BTW I use [[https://orgmode.org/][org-mode]] to take notes and |
||||
track my progress in my research and project and also when I read books. |
||||
** What do you do for fun ? |
||||
Usually video games, Anime music and sports. |
||||
|
||||
* Engineering |
||||
|
||||
** What GNU/Linux distro do you use ? |
||||
I used to use Debian unstable for 14 years. But I moved to Gentoo just because I care about my security and I |
||||
hate Systemd. |
||||
|
||||
** What editor/IDE do you use ? |
||||
I use my own editor, [[https://devheroes.codes/FG42/FG42][FG42]] which is based on on the mighty [[https://www.gnu.org/software/emacs/][GNU/Emacs]] and publicly available under GPLv2 license. |
||||
|
||||
** What desktop environment/window manager do you use? |
||||
Again, I use editor [[https://devheroes.codes/FG42/FG42][FG42]] for that purpose as well. I boot directly into my editor and manage everything from there. |
||||
|
||||
** What's your favorite programming language ? |
||||
I don't have any bias toward any specific language, But I've found the Lisp family to be really good. |
||||
|
||||
** Have you studied Software engineering in college ? |
||||
No |
||||
|
||||
** How can I improve as an engineer ? |
||||
I'm not an advisor and frankly I'm somewhere in my own journey and far from the end. But I can give one piece of |
||||
advise. Whatever you do, try to be scientificly literate. |
||||
|
||||
** Where is your hang out place up on the internet ? |
||||
I'm an [[https://en.wikipedia.org/wiki/Internet_Relay_Chat][IRC]] user and I usually hang out in the irc://freenode.net/#5hit channel. |
||||
|
||||
|
||||
* Science |
||||
** What do you do in Science ? |
||||
Physics |
||||
** What is your field of study ? |
||||
Quantum field theory |
||||
** I though you're an engineer, What's your deal? |
||||
Well, I love physics. Simply I want to know more. The universe baffles me. |
||||
** What do you suggest to those who want to start in science ? |
||||
I'm not an advisor and even not a top notch scientist, So I can't give you any good advise, But I share my own |
||||
experience with you. The most important thing is to keep an open mind, always think like us humans are not |
||||
the center of universe and finally learn algebra. |
@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
|
||||
#+SETUPFILE: ../config.org |
||||
#+OPTIONS: toc:nil |
||||
#+EXPORT_FILE_NAME: gpg.html |
||||
#+DATE: 2021-02-11 |
||||
#+TITLE: GPG Information |
||||
#+PAGE: true |
||||
|
||||
I use GPG heavily. There might be some expired or fake keys of mine on key servers but here is [[https://dl.lxsameer.com/lxsameer.pub.gpg][My Current Key:]] |
||||
|
||||
#+BEGIN_SRC |
||||
pub rsa4096 2020-01-03 [SC] [expires: 2022-01-02] |
||||
75B126C50711BF7F4816D451B775967058BB835C |
||||
uid [ unknown] Sameer Rahmani (For more info refer to: lxsameer.com) <lxsameer@gnu.org> |
||||
sub rsa4096 2020-01-03 [E] [expires: 2022-01-02] |
||||
#+END_SRC |
@ -1,17 +1,23 @@
@@ -1,17 +1,23 @@
|
||||
#+SETUPFILE: ../config.org |
||||
#+OPTIONS: toc:nil |
||||
#+EXPORT_FILE_NAME: index.html |
||||
#+TAGS: ABD(a) BD(c) ASDK(k) |
||||
#+DATE: 2021-02-11 |
||||
#+CATEGORY: blah |
||||
#+TITLE: blah |
||||
#+TITLE: The little nest of mine |
||||
#+PAGE: true |
||||
|
||||
* Welcome :ABD:BD: |
||||
:PROPERTIES: |
||||
:BLAH: s |
||||
:END: |
||||
blha bbasd asds asd a asd =asdasd= somer `asdasd` sadasd *asdasD* asdasd /asdasd/ asdasd. |
||||
ads ad asd asd as dasd asd |
||||
- [[http://localhost:3003/essays/serene-blah.html][Serene blah]] |
||||
- [[http://localhost:3003/essays/javascript-blah.html][javascript blah]] |
||||
Welcome to my little piece of the world. I'm a software engineer by day and an armature scientist by night |
||||
who lives by his [[./coh.org][Code of Honor]]. I write about my thoughts and researches. The views expressed here are my |
||||
personal views and not of those organizations that I might be associated with. Most of my time goes to developing |
||||
free software either by contributing to other projects or by working on my own stuff which you can find |
||||
them on [[https://devheroes.codes][devheroes]]. |
||||
|
||||
If you're interested in my work and research feel free to contact me to have a friendly chat Or share you thoughts |
||||
with me via email (check my [[./gpg.org][GPG]] info page). If you have a question for me, you might be able to find the answer to it |
||||
in the [[./faq.org][FAQs]] page. |
||||
|
||||
* Recent updates: |
||||
- [[http://localhost:3003/essays/serene-simple-version.html][Serene (simple versoin)]] |
||||
- [[http://localhost:3003/essays/choosing-the-target-platform.html][Choosing the target platform]] |
||||
- [[http://localhost:3003/essays/rational-and-a-name.html][Rational and a name]] |
||||
- [[http://localhost:3003/essays/my-new-programming-language.html][My new programming language]] |
||||
- [[http://localhost:3003/essays/transaction-variants.html][Transaction Variants]] |
||||
|
@ -1,12 +1,22 @@
@@ -1,12 +1,22 @@
|
||||
#+TITLE: lxsameer's nest |
||||
|
||||
- [[file:essays/serene-simple-version.org][Serene (simple versoin)]] |
||||
- [[file:essays/choosing-the-target-platform.org][Choosing the target platform]] |
||||
- [[file:essays/rational-and-a-name.org][Rational and a name]] |
||||
- [[file:tags/Serene.org][Serene]] |
||||
- [[file:tags/Languages.org][Languages]] |
||||
- [[file:essays/my-new-programming-language.org][My new programming language]] |
||||
- [[file:categories/Engineering.org][Engineering]] |
||||
- [[file:tags/Databases.org][Databases]] |
||||
- [[file:essays/transaction-variants.org][Transaction Variants]] |
||||
- [[file:faq.org][FAQ]] |
||||
- [[file:gpg.org][Code of Honor]] |
||||
- [[file:coh.org][Code of Honor]] |
||||
- [[file:categories/nil.org][<<<title>>>]] |
||||
- [[file:categories/SLA.org][SLA]] |
||||
- [[file:tags/ASDK.org][ASDK]] |
||||
- [[file:tags/BD.org][BD]] |
||||
- [[file:tags/ABD.org][ABD]] |
||||
- [[file:essays/javascript-blah.org][javascript blah]] |
||||
- [[file:tags/index.org][index]] |
||||
- [[file:categories/index.org][index]] |
||||
- [[file:essays/serene-blah.org][Serene blah]] |
||||
- [[file:index.org][blah]] |
@ -1,16 +1,19 @@
@@ -1,16 +1,19 @@
|
||||
#+SETUPFILE: ../config.org |
||||
#+OPTIONS: toc:nil |
||||
#+EXPORT_FILE_NAME: index.html |
||||
#+TAGS: ABD(a) BD(c) ASDK(k) |
||||
#+DATE: 2021-02-11 |
||||
#+CATEGORY: blah |
||||
#+TITLE: blah |
||||
#+TITLE: The little nest of mine |
||||
#+PAGE: true |
||||
|
||||
* Welcome :ABD:BD: |
||||
:PROPERTIES: |
||||
:BLAH: s |
||||
:END: |
||||
blha bbasd asds asd a asd =asdasd= somer `asdasd` sadasd *asdasD* asdasd /asdasd/ asdasd. |
||||
ads ad asd asd as dasd asd |
||||
Welcome to my little piece of the world. I'm a software engineer by day and an armature scientist by night |
||||
who lives by his [[./coh.org][Code of Honor]]. I write about my thoughts and researches. The views expressed here are my |
||||
personal views and not of those organizations that I might be associated with. Most of my time goes to developing |
||||
free software either by contributing to other projects or by working on my own stuff which you can find |
||||
them on [[https://devheroes.codes][devheroes]]. |
||||
|
||||
If you're interested in my work and research feel free to contact me to have a friendly chat Or share you thoughts |
||||
with me via email (check my [[./gpg.org][GPG]] info page). If you have a question for me, you might be able to find the answer to it |
||||
in the [[./faq.org][FAQs]] page. |
||||
|
||||
* Recent updates: |
||||
<<<links>>> |
||||
|
Loading…
Reference in new issue