HN user

Martinsos

43 karma

Creator of wasp-lang.dev - DSL for building web apps with less code.

Enthusiastic about functional programming, web development, emacs, open source, computer science, startups.

Posts10
Comments54
View on HN

In a meta way, this blog post itself is example of that: sounds obvious that blogging can just be stating the obvious, but I still enjoyed reading it, and it served as a good reminder. I guess part of what makes it feel good is confirmation bias, but it can also be nice to read something that confirms your beliefs especially if the other side(s) are very loud online. I would also add that it is just expressing oneself, and therefore it is ok for it to be non-original thought: you are not publishing a scientific article, you are expressing your opinion, adding to the general discourse online, making your voice be heard, and also just capturing your thoughts into something concrete.

[dead] 1 month ago

Hey all, Martin here, co-founder of Wasp, a batteries-included full-stack JS/TS framework!

A unique thing about Wasp is that we built it around the central "specification" language (DSL) that lets you express the high-level of your app, for which we wrote our own compiler and language server in Haskell. You write 90% of logic in popular web tech (React, Node.js, Prisma), but specify the "full-stack" part of it in the DSL.

While we loved the idea of a custom language for optimal ergonomics and declarative nature of it, and had great time building it, we realized with time how hard it is to develop all the tooling around it while also building the framework, and also started getting constrained by its simplicity for some of the new ideas we have developed through time, of where we wanted to take Wasp.

So, 5 years since the start (wow feels like 2y), we made a big change: we replaced our custom DSL with the eDSL (embedded DSL, a library) in TypeScript!

So Wasp still has its spec(ification), stack-agnostic and standalone (runtime), but now you write it in TypeScript, with all the Turing-complete and side-effect goodiness, which allows things like splitting spec into multiple files, writing helpers, checking env vars, implementing your own file-based routing if you want, ... . And, also enables a lot of exciting stuff we plan to build upon it, like extensible spec, and full-stack modules.

For the full-story, please check out the attached blog post I wrote, and I am happy to answer any questions here!

I started working on this a couple of months ago in the spare time and have finally gotten it to the state where I am ready to pronounce it done! It was great fun and I love the result, I hope you find it interesting. Would also love any feedback on what I could do better. Thanks!

Martin from Wasp here -> you are right that we kind of went overly into DSL, we are actually switching it to TS at the moment (experimental version already out for some time but now making it the main way to use Wasp), but not because of the AI, instead because we found it was too hard to maintain and develop. We thought custom ergonomics of it will be worth it, but turned out we didn't get much on that side, while we lost a lot by not using existing ecosystem of well known language.

Btw, AI actually works great for it. I am sure part is that the Wasp's DSL exists for some time now, but it actually worked well for the very start, because the DSL was quite simple (similar to JSON) and AI knows how to generalize very well.

So I wouldn't discourage people from writing DSLs because of AI -> AI can understand them very well -> but for the reasons of missing out on all of the benefits of using a strong host language and doing it as an embedded DSL in it. If you are doing your own, completely standalone DSL, you will need to implement a compiler, editor extensions, LSP, maybe module system if you need it, maybe package system/manager if you need it, ... . Although when I think about it, that is also easier now with AI, than it was before! Hm yeah actually maybe custom DSLs are a good idea these days, with AI doing most of the job for you. I still wouldn't go back to custom DSL for Wasp however because biggest thing for us is probably familiarity -> custom DSL just scares people off.

Rust vs. Haskell 3 years ago

I can confirm this! We had no problem getting a very decent number of applications for Haskell position, both from very experienced Haskell devs, and from junior Haskell devs, all very motivated.

As for getting somebody on-board -> we hired a couple senior / intermediate devs that had no or introductory knowledge of Haskell, and all of them so far got up to speed in a month or so, while not learning exclusively Haskell but also the rest of the codebase at the same time, so normal learning process in the new company. So I wouldn't say at all that learning Haskell for them was an issue, but I am certain that big factor here was that they are generally experienced in other languages. That said, we do keep our codebase pretty tidy and simple (no super crazy Haskell features).

Rust vs. Haskell 3 years ago

