John Mikael Lindbakk // 13/04/2026 12:00
Many countries have multiple languages. Wales has English and Welsh. The Irish got Gaelic, and Cornwall in England has Cornish. In Norway, we technically have three languages: Bokmål, Nynorsk, and Sami.
One can argue that we can technically speak Nynorsk as well, which is true, but there is no "pure" Nynorsk dialect. Nobody speaks "pure" Nynorsk, at least not unless going out of their way to do so. There are many dialects that come pretty close, but none are Nynorsk. This makes Nynorsk a purely written language, which I find fascinating.
One can make similar arguments about Bokmål and Sami, but people speak Sami. And I would argue that a lot more people speak "pure Bokmål" than Nynorsk.
Or maybe I'm just pulling this out of my ass, and I don't know what I'm talking about.
The point is that I made a purely Nynorsk programming language: Brunost.
Brunost
Brunost is held in high regard in Norwegian culture as a smooth, sweet goat cheese that we use for waffles, sauces, and sandwiches. It is the most Norwegian of cheeses. If you want to have a good time, put some brunost on your waffles with strawberry jam and sour cream!
This is why Brunost is the perfect name for a Nynorsk programming language. Sweet and smooth, yet with depth that tickles the palate. Therefore, let me present "Hello world" written in perfect Brunost:
bruk terminal
terminal.skriv("Hei, verda!")
Do you see its smoothness? Come on, give it a taste! And if you don't like it, then maybe you're the problem. Have you thought about that?
Brunost is a delightful functional language. It is interpreted, and its types are almost as loose as your mum.
The Brunost interpreter is written in Zig, which is why it is blazingly fast-ish. Python and JavaScript are going down!
With Brunost, you'll get unmatched aesthetic performance with unmatched Nordic bravado.
Heck, you can play with it online, in your browser, right now!
Enforced Nynorsk
One of the most important aspects of Brunost is that it requires Nynorsk. Variable, parameters and function names must be in Nynorsk. The interpreter ships with a Nynorsk dictionary that is used during the interpretation, and if the developer tries to do anything but Nynorsk, they'll get a clear message:
Feil: Namnet er ikkje gyldig nynorsk: 'thisIsNotNynorsk' på linje 8, kolonne 6
Nynorsk is no longer a minor subject in school. Soon, it will become a requirement for the Norwegian IT industry!
Many lesser languages value developer expression. Brunost knows that developers overuse jargon and should use plain (Nynorsk) language rather than fancy abbreviations.
Syntax
I know that you're about to explode from anticipation, so let's go over this magnificent syntax, shall we?
Variables, mutability & assignments
You can assign a variable like this:
fast fart er 80
Now, the keyword fast here is saying that the variable fart cannot be changed - it is immutable.
If you try to change it like this:
fast fart er 80
fart er 50 //Error
then you get an error:
Kan ikkje endra ein uforanderleg variabel (deklarert med 'fast')
If you want to change a variable, you must mark it with endreleg, which indicates that it is changeable:
endreleg fart er 80
fart er 50 //Ok
Conditionals
A language where you cannot create branches is not very useful, so we have the viss statement, which functions similarly to how if statements work in other languages:
bruk terminal
fast fartsgrense er 80
fast minFart er 90 // Change this for different outcomes
viss (minFart erStørreEnn fartsgrense) gjer {
terminal.skriv("For høy fart!")
} ellers viss (minFart erSameSom fartsgrense) gjer {
terminal.skriv("Farten din er på grensa!")
} ellers {
terminal.skriv("Farten din er innafor.")
}
Here we see a classical if-else demonstration where we can use ellers to chain viss statements, or just to have a default branch.
You might also notice the English comment in that code snippet. I KNOW! It's horrid. Disgusting! It is revolting! But I did it for you, dear reader, because I'm no bigot and I love you despite you not understanding Nynorsk. After all, we all have flaws, and mine is caring too much.
Functions & returns
A functional programming language without functions wouldn't be very useful, and I think it is already clear that Brunost is super useful. So Brunost has functions as well!
bruk terminal
gjer kalkulerFart(distanse, timer) {
gjevTilbake distanse / timer
}
fast distanse er 50
fast timer er 2
terminal.skriv("Din fart er: " + kalkulerFart(distanse, timer) + "km/t")
Here we have the function kalkulerFart, which is called within terminal.skriv.
Within kalkulerFart, we have the gjevTilbake statement (give back in English), which is essentially a return statement in other languages.
You might wonder what happens if we move kalkulerFart under the terminal.skriv call? Well, things break, of course! Why would you want to use something that hasn't been declared yet!? Silly.
Brunost has strong opinions on the order of declaration. Developers are expected to know the order in which things exist.
Loops
Being able to iterate over things is important, and Brunost has got you covered!
Here we have Brunost's version of a foreach loop called forKvart:
bruk terminal
fast fylke er ["Vestland", "Rogaland", "Troms", "Finnmark"]
forKvart namn i fylke {
terminal.skriv("Hei frå " + namn + "!")
}
And we also got a while loop:
bruk terminal
endreleg teljar er 1
medan (teljar < 4) gjer {
terminal.skriv(teljar)
teljar er teljar + 1
}
Types, Data Structures & standard library
We got the common ones, don't worry. We got the numbers, strings, and whatnot. But why do we have to be so specific about it? Why the need to box everything into rigid types and structures? What are you? Some kind of rigid nerd?
Don't worry about the types, man.
Imports
So far in the code snippets, you have seen the keyword bruk; this is how we do imports.
So far, we've seen terminal, a built-in module, but we have others, like matte and streng.
You can also create your own modules. Let's say we have two files, one called "hovud.brunost" and the other called "logikk.brunost".
In hovud.brunost, if you wanted to use something from logikk, then you would do:
bruk logikk
But let's say that logikk is in a subfolder called "kode", then we would do it like this:
bruk kode.logikk
Exception handling
Every program can fail. Brunost can do a lot, but it cannot break physics (yet). But Brunost tries to help you deal with those failures with classical try-catch logic:
bruk terminal
gjer del(første, andre) {
viss (andre er 0) gjer {
kast "Kan ikke dele på null"
}
gjevTilbake første / andre
}
prøv {
del(10, 0)
} fang(feil) {
terminal.skriv("Noe feilet: " + feil)
}
Some might say that errors-as-returns is better than exceptions. Why? Just don't make errors. Easy as that.
WebAssembly support!
One fun thing is that making a Wasm deployment of the interpreter was not that difficult, and you can use it online right now!
Making Zig play well with WebAssembly was easy enough, and most of the complexity was in handling terminal commands (or rather, input/output buffers, which do not play well with Wasm).
I didn't put much effort into it, which is why the game of life implementation further down is really funky in the online version.
This section of the post only exists in a vain attempt to boost my SEO by mentioning Wasm. Let's hope it works.
Wrapping up
Joking aside for a second: I won't be making a whole ecosystem for this silly language. There are a few more things I want to have in place before I wrap up completely:
Hashmaps.
Proper documentation.
Enough features that Brunost can serve a website.
Some way to deal with non-Nynorsk words that are still allowed in Nynorsk.
Some FFI support.
File read/write in the standard library.
A language mascot.
After this, I will most likely just leave Brunost as a fun little thing that people can play around with.
This was just a fun little project to create something that didn't exist. I've had the idea of a fully Norwegian programming language for quite some time, mostly because I think the overall idea is dumb.
One thing I didn't anticipate is that I am barely able to write Brunost myself! I use a US keyboard layout, which means I simply can't, for the life of me, write Brunost code. Trying to get the { and } correct while also handling æ/ø/å is extremely horrid after years of using a US keyboard layout.
Not only that, but it turns out that being locked to a dictionary is kinda limited. Earlier in the project, I had a BMI calculator example, but I had to remove it because BMI is not a word in Nynorsk...
All that said, if people want to have fun and continue building on Brunost, then I'm happy to look over and deal with pull requests. If someone wants to do some low-stakes programming-language development, Brunost might be a place to have some fun. If people have some fun/stupid ideas for how to "elevate" the language, then let me know!
Here are some suggestions if you "just want a project":
Editor & Web syntax highlighting, and general editor integrations
Language server
Extend the standard library
If that is the case, head over to the GitHub repo and pitch something, then we'll see if we can figure something out!
And just in case the joke is lost on you: No, Brunost is not a language you should use in actual production. You could... but you shouldn't. And if you do, let me know so that I can scold you.
Bonus: Examples
These are just some Brunost code snippets I've made, just to show that it is a real language that does real things. More examples can be found in the GitHub examples folder.
Fibonacci
bruk terminal
gjer rekke(grense) {
viss (grense < 2) gjer {
gjevTilbake grense
}
gjevTilbake rekke(grense - 1) + rekke(grense - 2)
}
endreleg teljar er 1
medan (teljar erSameEllerMindreEnn 15) gjer {
terminal.skriv(rekke(teljar))
teljar er teljar + 1
}
FizzBuzz
bruk terminal
bruk matte
endreleg teljar er 1
medan (teljar erSameEllerMindreEnn 15) gjer {
viss (matte.modulus(teljar, 15) erSameSom 0) gjer {
terminal.skriv("FizzBuzz")
} ellers viss (matte.modulus(teljar, 3) erSameSom 0) gjer {
terminal.skriv("Fizz")
} ellers viss (matte.modulus(teljar, 5) erSameSom 0) gjer {
terminal.skriv("Buzz")
} ellers {
terminal.skriv(teljar)
}
teljar er teljar + 1
}
Conway's Game of Life
bruk liste
bruk terminal
bruk matte
bruk prosess
bruk streng
fast breidde er 60
fast høgd er 30
// Skapar ei liste med tilfeldige 1-ar og 0-ar
gjer lagBrett() {
endreleg brett er []
endreleg rad er 0
medan (rad erMindreEnn høgd) gjer {
endreleg rekkje er []
endreleg kolonne er 0
medan (kolonne erMindreEnn breidde) gjer {
rekkje er liste.leggTil(rekkje, matte.tilfeldig(0, 1))
kolonne er kolonne + 1
}
brett er liste.leggTil(brett, rekkje)
rad er rad + 1
}
gjevTilbake brett
}
gjer hentCelle(brett, kolonne, rad) {
viss (kolonne erMindreEnn 0
eller kolonne erSameEllerStørreEnn breidde
eller rad erMindreEnn 0
eller rad erSameEllerStørreEnn høgd) gjer {
gjevTilbake 0
}
fast rekkje er liste.hent(brett, rad)
gjevTilbake liste.hent(rekkje, kolonne)
}
gjer telNaboTal(brett, kolonne, rad) {
endreleg sum er 0
sum er sum + hentCelle(brett, kolonne - 1, rad - 1)
sum er sum + hentCelle(brett, kolonne, rad - 1)
sum er sum + hentCelle(brett, kolonne + 1, rad - 1)
sum er sum + hentCelle(brett, kolonne - 1, rad)
sum er sum + hentCelle(brett, kolonne + 1, rad)
sum er sum + hentCelle(brett, kolonne - 1, rad + 1)
sum er sum + hentCelle(brett, kolonne, rad + 1)
sum er sum + hentCelle(brett, kolonne + 1, rad + 1)
gjevTilbake sum
}
gjer nesteGenerasjon(brett) {
endreleg nyttBrett er []
endreleg rad er 0
medan (rad erMindreEnn høgd) gjer {
endreleg nyRekkje er []
endreleg kolonne er 0
medan (kolonne erMindreEnn breidde) gjer {
fast celle er hentCelle(brett, kolonne, rad)
fast naboTal er telNaboTal(brett, kolonne, rad)
endreleg nyCelle er 0
viss (celle erSameSom 1) gjer {
viss (naboTal erSameSom 2) gjer { nyCelle er 1 }
viss (naboTal erSameSom 3) gjer { nyCelle er 1 }
} ellers {
viss (naboTal erSameSom 3) gjer { nyCelle er 1 }
}
nyRekkje er liste.leggTil(nyRekkje, nyCelle)
kolonne er kolonne + 1
}
nyttBrett er liste.leggTil(nyttBrett, nyRekkje)
rad er rad + 1
}
gjevTilbake nyttBrett
}
gjer teiknBrett(brett) {
terminal.tøm()
endreleg rad er 0
medan (rad erMindreEnn høgd) gjer {
fast rekkje er liste.hent(brett, rad)
endreleg linje er ""
endreleg kolonne er 0
medan (kolonne erMindreEnn breidde) gjer {
fast celle er liste.hent(rekkje, kolonne)
viss (celle erSameSom 1) gjer {
linje er linje + "██"
} ellers {
linje er linje + " "
}
kolonne er kolonne + 1
}
terminal.skriv(linje)
rad er rad + 1
}
}
gjer køyrKomeSjø(fart, iterasjonar) {
endreleg brett er lagBrett()
endreleg omgang er 0
medan (omgang erMindreEnn iterasjonar) gjer {
teiknBrett(brett)
brett er nesteGenerasjon(brett)
prosess.sov(fart)
omgang er omgang + 1
}
}
endreleg iterasjonar er 50
prøv {
iterasjonar er streng.tilTal(terminal.argument(0))
} fang (feil) {
}
terminal.skriv("Startar Conway sitt livsspel i Brunost!")
prosess.sov(1000)
køyrKomeSjø(100, iterasjonar)
terminal.skriv("Slutt på livsspelet!")