Discoverability in Guile (namespaces, ide hints, intuition?)

Background:
Since Spritely has inspired me to repent and join the right path of computing, I’ve been rapidly learning and assimilating the Guile way of doing things. I’m running Guix on my desktop with Emacs (not Guile Emacs yet though) as my DE. I’ve started writing some crazy distributed-state code with Goblins and converting all my config into literate org files.

I’ve read the excellent Spritely Scheme Primer, as well as plenty of intro-level books and articles on various other lisps (my favourites are pig’s On Lisp, and The Little Schemer). I have lots of experience with other kinds of languages in various contexts, and have been a professional programmer for many years. I don’t say this to boast. I say it because it gave me an expectation that I would have an easier time learning.

Problem:
When I look at a piece of Guile code I find it very hard to know what’s going on. I know familiarity Congress with time and practice, but I feel like I’m missing something. In C there’s usually few enough libraries and tricks that I can easily spot unfamiliar imported functions and guess what they’re doing. In Haskell I use the type system and Hoogle to tell me what to expect and then I can use typed holes or the REPL to check my intuition. Even in Python every imported object will either have a namespace or explicit import in the same file, and I can use dir and help to inspect objects to see what’s possible and what I’m expected to do with them.

I don’t have this power (yet) in Guile. I see a symbol and apart from its name I don’t have any information about it. Is it defined yet? What’s in that definition? If it’s a function what arguments can it take? If it’s an object what members does it have? If I can’t see where it was bound (use-module #:select or let or define etc.) then how can I know where it came from?

Presumably all experienced Guile programmers have overcome this and have the familiarity to recognize common things, and reliable methods to quickly find out about unfamiliar things. I think my problem is that this is not present in introductory material, which focusses on basic examples and syntax, and is more likely just “common knowledge” that’s learnt from discussions and hard work.

Question (and tl;dr):
When you see (foo (bar #:baz quux)) in a piece of code, and you can’t tell what those symbols refer to, what do you do? (I can guess that maybe quux is local, baz is a keyword specific to bar and foo is some function related to baz’s output type, but that’s all.)

Does everyone use some handy geiser/lsp setup that integrates documentation and local definitions? Do you just grep your source folder? Do you spend years memorizing ice-9?

I know there’s no single answer, but I’d love to know what you working, productive Guile programmers do and how I can make my learning journey less bewildering.

Thanks, and please excuse for the excessively long post.

3 Likes

Hi @jdr!

First, a similar thread that may be helpful: How do I find what module to import for a given binding?

Leaving extensions like Geiser aside, you can use Guile’s built-in “meta commands” to query the state of the running system. At the REPL, you could ,apropos foo (or ,a foo for short) to get information about all bindings in the current environment matching foo. This will tell you the name of the module that exported that symbol, notably. To retrieve docstrings, use ,describe foo.

Geiser provides additional features that will do docstring lookup, show the signature of the procedure at the current point, jump to symbol definition (if it’s Scheme and not C), etc. Geiser is a must-have for Guile development, imo.

All else fails, I just grep the project for the symbol and typically find it, else it’s in some dependency or Guile itself. Scheme’s hygienic naming makes it easy to grep for things when you need to.

Hope this helps!

3 Likes

FWIW, I also find it useful to search at https://index.scheme.org/.

Come to think of it, common lisp has search integration for the common lisp hyperspec, it would be good to do the same for that index. Or does it exist as an emacs extension already?

3 Likes

Hmm, I’m not sure if there’s an Emacs extension for it. I must confess that I’ve been Scheming for some time and have never seen this index so thanks for sharing a link!

1 Like

Is there a good reason to refrain from using form like (use-modules ((string transform) #:select (collapse-repeated-chars)))? (I tend to use it all the time for my own sanity) Why not default to that?

1 Like

I’ve also been struggling a lot with this. Let’s say I want the docstring for a basic function like let, when trying ,describe I get.

scheme@(guile-user)> ,describe let
While executing meta-command:
Syntax error:
unknown file:42:10: let: bad let in form let

I’m working in emacs with Geiser if that helps. I can run (+lookup/documentation IDENTIFIER &optional ARG) on let, but the result isn’t helpful

(let ...)

A macro in module (guile).

Thanks for the help!

1 Like

let is not a function, it’s syntax. ,describe is for function docstrings so that’s why you get an error. Documentation for syntax at the REPL could be better, but that’s the current state of things.

1 Like

Is there a simple way to tell what parts of scheme are syntax and which are functions in scheme? I tried to check online, but couldn’t find a clear answer.

To try again with the display function (I think/hope it’s a function?) I get the result,

scheme@(guile-user) [2]> ,describe display
#f

Though I do get a reasonable result with null? for example

scheme@(guile-user) [2]> ,describe null?
- Scheme Procedure: null? x
     Return `#t' iff X is the empty list, else `#f'.

Is it just a matter of docstrings not being available for some functions, and do you think this would be a reasonable place to try and contribute if so? Would it also be possible to contribute docstrings for syntax, or is that not a thing?

Thanks again for the assistance.

1 Like

I think that you’re expected to memorize the built-in forms like let and define. Anyway to find out what a particular name refers to you can use: https://www.gnu.org/software/guile/manual/guile.html#Procedure-Index-1

Since Guile is based on R5RS you can look there for the list of special forms - Revised^5 Report on the Algorithmic Language Scheme

1 Like