I'll keep publishing on Medium though :)
2,632 karma · joined June 19, 2019
- https://link-society.com
- https://krouter.cloud
- https://flowg.cloud
- https://linkdd.github.io
- https://david-delassus.medium.comI'll keep publishing on Medium though :)
I use Medium because I can monetize easily the articles I write, though it's not much (currently around 10-15$/month).
I'm not doing it for the money, but it's still nice.
Though, I should try to use `quote_spanned!` instead of `quote!`, but I'm kind of a newbie with proc-macros.
As for your take on the visitor pattern. In my previous code, I had the `visit_expr`, `visit_stmt`, etc... And it's a lot of boilerplate, I wanted to reduce the amount of copy/paste and facilitate refactoring, hence my DSL.
I do have multiple phases during compilation where I walk the AST and the "only" difference is the return value of those `visit_*` functions and their body. Here, I just have to use a `model!{}` macro for each "phase", which is less boilerplate code.
In the end, it's just static dispatch through the use of a trait that I called "Visitable".
Though, this is fine by me. I don't mind people bypassing the paywall, but it's a source of (rather small) income for me :)
Please don't assume things when you don't know the person.
I don't even understand how that's possible. Every time I tried to post a link that was already posted (by me or someone else), it redirected me to the original post (and upvoted it if I wasn't the author).
After observing that behavior 2 or 3 times, I assumed HN had some kind of deduplication system.
Take a look at how django does it. `django-admin startproject` creates:
- settings.py
- wsgi.py
- asgi.py
- urls.py
Then you just need to add your models and views. There is the `startapp` command, but you don't really need it.Even with the codegen tools, you still need to learn what a plug router is, what a phoenix controller/view is, what an ecto repository is, etc...
The codegen tools just overwhelm you with irrelevant code (i'm making an API, why the hell do you generate static files? and why do i need to edit so many files to take it out?)
Another point I don't like about Ecto is that you can't easily switch the database backend depending on the environment.
With django, I can have an sqlite3 db for the dev environment, and a pgsql one for the production. I don't want to run a docker image for pgsql on my machine. I just need to read the DATABASE_URL environment variable, and the ORM takes care of the compatibility. If later on I need some specific pgsql feature, I will have to run the docker image on my computer anyway, but until them please keep the complexity out of my way.
I hate code generators with a passion. They obfuscate the inner workings of the library. Have you tried to find a tutorial which do not use the Mix task "phx.new"? (an up-to-date one).
For example, if I need an API, I will pass `--no-assets --no-html --no-live --no-mailer --no-dashboard` and the generator still creates a static files folder and static files related code.
If I want the strict minimum, I have to take some time to remove the un-necessary code.
Don't misinterpret me, I think Phoenix is a great framework, just like Python/Django, and Ruby on Rails are.
But sometimes, I just want Flask/FastAPI. You can say "just use plug_cowboy", but IMHO, "plug_cowboy" is more like "express.js", not really a FastAPI-like framework.
Also, absinthe is the only GraphQL implementation that natively supports subscriptions (via phoenix+phoenix_pubsub). In Django (graphene), you need so much more boilerplate with django-channels and yet another library. In JS land, Apollo Server also do not support it natively, you need graphql-ws and a pubsub implementation as well. And most of those pubsub implementations will need a Redis (or whatever) to distribute the queue between multiple web workers, while Erlang/Elixir have been designed for this (which actually simplify your infrastructure).
In the end, I often go with Django Async (via ASGI) with Server-Sent-Event (via an async StreamingHTTPResponse) to avoid the complexity of GraphQL subscriptions. But even that, I need a pubsub via Redis to make my multiple workers aware of each other. While with Elixir, a simple libcluster dependency, and a `receive` in the endpoint handler would be enough :)
I love the OTP framework. So much that I suffer from "erlang envy" in every other language I'm using.
Which is why I made this a couple years ago: https://linkdd.github.io/triotp/ (not benchmarked, not tested in production, only an experiment)
With OTP, you have what we call a "supervision tree". Your software is divided into components (erlang processes) and organized in a tree where the leafs are your components, and the nodes are the "supervisors" which will catch the error, and restart/retry the operation.
What's the result? Imagine you have a component that is polling data from the database and then sending a message to another component based on that data. For a split second:
- there is a network error
- you lose the connection to the database
- the component crashes
- its supervisor notice the crash, restart the process
If the network error was temporary, everything goes back to normal, and your system did not stop for a temporary network error. And your component does not need to have "retry code" (it is delegated to the parent supervisor).If the network error was not temporary, the parent supervisor will notice that the component crashes far too often, and will decide to crash as a result. Letting the parent supervisor of the supervisor deal with the problem.
In Erlang/Elixir, processes can "monitor" other processes. They will receive a message when the target processes dies (normally, or abnormally). This allows you to delegate the error handling so yes, you write less defensive code, because that code is located in a dedicated component that will be notified automatically.
To be fair, it is very similar to a `Result<T, E>` type, which is why I made this library a while ago: https://github.com/linkdd/rustic_result
Consider:
for item in iterable.iter() {
item.foo()?;
}
You will exit the loop as soon as there is an error. But with .map().collect(), you iterate over the whole list, and then exit if one of the items yield an error.EDIT: Thank you for your answers, it seems I was wrong and it does short-cricuit!
Maybe as open-source, the product could have some success, but I don't know how to "sell" open-source or make a revenue from it.
Beware of survivor bias, for every successful business, there might be a 1000 failed ones.
Not with the Traffic President mod which allows you to disable cars despawning and adds to the challenge of building a good transit system.
The generated Rust code should be valid Rust code that compiles with no error. I only have 2 exceptions to that rule:
---
Function calls:
I haven't found a way to verify that the identifier used resolve to a function with the correct signature (it would need some sort of partial evaluation and static type checking I don't have).
Rust FFI:
Letlang will have the ability to call native Rust functions (allowing me to rely on the Rust ecosystem to implement the stdlib), but it expects functions to have a specific signature.
---
Those are the 2 cases where `cargo` can throw an error. So I (plan to) include source mapping comments in the generated Rust code. After getting the output of `cargo` as JSON, I can identify the error type and location, then using the source mapping info, I can pinpoint what AST node generated the wrong code.
On a semi-unrelated note: Every Letlang module is a single Rust crate. The compiler generates a cargo workspace, and a cargo project in that workspace for every module. It makes dependency management (and my life) easier.
It's funny how people are intrigued by my choice of targeting Rust, while other compilers targets C, Javascript, or other higher-level languages :P
But I guess it's because I use the term "compiler" and not "transpiler". Hint: I hate the word "transpiler", which is just a synonym to "compiler" IMHO.
I also get the borrow checker, move semantics, etc... that I would have to implement myself if I were to target LLVM IR.
That question has been asked so many times, I should probably start a FAQ :P
You may be interested by https://github.com/brundonsmith/rust_lisp though
The crates listed in that article are the ones I use for my compiler: https://letlang.dev
Lisp was only chosen as a way to demonstrate the power of those crates and Rust features. A kind of way of justifying my choices for Letlang.
It's not "you should do it like this" but "you can do it like this".
How slow is JavaScript really? Javascript vs C++ [Data Structures & Optimization] https://www.youtube.com/watch?v=WLwTlC1R2sY
---
"simpler than Python", what does "simple" mean?
Simple design? Python's design is very complex (take a look at "Crimes with Python's pattern matching" < https://www.hillelwayne.com/post/python-abc/ > for example), on the other hand, assembly languages, or Lisp, or Forth, have a very simple design.
Simple as in "easy to use"? Rust is easy, write code, fix what the compiler tells you you did wrong. Joke aside, Go is quite easy to use and while I personally don't like this language, I get why it replaced Python in a lot of use cases.
Also, once you get used to the OTP framework, Erlang/Elixir/Gleam/any beam language are quite easy to use and have less footguns than Python.
---
"safer than Rust" is too vague. Is it memory safety? type safety? thread safety? cosmic ray safety? A mix of all of that?
Let's guess you meant "memory safety". All languages with a Garbage Collector are "memory safe".
---
On a semi-unrelated note, I've been working on https://letlang.dev
It's a language inspired by Erlang/Elixir (same concurrency model) that compiles to Rust code (the runtime use tokio). It is immutable, have no Garbage Collector thanks to Rust semantics, and dynamically typed.
I haven't run any benchmark (it's not even finished, I've been working on the specification before continuing the implementation), but I guess it could be slower than a rock.
---
For some recommendations, have you looked at Zig? Nim? Hare?
https://ziglang.org/ https://nim-lang.org/ https://harelang.org/
But let's avoid flamewars irrelevant to the discussion.
Of course they get to complain. Complaining and doing nothing to change the situation is what's problematic.
Doing work without reward, even if you did not expect any in the first place, can be soul-sucking, and it is totally normal to complain about it.
You don't do FOSS for the recognition/glory, but complaining about the hatred, hypocrisy, and complete lack of respect of the industry towards FOSS is normal.
The backlash he got after he "dared" ask for help was completely uncalled for. I'd even say this, it's the user who does not get to complain, let's remind everyone of what most FOSS licenses include:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
You get a message during `npm install` from the dev asking for funding and a job? Tough shit, you don't get to complain because you're not paying him.NB: It's actually already possible, since the type system is already implemented, it's just that many features of the language are not yet available.