I wanna compare a CHIP 8 or NES emulator or port CAMLBOY to WASM using ocaml-wasm
https://www.slideshare.net/slideshow/emulating-game-boy-in-j...
Essentially, there are 4 channels, each providing a number 0-15 on every tick. Emulator should mix them together (arithmetic average), scale up to 0-255 and feed to the sound buffer, adjusting the tick rate (4.19MHz) to the sound output rate (e.g.: 22 kHz) - taking every ~190 value (4.19MHz / 22 kHz) is a good start.
Now the 0..15 value that should be produced by each channel depends on its characteristics, but it's well documented:
https://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware
Channels 1 and 2 produce square waves, so a bunch of low (0) and high (15) values, with optional volume envelope (gradually going down from 15 to 0 on the "high" part of the square) and frequency sweep (alternating 0s and 15s slower or faster).
Channel 3 allows an arbitrary waveform, read from the memory.
Channel 4 is a random noise, generated by the LSFR.
See SoundModeX.java for the reference:
https://github.com/trekawek/coffee-gb/tree/master/src/main/j...
Needs a (2022).
Where, when I say "better", I'm not so much talking about getting results that are particularly efficient/performant; nor in making fewer implementation errors... but more in terms of the experience of implementing an emulator in this particular language, being more rewarding, intuitive, and/or teaching you more about both emulators and the language.
I ask because I know that this sort of language exists in other domains. Erlang, for example, is particularly rewarding to implement a "soft-realtime nine-nines-of-uptime distributed system" in. The language, its execution semantics, its runtime, and its core libraries, were all co-designed to address this particular problem domain. Using Erlang "for what it's for" can thus teach you a lot about distributed systems (due to the language/runtime/etc guiding your hand toward its own idiomatic answers to distributed-systems problems — which usually are "best practice" solutions in theory as well); and can lead you to a much-deeper understanding of Erlang (exploring all its corners, discovering all the places where the language designers considered the problems you'd be having and set you up for success) than you'd get by trying to use it to solve problems in some other domain.
Is there a language like that... but where the "problem domain" that the language's designers were targeting, was "describing machines in code"?
I think the only thing that ocaml has that I miss in sml is applicative functors, but in the end that just translates to slightly different module styles.
IMO: it's certainly "simpler" and "cleaner" (although it's been a while but IIRC the treatment of things like equality and arithmetic is hacky in its own way), which I think causes some people to prefer SML over aesthetics, but TBH I feel like many of OCaml's features missing in SML are quite useful. You mentioned applicative functors, but there's also things like labelled arguments, polymorphic variants, GADTs, even the much-maligned object system that have their place. Is there anything SML really brings to the table besides the omission of features like this?
mlton allows you to use a keyword to get the same facility for function overloading that is used for addition and equality. it's disabled by default for hygienic reasons, function overloading shouldn't be abused.
https://baturin.org/code/mlton-overload/
> labelled arguments
generally speaking if my functions are large enough for this to matter, i'd rather be passing around refs to structures so refactoring is easier.
> polymorphic variants
haven't really missed them.
> GADTs
afaik being able to store functors inside of modules would fix this (and I think sml/nj supports this), but SML's type system is more than capable of expressing virtual machines in a comfortable way with normal ADTs. if i wanted to get that cute with the type system, i'd probably go the whole country mile and reach for idris.
> even the much-maligned object system that have their place
never used it.
> Is there anything SML really brings to the table besides the omission of features like this?
mlton is whole-program optimizing (and very good at it)[1], has a much better FFI[2][3], is much less opinionated as a language, and the parallelism is about 30 years ahead[4]. the most important feature to me is that sml is more comfortable to use over ocaml. being nicer syntactically matters, and that increases in proportion with the amount of code you have to read and write. you dont go hiking in flip flops. as a knock-on effect, that simplicitly in sml ends up with a language that allows for a lot more mechanical sympathy.
all of these things combine for me, as an engineer, to what's fundamentally a more pragmatic language. the french have peculiar taste in programming languages, marseille prolog is also kind of weird. ocaml feels quirky in the same way as a french car, and i don't necessarily want that from a tool.
[1] - http://www.mlton.org/Performance
[2] - http://www.mlton.org/ForeignFunctionInterface
[3] - http://www.mlton.org/MLNLFFIGen
[4] - https://sss.cs.purdue.edu/projects/multiMLton/mML/Documentat...
I respect the sheer power of what mlton does. The language itself is clean, easy to understand, reads better than anything else out there, and is also well-formalised. I read (enjoyed!) the tiger book before I knew anything about SML.
Sadly, this purism (not as in Haskell but as a vision) is what probably killed it. MLTon or not, the language needed to evolve, expand, rework the stdlib, etc.
But authors were just not interested in the boring part of language maintenance.
I have been looking forward to ML like capabilities on mainstream since using Caml Light.
Regarding those books, while we used the Java version, alongside JavaCC, when time came to actually buy the book, I also got the SML edition.
> have been looking forward to ML like capabilities on mainstream since using Caml Light.
Modern Java is not built around ADTs and pattern matching, these things feel a bit bolted on. SML feels almost like a compiler writing DSL.
I rather have Java, C#, C++, Kotlin, Rust,.... with their imperfections to ML, Smalltalk, and Lisp ideas, than hoping for getting the right languages at the workplace.
Fully agree on the C edition.
Good enough wins. Worse is better rules. Release early... and so on.
I am old enough to remember the full drama behind adding lambda to python, and resistance to bits of functional goodies coming from lisps.
But here we are. Java is an aaaalmost acceptable language!
While scheme, standardml and numerous other perfect little languages are mostly just Wikipedia pages now.
None of that applies to this case, where ML is not only a fair bit older than Java, but abides the principle of "worse is better" far more.
> While scheme, standardml and numerous other perfect little languages are mostly just Wikipedia pages now.
Frankly, I'm thankful I work in an environment with creative autonomy and can choose (and create) my own tools, rather than being forced to work with ones I don't particularly care for because they had a large marketing push.
Sml is a superpolished language, very well-defined formally, being a culmination of years of design effort and uniting a family of academia-born languages. It took 40 years of research to release the final version in 1997.
Java is on the industry side of thing: semi-formalized, numerous omissions when created, a bunch of ad hoc ideas, released in 1995. Not sure it took more than 3-4 years to get the first official version out.
SML is borderline minimalist, it's "worse" because it doesn't try to solve everybody's problems with a bunch of ad hoc language features and a stdlib and formalization larger than the entire legal code of the United States. It's "worse" because it comes with "less". You can't just import solution and start writing AbstractFactoryManager2 garbage. It's not actually worse to anybody but beancounters.
But... I feel that this will not matter all that much soon
"How we prevent conflicts in authoritative DNS configuration using formal verification"
People write "production-grade" emulators in C because it's fast, not because it's uniquely suited to the domain as a language.
...just kidding (maybe).
Assuming we're talking about a pure interpreter, pretty much anything that makes it straightforward to work with bytes and/or arrays is going to work fine. I probably wouldn't recommend Haskell, just because most operations are going to involve imperatively mutating the state of the machine, so pure FP won't win you much.
The basic process of interpretation is just: "read an opcode, then dispatch on it". You'll probably have some memory address space to maintain. And that's kind of it? Most languages can do that fine. So your preference should be based on just about everything else: how comfortable are you using it, how much do you like its abilities to interface with your host platform, how much do you like type checking, and so on.
Of course, if you actually want to run games on the emulator, C or C++ is where the game is. I suppose Rust would work too, but I can't speak much for its low-level memory manipulation.
At any rate, that’s not really the case when building an emulator or bytecode interpreter. And Haskell ends up being mostly a liability here, because most work is just going to be imperatively modifying your virtual machine’s state.
I believe those two languages themselves self-host. So not saying it’s impossible. And I have no clue about the technical merits.
But if you look around programming forums, there’s this ideas that”Ocaml is one of the leading languages for compiler writers”, which seems to be a completely made up statistic.
But I would call Rust, Haxe and Hack production compilers. (As mentioned by sibling, Rust bootstraps itself since its early days. But that doesn't diminish that OCaml was the choice before bootstrapping.)
RAII, iterators, templates, object encapsulation, smart pointers, data ownership, etc are entrenched in C++; while C is still raw pointers, no generics (no _Generic doesn’t count), procedural, void* casting, manual malloc/free, etc.
I code in both, and enjoy each (generally for different use cases), but certainly they are significantly differing experiences.
It is like adopting Typescript, but the only thing they do is renaming the file extension for better VScode analysis.
Another one is C++ "libraries" that are plain C with extern "C" blocks.
> Another one is C++ "libraries" that are plain C with extern "C" blocks.
Sure, and you also see "C Libraries" that are the exact same. I don't usually judge the communities on their exceptions or extremists.
Also, Haskell has parsers for all major languages. You can find them on Hackage with the `language-` prefix: language-python, language-rust, language,javascript, etc.
That sounds odd to me. Haskell is great for managing state, since it makes it possible to do so in a much more controlled manner than non-pure languages.
Not that you'd need a monad for something like this anyway.
C++: game.level.unit[10].position += 5;
Haskell: way too much code here (unless you use lenses of course but then you are effectively turning Haskell into an imperative language).
https://www.microsoft.com/en-us/research/wp-content/uploads/...
Also when people say Lisp in 2025, usually we can assume Common Lisp, which is far beyond the Lisp 1.5 reference manual in capabilities.
In fact, back when I was in the university, Caml Light was still recent, Miranda was still part of programming language lectures, the languages forbidden on compiler development assignments were Lisp and Prolog, as they would make it supper easy assignment.
Ultimately, the hard thing in emulation was not decoding instructions. It was synchronization, timing, and faithfully reproducing all the hardware glitches (because many games will not work without certain hardware bugs). Haskell doesn't help much for those things. If I was doing another emulation project I'd choose rust.
No, absolutely not. Emulation is super easy to implement in any language with arrays (constant-time lookup of arbitrary indices) and bit operations. At least before considering JIT.
And even functional languages have arrays and bitwise operations.
The reason being that the methodologies are far more orthogonal. A uint8 directly represents a byte in memory, doing a memcpy is equivalent to a blit, etc. You spend far less time trying to wrangle a JavaScript Number type into acting like a byte/word/etc for a bitshift operation for a simple ADC. A very simple example that you’ll run into in the first day of writing a js emulator.
That all being said, if the language can paint to some surface and has the memory size to handle the machine you’re emulating, they’re all roughly equivalent. So the answer becomes “whichever language you’re most comfortable with is the one writing an emulator in is most enjoyable”.
le-mark•7mo ago
As an aside I’ve always thought it would be awesome to create a single page app with an assembler editor and assembler/linker/loader to enable doing gameboy homebrew in the browser. I think it would be a great, accessible embedded development teaching opportunity.
binji•7mo ago