Skip to content

Commit 546a143

Browse files
committed
add elm-playground post
1 parent fec1cb5 commit 546a143

File tree

1 file changed

+223
-0
lines changed

1 file changed

+223
-0
lines changed
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
+++
2+
title = "Compiler-Driven Development: Building an Elm Playground That Compiles in the Browser"
3+
description = "How I built an interactive Elm workshop environment using Guida's in-browser compiler after server-side compilation hit memory limits"
4+
tags = ["elm", "compiler", "guida", "workshop", "frontend", "functional programming", "in-browser compilation"]
5+
date = "2025-08-31"
6+
draft = false
7+
+++
8+
9+
Sometimes the best solutions emerge from the ashes of failed approaches. This is the story of how I built [elm-playground](https://elm-playground.render.com) – an interactive Elm environment for teaching "compiler-driven development" – and how hitting memory limits forced me to discover something even better than my original plan.
10+
11+
## The Mission: Teaching Compiler-Driven Development
12+
13+
I've been planning to host an Elm workshop at [Ensō](https://enso.no) with a specific theme: "Compiler-driven development." The idea is to showcase how Elm's famously friendly compiler can guide your development process, catching errors before they become runtime surprises and helping you write better code through its helpful error messages.
14+
15+
But explaining the magic of compiler-driven development requires more than slides – it needs hands-on experience. I wanted workshop participants to feel the difference between "debugging by guessing" and "being guided by the compiler." That meant building an interactive playground where people could write Elm code and immediately see compilation results.
16+
17+
Granted, setting up Elm locally is not hard by any stretch of the word. But no setup is better than little setup!
18+
19+
## The Obvious Initial Approach
20+
21+
My initial plan was textbook simple:
22+
23+
1. Build a frontend with a code editor
24+
2. Set up a backend that receives code via POST requests
25+
3. Run `elm make` on the server
26+
4. Return compilation results to the frontend
27+
28+
I even had it working! The Go backend would create temporary directories, write the submitted code to `Main.elm`, run the compiler, and return either success (with the compiled JavaScript) or failure (with error messages).
29+
30+
```go
31+
// The original backend approach (simplified)
32+
func compileHandler(w http.ResponseWriter, r *http.Request) {
33+
tmpDir := createTempDir()
34+
writeCodeToFile(tmpDir, requestBody.Code)
35+
defer removeTmpDir()
36+
37+
cmd := exec.Command("elm", "make", "src/Main.elm", "--output=main.js")
38+
cmd.Dir = tmpDir
39+
40+
output, err := cmd.Run()
41+
// Handle success/error cases...
42+
}
43+
```
44+
45+
It worked perfectly on my local machine. It worked perfectly in development. But the moment I deployed to production on Render.com's free tier...
46+
47+
```
48+
failed
49+
43b72b0
50+
Ran out of memory (used over 512MB) while running your code.
51+
```
52+
53+
## The Memory Problem
54+
55+
No matter what I tried, `elm make` consistently exceeded the 512MB memory limit. I attempted several optimizations:
56+
57+
- **Pre-warming `elm-stuff`**: Reusing compiled dependencies between requests
58+
- **Symlinking instead of copying**: Reducing disk I/O overhead
59+
- **Switching from Go to Node.js**, using the existing node-elm-compiler package: Using lighter runtime abstractions
60+
- **Constraining `elm make`**: Looking for memory limit flags (spoiler: they don't exist)
61+
62+
Nothing worked. The Elm compiler, while excellent at its job, simply requires more memory than a free hosting tier provides for even simple programs. (_Allegedly_, that is – locally I could never reproduce this high memory consumption! But how and why that is is a different post.)
63+
64+
## Discovery: The Elm Community Has Solutions
65+
66+
Frustrated but not defeated, I turned to the Elm community Slack. The response was immediate and enlightening – several people had already solved this exact problem in different ways:
67+
68+
- One suggested [Ellie](https://ellie-app.com/new), the established Elm playground
69+
- Others mentioned [elmrepl.de](https://elmrepl.de) and [elm-repl-worker](https://pithub.github.io/elm-repl-worker/eager-tea.html)
70+
- Not least: [@deciojf](https://github.com/deciojf) introduced me to the [Guida/Try](https://guida-lang.org/try) PoC – a similar project using [Guida](https://guida-lang.org/), a port of the Elm compiler written in Elm itself
71+
72+
While Ellie would have worked, I wanted something I could customize and brand for our workshop. And Guida? That was intriguing.
73+
74+
## Enter Guida: Elm Compiling Elm
75+
76+
[Guida](https://guida-lang.org/) is a remarkable project – it's literally the Elm compiler ported to Elm, which means it can run in the browser via JavaScript. No server required, no memory limits, no deployment complexity.
77+
78+
The architecture is beautifully simple:
79+
80+
```elm
81+
-- (The actual code has more to it, but the following is a simplified illustration)
82+
83+
-- Instead of HTTP requests to a backend:
84+
type Msg
85+
= CompileCode String
86+
| CompilationComplete (Result Error String)
87+
88+
-- Guida handles compilation directly in the browser, using ports to pass source / compiled js:
89+
update : Msg -> Model -> ( Model, Cmd Msg )
90+
update msg model =
91+
case msg of
92+
CompileCode sourceCode ->
93+
( { model | status = Compiling }
94+
, sendSourceCodeToPortCmd sourceCode
95+
)
96+
97+
CompilationComplete result ->
98+
case result of
99+
Ok compiledJs ->
100+
( { model | status = Success compiledJs }, Cmd.none )
101+
102+
Err error ->
103+
( { model | status = Error error }, Cmd.none )
104+
```
105+
106+
## The Journey to Working Solution
107+
108+
Getting Guida integrated wasn't without challenges. Initially, I hit CORS issues even with identical configuration to the Guida demo. And the errors I got from the compiler once I'd solved the CORS/Proxy issue were not pretty-printed text but rather JSON that needed massaging.
109+
110+
But the Elm community came through again. [@deciojf](https://github.com/deciojf), Guida's creator, provided crucial guidance:
111+
112+
- **Error formatting**: Pointed me to [`elm/project-metadata-utils`](https://package.elm-lang.org/packages/elm/project-metadata-utils/latest/) for proper error message decoding
113+
- **Configuration tips**: Shared how the default `elm.json` is [hardcoded in browser.js](https://github.com/guida-lang/compiler/blob/master/lib/browser.js#L326C7-L349)
114+
115+
With these insights, I finally had a working solution. The final architecture is refreshingly simple:
116+
117+
1. **Pure frontend application** built with Elm
118+
2. **Guida integration** for in-browser compilation
119+
3. **No backend** required beyond static file hosting, and [a proxy](https://github.com/ensolabs/elm-playground/blob/master/index.cjs#L13) to Make Things Work™ when fetching Elm dependencies.
120+
4. **Proper error formatting** using Elm's own error decoders
121+
122+
## The Final Result
123+
124+
[Elm Playground](https://elm-playground.render.com) now provides exactly what I wanted for the workshop:
125+
126+
- **Interactive code editing** with syntax highlighting
127+
- **Real-time compilation** without server round-trips
128+
- **Friendly error messages** formatted just like the CLI compiler
129+
- **Zero infrastructure overhead** – just static files
130+
131+
More importantly, it perfectly demonstrates compiler-driven development. Workshop participants can:
132+
133+
1. Write invalid Elm code and see helpful error messages
134+
2. Fix errors guided by compiler suggestions
135+
3. Experience the confidence that comes with "if it compiles, it works"
136+
4. Understand why Elm developers love their compiler
137+
138+
The final two exercises don't work yet, because they have additional package dependencies not found in the deault `elm.json` file in Guida. But I'm quite confident I'll be able to solve that quite quickly 🤓
139+
140+
## Why This Solution is Better
141+
142+
The journey from server-side to client-side compilation turned out to be more than just a workaround – it's actually superior in several ways:
143+
144+
### 1. **Instant Feedback**
145+
146+
No network latency means compilation results appear immediately. This makes the compiler-driven development cycle feel natural and responsive.
147+
148+
### 2. **Scalable by Default**
149+
150+
Since compilation happens on the user's machine, the playground can handle unlimited concurrent users without additional infrastructure.
151+
152+
### 3. **Offline Capable**
153+
154+
Once loaded, the playground works without internet connectivity – perfect for workshop environments with unreliable WiFi.
155+
156+
### 4. **Lower Operating Costs**
157+
158+
Static file hosting is essentially free compared to maintaining server-side compilation infrastructure.
159+
160+
### 5. **Better Privacy**
161+
162+
Code never leaves the user's browser, addressing any potential concerns about code privacy.
163+
164+
## The Meta-Experience
165+
166+
There's something delightfully meta about this solution. The workshop is about compiler-driven development, and the tool we're using to teach it exemplifies the principle perfectly:
167+
168+
- **Elm code compiling Elm code** in the browser
169+
- **Type safety** throughout the compilation pipeline
170+
- **Impossible states** made impossible by Guida's architecture
171+
- **Helpful errors** at every layer
172+
173+
It's Elm all the way down, and it works beautifully.
174+
175+
## Lessons Learned
176+
177+
Building elm-playground taught me several valuable lessons:
178+
179+
### 1. **Constraints Drive Innovation**
180+
181+
The 512MB memory limit felt like a showstopper, but it forced me to discover a fundamentally better solution.
182+
183+
### 2. **Community Knowledge is Invaluable**
184+
185+
The Elm community's willingness to share experiences and solutions was crucial to success. (Good luck having GPT-whatever giving you advice is these scenarios...)
186+
187+
### 3. **In-Browser Compilation is Powerful**
188+
189+
Tools like Guida open up new possibilities for educational and development environments.
190+
191+
### 4. **Simple Solutions Scale**
192+
193+
The final architecture is much simpler than the original server-side approach, yet more capable.
194+
195+
## Looking Forward
196+
197+
Elm Playground is just the beginning. The success of in-browser compilation opens up interesting possibilities:
198+
199+
- **Multi-file projects** with proper module structure
200+
- **Package installation** and dependency management
201+
- **Interactive tutorials** with progressive skill building
202+
- **Code sharing** and collaborative editing
203+
- **Integration with other tools** in the Elm ecosystem
204+
205+
The foundation is solid, and the community is active. I'm excited to see where this goes.
206+
207+
Bonus: I was also invited to collaborate on [Guida](https://github.com/guida-lang/compiler), and I think I just might 🤓
208+
209+
## Try It Yourself
210+
211+
[Elm Playground](https://elm-playground.render.com) is live and ready for exploration. Whether you're teaching Elm, learning compiler-driven development, or just curious about in-browser compilation, I encourage you to give it a try.
212+
213+
The source code is available on [GitHub](https://github.com/ensolabs/elm-playground), and contributions are welcome. Special thanks to [@deciojf](https://github.com/deciojf) and the Guida project for making this possible.
214+
215+
## Conclusion
216+
217+
Sometimes the best engineering solutions come from embracing constraints rather than fighting them. What started as a simple backend compilation service evolved into something much more interesting: a demonstration of Elm's potential for meta-programming and self-hosting.
218+
219+
The elm-playground project proves that compiler-driven development isn't just a teaching concept – it's a practical approach that can lead to elegant, efficient solutions. And when your playground for teaching compiler-driven development is itself an example of compiler-driven development working beautifully... well, that's just good teaching.
220+
221+
At the time of writing this project has already been forked by my betters, as it turns out to be quite interesting in more ways than expected.
222+
223+
I'm glad I didn't give up!

0 commit comments

Comments
 (0)