Hey folks,
I was wondering whether we could add a few low-hanging QOL fruits to the repl such as printing the type, like ghci can do with :t:
![]()
I didn't manage to do that, but instead I was able to implement a :defs command printing the known definitions and their source:
![]()
that in itself was pretty simple:
fn printDefs(self: *ReplSession) Allocator.Error![]u8 {
var output = std.ArrayList(u8).empty;
errdefer output.deinit(self.allocator);
for (self.definitions.items.items) |item| {
const desc = switch (item.kind) {
.value => "value",
.annotation => "annotation",
.type_decl => "type",
.import => "import",
};
try output.print(
self.allocator,
"\x1b[90m{s}: {s}\x1b[0m\n",
.{ item.name, desc },
);
try output.print(self.allocator, "{s}\n", .{ item.source });
}
return try output.toOwnedSlice(self.allocator);
}
but how would I go about the type? is that also part of the interpreter state somehow, or do I have to invoke something like eval.test_helpers.compileInspectedProgramForTargetWithBuiltin and look for type information there somehow?
Hi @Lukas Juhrich,
Based on a quick check parseAndCanonicalizeProgramPublishedRootsWithBuiltin returns ParsedResources which contains a fully type checked *ModuleEnv. You should be able to render those types using TypeWriter.
Anton said:
You should be able to render those types using
TypeWriter.
thanks for the pointer, I think I'm getting the hang of it! :D
![]()
To be honest I'm a bit surprised that I have to do a full module source construction / parse / canonicalize for this.
But this is the way even eval works for every input right now, correct?
Yeah, it has not yet been optimized
I effectively have no LSP so things are a bit slow, but it's taking shape!
![]()
welcome @Lukas Juhrich! :smiley:
and thanks for doing this!
I meant to comment earlier but I got sidetracked and never finished the draft :sweat_smile:
both of these are definitely features we want, although I'm curious to explore what it would look like if you didn't have to do :t or :defs
so in the old repl, we would just do the equivalent of :t every time
like we'd print:
"Hello, World!" : Str
the tricky part with this is that it's now confusing for beginners because the types of literals are actually very complicated now
like that wouldn't be Str, it would be something more like a where [a.from_quote : ...etc ]
my initial thought was just "don't print type annotations on literals" but that gets tricky because what if you do like ("Hello", "World!") - do we not print the full type of the tuple because it has string literals in it?
so maybe :t is the best answer for now, until (if) a better idea presents itself :smile:
and with :defs I also wonder if there's some way we could make it more discoverable, e.g. maybe if you press Enter without typing anything, we print the defs?
but either way, the implementations are good ideas!
Could we use slash commands “/“ as a trigger for command palette instead of “:”. It is pretty common these days: Discord, Slack, OpenCode, Obsidian, Zed, etc
I like slashes too :smile:
if there's some way we could make it more discoverable
We can print a brief list of all slash commands when launching the repl
as long as we don't accumulate too many :sweat_smile:
I think that the most useful info when entering a REPL are:
(pressed enter too early while writing this post, sorry if you got a ping)
Hi, a few generic followed by more specific thoughts. Sorry about the length.
Richard Feldman said:
as long as we don't accumulate too many :sweat_smile
…I would hope the repl would get a ton of commands (but maybe I'm just used to that)!
A really good repl is what kept pulling me back to python (at least multiple times over my 12 years of programming). Specifically, the ipython repl (not to be confused with the ipython notebooks). What makes it really good for me?
python (≤3.12) or e.g. ghci (juggling with :{ and :} is not multiline support)pretty printing out of the box
![]()
Being able to load existing code into my session using the %load magic or a normal import, enabling me to develop ideas which actually relate to my codebase
Quickly looking up documentation on symbols using <identifier>?
![]()
if that's not enough, quickly looking up the definition (even of imported functions) using <identifier>??:
![]()
Being able to save parts of the session to a script
Due to its static/compiled nature, I think Roc would even be able to outdo ipython with one killer feature: saving the contents of a repl cell and all its dependencies into a file. For instance, here is a typical excerpt from my ipython history:
![]()
Highlighted in yellow are the parts where I converged to what I actually wanted to express. Being able to /save --append ./my_important_roc_file.roc matches and having words regex I typed in way back in the session be pulled in automatically would be indescribably useful.
Romain Lepert said:
Could we use slash commands “/“ as a trigger for command palette instead of “:”. It is pretty common these days:
I would disagree with this statement by scoping it: While / is the de facto standard for messengers and webapp command pallettes, I wouldn't say this is true for repls and command-/modelines of terminal-based apps at all:
: for their modelines/command lines, such as less, vim, vim-overlays, vim-inspired editors, tmux (after its bind-key): as well%psql uses \sqlite3 uses . (why though (later edit: node does that, too, which might explain it.))M-x to trigger the minibuffers focus! escape hatch, which I'd say is a different discussion.All of these have their reasons and I don't really favor one over the other (given I use most of these tools and got used to their way of doing). I just implemented what felt natural. I just want to point out that / is not an obvious truth in this case.
Richard Feldman said:
I also wonder if there's some way we could make it [
:defs] more discoverable
Let me play devils advocate on my own contraption here: Is that command useful enough to warrant prominent discoverability (beyond tab completion or a :help command which often exists)? I see it replaced by more specific but useful commands in the future. More specifically, if the following would exist I would probably never use :defs again:
:info <symbol> printing type + def (or a ?? suffix, the precise solution does not matter):l/:list:defs-for <symbol> printing the all the defs required to make <symbol> well-defined:defs vs. the :defs <symbol> specialization. That would be really cool, now that I think about it :thinking: Ctrl-rGiven how much junk I usually write in a repl before arriving at what I want I wouldn't be so sure in which scenario I'd want to see all that printed back at me. But maybe other folks have a better signal-to-noise ratio than me, which changes the stakes :)
All of this is based on my experience. I'm not saying ipython or any other tool is what one must mimic. I think it's an important case study though, and there's probably potential to do an even better job. Making a good repl is not trivial though; I've only seen one so far.
I am killing the vibe here but I feel like repls are becoming less important over time.
I think the core features should be really solid and we can have some of the niceties but I would not go much beyond that.
Anton said:
I am killing the vibe here but I feel like repls are becoming less important over time.
I think the core features should be really solid and we can have some of the niceties but I would not go much beyond that.
That's completely fair. Ipython is also a third-party tool, and ultimately theres nothing stopping me from building an external roc repl ;)
So defining some boundaries (goals/non-goals) for roc repl would probably be good.
Anton said:
I am killing the vibe here but I feel like repls are becoming less important over time.
I think the core features should be really solid and we can have some of the niceties but I would not go much beyond that.
I must admit that I've stopped using Python's REPL (except for quick math and one-liners) eversince the Jupyter VSCode extension made it possible to write regular .py files containing executable cells delimited by #%%. You get the ideal mix of easy editing and interactive shell, without the editing limitations of a regular shell, or the bloat, git nightmare, and execution ordering hell of Jupyter notebooks.
Maybe we could have a similar Roc extension rather than invest time in the REPL (says the guy who just invested time in the REPL).
Or actually we could have a Roc kernel for Jupyter, if this doesn't exist yet.
Ayaz built a Roc kernal for Jupyter using the old rust compiler I think... I'm not sure what would be required to revive that
I'll take a look, thanks
Android Compiler
@Ayaz Hafiz pointed to the code in this topic:
https://gist.github.com/ayazhafiz/2ecb5d07393421c06fde945784efb384
And the corresponding compiler patch: https://github.com/roc-lang/roc/commit/888812b6d8f4efb34d137a4dd29d245ae8685e2a
Does that mean we could modify our REPL to support that use case?
I dont have any experience with Jupyter kernel's or anything so I'm not even sure what you would use that for
Having a Jupyter kernel means that you can use create Jupyter notebooks written in Roc, which is great for literate programming, data visualization, exploration, etc. You could also use the kernel with the Jupyter extension for VSCode (and probably other editors) which allows you to write .roc files containing executable cells delimited by #%%, as I mentioned above.
Just thought I'd chip in my own use cases for repls and interactive environments more generally. Elixir's IEx and Hakell's GHCi were both invaluable as a beginner. Both remained valuable either because e.g., they permitted connecting to and inspecting the state of running systems, or for decomposing a problem interactively and seeing the outputs as you go with ghcid/ghciwatch.
I'm a big fan of Elixir's Livebook (~ similar to Jupyter) for reproducible notebooks, and plumbing data around when building datasets, as well as visualising results. Doing it in a language with immutable semantics is big piece of mind for just being able to run the notebook head to tail and getting the same result/graph/etc. I'm not saying Roc is likely to overtake Python here (neither will Elixir) but the usability was sufficient at least for me to switch. I also don't know what Roc's plans are for interactive debuggers, but if there was one I'd imagine a good repl would help.
That said... I think a lot of this has been/can be done outside of the language by third party tools as @Lukas Juhrich noted. The core to me would be a usable repl with a watch mode tbh.
I like notebooks too :)
I also don't know what Roc's plans are for interactive debuggers
LLMs are getting extremely good at debugging so I guess we will not need them unless interactive debuggers could speed up their debugging significantly. It could save a bunch of compiler and run cycles.
Anton said:
I also don't know what Roc's plans are for interactive debuggers
LLMs are getting extremely good at debugging so I guess we will not need them unless interactive debuggers could speed up their debugging significantly. It could save a bunch of compiler and run cycles.
Please don't forget us non-AI users though :sweat_smile:.
Hopefully we'll have enough time to take care of the artisanal programming experience :)
Is that how it is at the moment, the artisanal programming experience a nice-to-have? :tear: Not my decision of course, but I do think it'd be good for potential users of Roc to know if it's assumed they're using an LLM.
I'll start a new topic in community.
#community > focus on human vs AI users
Throwing my two cents in, I don't use repls too often but the last times I found them really helpful was:
1) exploring the Ghidra Python scripting API to inspect things and check my understanding before updating the actual script file
2) learning some Haskell a few years ago I found the repl really helpful, especially with trying to understand more complicated types etc. Roc definitely seems designed to have a simpler type system (from the user's perspective at least), BUT I think a repl would help with understanding what type things are being inferred as. Maybe down the line that's something an LSP could take care of, I don't know. So yeah my most likely use case with the Roc repl would be inspecting types to gain some understanding I think. But I take Richard's point that the types of literals for example are pretty long and complicated. Also I guess the type system is bi-directional (noob here), and printing the type after any assignment might give the impression that the type can't change later. When it's being inferred it can change later right? Was trying the following example in the repl:
» x = 5
assigned `x`
» x
5.0
» y : U8
» y = x
assigned `y`
» y
5
» x
5 # seems that x has become an integer, by virtue of it being assigned to y a few lines after x was declared
»
Also +1 about being able to load a source file into the repl, be able to try running functions with different inputs, check assumptions ect. And regarding a debugger it seems like even if you use an LLM to debug, it could be a useful tool for walking the programmer's brain through how the code works and check assumptions there.
Being able to use the Elm repl while loading modules in the same project was very useful for going through tutorials or Advent of Code. You can put a bunch of code in a file, then import that file in the repl, and just call functions you just added/modified in the imported module to make progress.
repl imports may be easy to add, I will explore that right now.
Completely forgot – What does the discussion mean for my PR though? @Richard Feldman should I keep :info in or remove it, or adapt to a different behavior as you pondered?
The / to me is separate because the existing commands use :.
:info sounds good! :thumbs_up:
Anton said:
repl imports may be easy to add, I will explore that right now.
Last updated: Jul 23 2026 at 13:15 UTC