I know it is common to think that Haskell is used only in academia and side/weird-projects, but there is a decent amount of companies using Haskell - e.g. we use Haskell in production for developing a DSL / web framework for building web apps (https://github.com/wasp-lang/wasp)!

I participated teaching students Haskell on my alma mater this year and "what can Haskell be used for" was a common question, with genuine expectation that the answer will be it is limited to only specific use cases. I would answer that it can be used anywhere where languages like Java, C#, Go, and similar can be used -> it is a general programming language that uses garbage collector! And while somewhat harder to learn due to abstractions that we are all not used to, it is a delight to express business logic in it once you get to know it well.

The biggest factor for deciding if Haskell is a good fit for the problem is probably ecosystem support -> are there enough libraries and tools to support efficient development in a specific problem domain. In our case, we are building a compiler/transpiler, and Haskell is well-known for great support in that area, so it was a no-brainer. We were actually also considering Rust, but we just had no need for that level of memory control and rather decided to go with language where we don't have to think about that (Haskell).

Our compiler is split into Analyzer which produces AppSpec, which is the central intermediate representation, and then Generator, that based on AppSpec produces the react / node web app.

So yes, you could use just Analyzer to produce AppSpec, and then write your own generator that will based on that AppSpec generate your own code! AppSpec currently exists only as Haskell in-memory data structure, but it could be relatively easy serialized via JSON and then imported in some other language.

You can see more about this in this README, there is a nice diagram showing parts of compiler (Analyzer, AppSpec, Generator): https://github.com/wasp-lang/wasp/tree/main/waspc#readme .

The reason why we already ensured this split was because the plan is in the future to have multiple different Generators, and also to allow users to write either their whole Generators or plugins that modify AST (AppSpec). We haven't explored this further yet, and probably won't push much in this direction until we get to Wasp 1.0, but we certainly want to work on it more in the future!

I think there will always be a trade-off between "one way to do things" (opinionated approach) vs flexibility - the thing is, we need both, depending on the use case and the person using the language.

But I do think we are falling behind with opinionated approaches. I am guessing that is because experts are usually the most prominent in communities, they are directing the ecosystem, and they prefer flexibility and power vs opinionated approach.

Ruby on Rails was an opinionated approach made by non-expert that grew into something really big because of that. I am also trying to contribute in that direction with highly opinionated solution for web app development (https://wasp-lang.dev).

I think that depends on how complex the config can get, and how it is used.

For example package.json in NPM packages -> that feels like a good fit for JSON (although it would be even better if JSON had comments). On the other hand, terraform, or build languages like Make or Meson -> they are complex enough that it probably makes sense to have a standalone DSL.

I was facing the same decision recently on my project while designing a declarative DSL for web app development (kind of like web framework). From simplest to most complex option:

- should I just let them define it all in JSON? There would be a lot of repetition at some point and it would become impractical, but it could be ok for the start.

- should I just implement JS library, that devs can use in JS to construct a config object that is then exported to JSON? That would be embedded DSL. Sounds flexible and easy to do, but it is also overly expressive and not "cool" (ok this is debatable).

- should I use something like Dhall? It is declarative and simple.

- should I come up with my own declarative, configuration-like DSL? It would probably end up similar to Dhall, but this means I can do whatever I want - I can make it as ergonomic and custom as I want to (which I guess is both good and bad :D!). It might also allow for nicer interop with Javascript and other languages.

In this case, we went for the last option, mostly because we felt the most important thing is ergonomics and interop, but well, I am still curious how would other directions play out. Plus at the end we didn't yet get to the point where language is more expressive than JSON (code example: https://github.com/wasp-lang/wasp/blob/master/examples/tutor...).

Maybe I am just missing a better design process, but it seems to me that with a language idea it is hard to say if it is good or not until you try using it.

Didn't Meteor offer a conceptually similar solution, in the sense that data is constantly synced, via sockets? But it didn't require us to think about sockets so much, which is great.

There is a lot of talk at the start of the article how web development should be easier, how it should be less about the underlying architecture and that we shouldn't spend time on implementation details like state that SPA suddenly has to manage. On the other hand, I felt the rest of the article seemed pretty implementation focused, talking about details of how sockets could be used - I do have decent web dev experience but grasping the practicality of the concept based on what was described was beyond me.

That said, I love the first part that is talking about simplifying the web app development, to make it similarly simple as it was 10 years ago, but I can hardly imagine that solution to that is replacing one piece of underlying technology with another piece. I imagine that instead, we need a higher level of abstraction that will hide all the implementation details that we don't care about and capture that what is common through time, so we don't have to care if we are using websockets or http or smth else underneath - instead we say how we want data to behave, and it gets done. Dealing with HTTP or sockets should be reserved for advanced use cases, not typical web apps. I am working on a concept of this, open-source web dev DSL, still in alpha though: https://wasp-lang.dev , and while I can't be sure that it is the exact solution, I imagine something similar in the future, a DSL. At the end, frameworks are a step in that direction (embedded DSLs), it is just that they are not standalone languages and are therefore again pretty coupled with the language and architecture they are modeling.

I also believe in this as a promising direction! Specifically I have been trying to apply it to full-stack web app development for some time now by building a DSL (https://github.com/wasp-lang/wasp).

Why do you think DSLs are not the full solution? My main thought was that having DSLs for specific areas of programming domains is the way to go. But they should have interop with Turing complete languages to implement specific sub-parts. That way DSLs can capture a lot of knowledge and best practices that we learned through years in that domain while still facilitating innovation being flexible. Such language would be very limited, more like "smart configuration languages". Some of them I have seen around are Terraform, Nextflow, Meson-build.

APL still sounds too general and self-sufficient to me to be a solution to this (but I don't know much about it so maybe I am wrong, I just checked it out on the wikipedia).

Thanks for sharing this! Reading this, there seem to be a lot of opposing opinions, but it seems to come down to curl being as safe as other methods, with the main problem being trusting the server from where the script is downloaded.

In our case, server is github so that is allright, but script is on master, so that is tricky because we might update it at any moment, so you should check it once it is downloaded. That is a concern for sure, and there are ways to remove that concern.

One thing that goes to our benefit though is that we are not requiring `sudo`.

Thanks for pointing all this out, we will certainly be looking into better ways to install Wasp in the future!

Hey, the thing is Wasp is not written in JS, so while we could use NPM, we would be forcing it in a way - at the end it would come down to shipping bash script as part of our npm package and just running that bash script from package.json commands/scripts. Second thing is, that while Wasp is dependent on node right now, which then serves as an argument to use npm as installation mechanism, we plan to remove dependency on node by dockerizing everything in the future (it is step toward detaching Wasp from specific implementations), so we lose that argument.

That said, we have not completely ditched idea of using NPM as installation mechanism, but we haven't convinced ourselves yet - maybe we should give it more thought though. What do you think is the benefit of using NPM vs our install script? Why would you trust NPM package more? Both NPM package and our install script are open source and you can see the code, both have equal execution privileges on your machine - why would you trust NPM package more?

Haha, ok this is certainly an unexpected question :D! No, we are not identical twins, we are fraternal, meaning we have different DNA and look apart. So we were never able to pull of that kind of stunts :D! But, even if we were, we couldn't really pull it off since we work together since ever so people would realize one of us is missing :D.

The usual situations we get into is that people at the beginning can't remember which one has which name. Luckily, often it is also not important hehe :D.

The worst situation we had so far was probably when we were applying for our first year of uni - our applications looked so similar that they thought it was the same application sent twice by mistake, so they took into account only one of the applications! Luckily we realized on time that something is wrong and were able to appeal :D.

Actually we have an example, you can see it lower on the landing page! Here is link to deployed real world app: https://wasp-rwa.netlify.app/ , while source code is here: https://github.com/wasp-lang/wasp/tree/master/examples/realw... .

Right now we have only client server routing really, while server side routing is abstracted away and replaced with RPC - but we will make sure soon to provide better customization support there too.

I love the idea you are describing, it is very much our initial (and maybe final) vision that we had when we started working on Wasp! But, our vision of the best approach for the current moment is somewhat different, let me explain below.

Web apps are complex beasts, and we believe there is a reason for multiple DSLs being used today (HTML, CSS, React/Vue/... as embedded DSL, Prisma as schema DSL + db access embedded DSL, Terraform, ...): it is easier to solve the problem by splitting it into subproblems and then solving each one of them with specialized language/DSL, than figuring out one DSL that works for everything. Even if we found one DSL that works for everything, it would at the end likely be a combination of multiple DSLs unified in one language.

So, if we decided to solve it all at once with Wasp, we would be solving N problems at the same time, and would need to beat whatever is the current best solution in most of the areas to be compelling enough to be used. At the same time, we would be competing with the whole web dev ecosystem that has shown in the recent years to innovate and progress very quickly.

That is why we believe more in the current approach we are having: by utilizing existing solutions for sub-problems, solutions that work relatively well and are on the edge of what we know is the best way of solving those sub-problems, and building our DSL in the space in-between, where the most of the friction is happening, we are bringing value where it matters the most while also riding the web dev ecosystem wave, instead of fighting it directly.

This doesn't mean that we can't expand Wasp with additional sub-DSLs if we figure out at some moment that, e.g. we have a better abstraction than React that would fit really well in Wasp - we can still do all that, but we can do it step by step, while still progressing in the meantime.

Also, this might not be very clear in the docs, but the idea of Wasp is not be tied just to React - idea is to also support Vue, Svelte, and whatever is coming next, when defining components. Also, idea is to support not just NodeJS, but also Go, Python and similar on the backend (maybe you could even combine them).

Regarding learning -> is it better for a web dev to learn complicated Wasp language and how to write view logic in it and how to write db access logic in it, or to learn a simple, smaller Wasp language and how to write view/db logic in React/Prisma? I think the second, since complexity is going to be similar in both cases due to Wasp becoming more complex in the first case, to be able to describe both view and db, while in the second case you are learning technologies that you can also use outside of Wasp later or you maybe even already know them.

Just to make it clear, what you are suggesting, to have 1 DSL for all -> I love it, it is our initial vision, and one I would very much like to be possible. We actually started that way with our prototype, but soon concluded that we need to divide and conquer (well, more like delegate in this case) if we are going to win this battle. We started with having elements in DSL like button, forms, but we left those to React for now -> however, we will probably try getting back to that in the future, if we manage to establish some footing first.

To answer the specific questions:

1. Yes! We are planning to relatively soon allow mixing of JS and Wasp logic, mostly for the purpose of making the code more readable and easier to organize. Describing lower-level details of the web app with Wasp, e.g. page content, elements, text -> that we would also like to do, but probably further in the future.

2. We initially started with our own syntax for describing schemas -> then we moved to Prisma since they have a DSL that works well. Right now we are working on our own Prisma DSL parser (almost done!), so that we can reason about those schemas independently of Prisma. Next step would be having our own syntax, which would make us independent of Prisma, as you said -> and yes, I believe we will be getting back to that relatively soon! Mostly with purpose of making it as simple and easy as possible. But now we will be armed with the knowledge of what worked well regarding Prisma DSL (PSL), what didn't, and what should work differently for Wasp.

3. That is a very good point, we should make that a rule: eliminate assumptions/caveats that we have to warn our users about - those are leaks in abstraction. I believe we can, but if not, I agree, we should look into modifying how we abstract those parts.

I have to admit with shame I know sparingly little about the accessibility when building web apps, but with Wasp we think it could be a really valuable feature to offer - guaranteed best accessibility practices - it should be integral part of Wasp.

Why doesn't screen reader detect it -> because URL does not change? What if SPA updates URL via history API, does that help? off-screen ARIA region sounds like something we could generate with Wasp automatically.

Once we reach some level of stability, we plan to put more effort into accessibility -> if you wish, write a comment on our GH issue and we will be happy to include you in the effort when we work on it, help from somebody experienced with accessibility would be great.

I get where you are coming from! It does feel like that sometimes when building Wasp - a lot of technologies we are layering on top of, sometimes we need to let them leek out, sometimes we want to hide them, and sometimes we have to play around them - it is a lot of work to put all that together to play nicely, and it will never stop shifting, even if we "make it" it is always going to be constant effort keeping up. On the other hand, I can't imagine tackling this in another way. Designing a web app is not complex for no reason - it is complex because web apps are complex. They are multi-user systems that work over distributed architecture, that need to maintain persistent state, look good, be scalable, be fast, work on all kinds of different browsers/devices/screens, be secure, ... . When you also add all the innovation that is happening in the current system, the idea of build a bespoke solution from the ground up sounds next to impossibly hard. Even Wasp sometimes feels impossibly hard to do, and it is still of much smaller scope than completely bespoke solution. I would love to hear your ideas on this though - I certainly haven't been thinking a lot in this direction, merely due to discarding it upfront as a possible approach.

Btw, one thing that Wasp does in alignment with your idea of bespoke solution, is that it brings DSL that is in big part detached from the technology used for the implementation of the specific parts (JS/React/Node/...), meaning that if we get it working with existing technologies, we have one foot on the solid ground and can work from there to see which other, "smaller" problems can be tackled next, potentially ending up with a set of solutions that together create one new, bigger, bespoke solution.

We know about Hasura -> it sounds like a cool idea! First we will be focusing on conquering React/Node combo, but it will be interesting to explore if there is an opportunity to include solutions like Hasura. What part of logic do you imagine happening in Hasura, and what in Wasp? I guess in this case Wasp would mostly be handling frontend right?

I will have to check out React Admin, I haven't heard about it yet.