<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://glean.software/blog</id>
    <title>Glean Blog</title>
    <updated>2026-02-06T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://glean.software/blog"/>
    <subtitle>Glean Blog</subtitle>
    <icon>https://glean.software/img/favicon.ico</icon>
    <entry>
        <title type="html"><![CDATA[Glean Release 0.2]]></title>
        <id>https://glean.software/blog/release-0-2</id>
        <link href="https://glean.software/blog/release-0-2"/>
        <updated>2026-02-06T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[I just uploaded]]></summary>
        <content type="html"><![CDATA[<p>I just uploaded
<a href="https://hackage.haskell.org/package/glean-0.2.0.0" target="_blank" rel="noopener noreferrer">glean-0.2.0.0</a> to
Hackage, along with releases of the Haskell Thrift compiler and other
dependencies.</p><p>Since version 0.1.0.0, Glean has been installable using plain <code>cabal
install</code> which vastly improves on the previous complex build
process. For full details, see <a href="https://glean.software/docs/building/" target="_blank" rel="noopener noreferrer">Building Glean From
Source</a>, but to summarise: on a
recent Linux distro, with GHC 9.2-9.6 and cabal 3.6+, install some
prerequisite system packages (listed in the building docs above), and
then just</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">cabal install glean</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>The build takes a while, partly because one of the dependencies is a
cabal-packaged copy of the "folly" C++ library
(<a href="https://hackage.haskell.org/package/folly-clib" target="_blank" rel="noopener noreferrer">folly-clib</a>) and
<a href="https://github.com/haskell/cabal/issues/7127" target="_blank" rel="noopener noreferrer">cabal doesn't currently build C++ files in
parallel</a>.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="changes-in-0200">Changes in 0.2.0.0<a href="#changes-in-0200" class="hash-link" aria-label="Direct link to Changes in 0.2.0.0" title="Direct link to Changes in 0.2.0.0">​</a></h2><p>Some pretty big things have landed:</p><ul><li><p>Glean now comes with a generic LSP server,
<a href="https://hackage.haskell.org/package/glean-lsp" target="_blank" rel="noopener noreferrer">glean-lsp</a>, which
supports common IDE operations like go-to-definition,
go-to-references, hover documentation, and symbol search. This means
you can index a software project with Glean and then browse it using
VS Code (for example). I'll give a couple of worked examples below
showing step-by-step how to do this for some real world codebases.</p></li><li><p>Glean has a new experimental DB backend based on
<a href="https://www.symas.com/mdb" target="_blank" rel="noopener noreferrer">LMDB</a>. LMDB is much smaller and simpler
than RocksDB, and in most of our benchmarks it performed around
30-40% better. We're still investigating some performance issues
encountered with very large indexing jobs, though. Currently it's
still not possible to build Glean without the RockDB dependency,
but we do intend to fix this in the future.</p></li><li><p>Added a new Haskell indexer that consumes <code>.hie</code> files directly, and
collects much richer data than the old indexer - in particular it
indexes local variables and collects type information for all
variable occurrences, which appears on hover with <code>glean-lsp</code> and VS
Code.</p></li><li><p>We're now also releasing the C++ indexer as a Cabal package along
with Glean:
<a href="https://hackage.haskell.org/package/glean-clang" target="_blank" rel="noopener noreferrer">glean-clang</a>, so
Glean can be used to index C++ projects out of the box.</p></li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="examples">Examples<a href="#examples" class="hash-link" aria-label="Direct link to Examples" title="Direct link to Examples">​</a></h2><p>Here are a couple of things you can play with, once you've <a href="https://glean.software/docs/building/" target="_blank" rel="noopener noreferrer">built and
installed Glean</a>.</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="index-llvm--clang-and-browse-it-in-vs-code">Index LLVM + Clang and browse it in VS Code<a href="#index-llvm--clang-and-browse-it-in-vs-code" class="hash-link" aria-label="Direct link to Index LLVM + Clang and browse it in VS Code" title="Direct link to Index LLVM + Clang and browse it in VS Code">​</a></h3><p>Clone an LLVM source tree:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">git clone https://github.com/llvm/llvm-project.git</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Configure, including Clang. This step also produces the
<code>compile_commands.json</code> file that Glean will later use during
indexing:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">cd llvm-project/llvm</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">mkdir build &amp;&amp; cd build</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DCMAKE_BUILD_TYPE=Debug -DLLVM_ENABLE_PROJECTS=clang ..</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Next, build LLVM. This step is required because LLVM includes a lot of
generated code which is produced as part of the build process, so to
index the source files we need to ensure all the generated code has
been built first.</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">cmake --build . -j12</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Go and get a coffee. Or two. (beware, even with 32GB this tends to OOM
my laptop, so you might want to reduce <code>-j12</code> to something
lower). Next, we can index the project using Glean's C++
indexer.</p><p>If you haven't already install Glean's C++ indexer, do that:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">cabal install glean-clang</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Next we'll run the indexer. We'll store the resulting DB in
<code>llvm-project/gleandb</code> for now.</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">cd ..</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">glean --db-root gleandb index cpp-cmake --db llvm/1 --cdb-dir "$(pwd)/llvm/build" . -j12</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Go and get another coffee... this is essentially running the compiler
over all the C++ code again. It <em>should</em> need no more than 16GB or so
with 12 indexer processes running in parallel.</p><p>Note that you need to do this from the <code>llvm-project</code> directory, this
ensures that the filenames in the Glean DB will be relative to that
directory which is what <code>glean-lsp</code> expects. (Storing the data under
the wrong filenames is the most common cause of things not working
when we connect up the full IDE/LSP/Glean stack).</p><p>Next we need to set up VS Code and <code>glean-lsp</code>. There are full
instructions for <code>glean-lsp</code> in its <a href="https://github.com/facebookincubator/Glean/tree/master/lsp/README.md">README</a>, but here's specifically how
to set it up for LLVM using the index we just created.</p><p>First install <code>glean-lsp</code> if you haven't already:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">cabal install glean-lsp</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>To use this LSP server with VS Code, you need a generic LSP client
such as <a href="https://github.com/zsol/vscode-glspc" target="_blank" rel="noopener noreferrer">Generic LSP Client
(v2)</a>. Install that extension in
VS Code, and then create <code>llvm-project/.vscode/settings.json</code>:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">[</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    "glean-lsp": {</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        "repo": "llvm"</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    },</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    "glspc.server.command": "glean-lsp"</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    "glspc.server.commandArguments": ["--db-root", "gleandb"],</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    "glspc.server.languageId": [</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">      "cpp", "c"</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    ]</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Now in VS Code, "Open Folder" and select the <code>llvm-project</code> folder. If
you have another C++ extension installed, it probably makes sense to
disable it for this folder, otherwise you'll see responses from both
extensions for things like go-to-definition.</p><p>Open a source file,
e.g. <code>llvm-project/clang/include/clang/AST/Decl.h</code>.  You should have
code navigation features available: holding down Ctrl while moving the
mouse around should underline identifiers, and clicking on an
identifier should jump to its definition. You should be able to
right-click "Go to References" on a definition to find references
throughout the whole LLVM + Clang tree instantly, and <code>Ctrl+T</code> for
symbol search should work. Hovering the mouse over an identifier
should show its type.</p><p>If things aren't working, then the first place to look for problems is in
the output window for the Generic LSP Client: show the Output window,
and then select Generic LSP Client from the dropdown on the right.</p><p>You can also open the DB in Glean's shell to check that it looks right:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">glean shell --db-root llvm-project/gleandb --db llvm</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Try e.g. <code>:stat</code> to see the contents, and try <code>src.File _</code> to show
known source files.</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="download-a-db-of-stackage-and-try-some-queries">Download a DB of Stackage and try some queries<a href="#download-a-db-of-stackage-and-try-some-queries" class="hash-link" aria-label="Direct link to Download a DB of Stackage and try some queries" title="Direct link to Download a DB of Stackage and try some queries">​</a></h3><p>You can <a href="https://drive.google.com/file/d/1rixJ6VCl6OlsECGnSjsH6IlXxFUU_msD/view?usp=sharing" target="_blank" rel="noopener noreferrer">download a DB of Stackage
21.21</a>
and try some queries. This DB was produced by building ~3000 packages
in Stackage 21.21 and then producing a Glean DB from the <code>.hie</code> files;
for more details see <a href="https://simonmar.github.io/posts/2025-05-22-Glean-Haskell.html" target="_blank" rel="noopener noreferrer">Indexing Hackage: Glean
vs. hiedb</a>.</p><p>Unpack the DB:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">mkdir /tmp/glean &amp;&amp; tar xf glean-stackage-21.21.tar -C /tmp/glean</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>and start the Glean shell:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">$ glean shell --db-root /tmp/glean</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">Glean Shell, built on 2025-07-14 13:39:35.711312749 UTC, from rev &lt;unknown&gt;</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">Using local DBs from rocksdb:/tmp/glean</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">type :help for help.</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">&gt; </span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Load the DB:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">&gt; :db stackage/1</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">stackage&gt;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Let's see what's in it:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">stackage&gt; :stat</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">hs.ClassDecl.3</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  count: 6503</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  size:  350074 (341.87 kiB) 0.0309%</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">hs.ConstrDecl.3</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  count: 89371</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  size:  4048652 (3.86 MiB) 0.3569%</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">hs.DataDecl.3</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  count: 40711</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  size:  2017999 (1.92 MiB) 0.1779%</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">...</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">Total: 21735709 facts (1.06 GiB)</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Let's find the class declaration for <code>Hashable</code>. First we have to find
its name:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">stackage&gt; hs.Name { occ = { name = "Hashable" }}</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">{</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  "id": 11500325,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  "key": {</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    "occ": { "id": 11923, "key": { "name": "Hashable", "namespace_": 3 } },</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    "mod": {</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">      "id": 733072,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">      "key": {</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        "name": { "id": 733071, "key": "Language.Preprocessor.Cpphs.SymTab" },</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        "unit": { "id": 560159, "key": "cpphs-1.20.9.1-inplace" }</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">      }</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    },</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    "sort": { "external": { } }</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">}</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">...</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">5 results, 20 facts, 7.40ms, 316816 bytes, 914 compiled bytes</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>We got 5 results, and only one of them was the one we wanted. So let's
restrict the query to find only results in the <code>hashable</code> package:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">stackage&gt; hs.Name { occ = { name = "Hashable" }, mod = { unit = "hashable".. }}</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">{</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  "id": 11924,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  "key": {</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    "occ": { "id": 11923, "key": { "name": "Hashable", "namespace_": 3 } },</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    "mod": {</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">      "id": 11922,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">      "key": {</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        "name": { "id": 11920, "key": "Data.Hashable.Class" },</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        "unit": { "id": 11921, "key": "hashable-1.4.3.0-inplace" }</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">      }</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    },</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    "sort": { "external": { } }</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">}</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">1 results, 5 facts, 1.18ms, 353848 bytes, 1489 compiled bytes</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>OK, now let's find the class declaration:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">stackage&gt; hs.ClassDecl { name = { occ = { name = "Hashable" }, mod = { unit = "hashable".. }}}</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">{</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  "id": 19072033,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  "key": {</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    "name": {</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">      "id": 11924,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">      "key": {</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        "occ": { "id": 11923, "key": { "name": "Hashable", "namespace_": 3 } },</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        "mod": {</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">          "id": 11922,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">          "key": {</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">            "name": { "id": 11920, "key": "Data.Hashable.Class" },</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">            "unit": { "id": 11921, "key": "hashable-1.4.3.0-inplace" }</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">          }</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        },</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        "sort": { "external": { } }</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">      }</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    },</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    "methods": [</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">      {</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">...</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">}</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">1 results, 15 facts, 6.45ms, 514328 bytes, 1777 compiled bytes</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Let's find the method names of the class:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">stackage&gt; (C.methods[..]).name.occ.name where C = hs.ClassDecl { name = { occ = { name = "Hashable" }, mod = { unit = "hashable".. }}}</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">{ "id": 21736733, "key": "hashWithSalt" }</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">{ "id": 21736734, "key": "hash" }</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>And finally, let's see how many instances in Stackage 21.21 provide a
definition of <code>hashWithSalt</code>:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">stackage&gt; :count I where B = hs.InstanceBind { name = { occ = { name = "hashWithSalt" }, mod = { unit = "hashable".. }}}; hs.InstanceBindToDecl { bind = B, decl = { inst = I }}; </span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">267 results, 267 facts, 26.40ms, 644736 bytes, 2462 compiled bytes</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>To see where these instance declarations are:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">stackage&gt; I.loc where B = hs.InstanceBind { name = { occ = { name = "hashWithSalt" }, mod = { unit = "hashable".. }}}; hs.InstanceBindToDecl { bind = B, decl = { inst = I }}</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">{</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  "id": 21736733,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  "key": {</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    "file": { "id": 3982208, "key": "text-latin1-0.3.1/src/Text/Latin1.hs" },</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    "span": { "start": 2662, "length": 157 }</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">}</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">{</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  "id": 21736734,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  "key": {</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    "file": { "id": 11054592, "key": "shake-0.19.7/src/General/Thread.hs" },</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    "span": { "start": 526, "length": 87 }</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">}</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">{</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  "id": 21736735,</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  "key": {</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    "file": { "id": 5786370, "key": "strict-tuple-0.1.5.3/src/Data/Tuple/Strict/T6.hs" },</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    "span": { "start": 1887, "length": 265 }</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">}</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">...</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>There are also some example queries in an <a href="https://simonmar.github.io/posts/2025-05-22-Glean-Haskell.html" target="_blank" rel="noopener noreferrer">earlier blog
post</a> (however, the</p><a href="https://github.com/facebookincubator/Glean/tree/master/glean/schema/source/hs.angle">schema</a>for Haskell has changed in a few ways since that post so some of the queries might not work exactly as written).<h3 class="anchor anchorWithStickyNavbar_LWe7" id="index-your-own-haskell-code">Index your own Haskell code<a href="#index-your-own-haskell-code" class="hash-link" aria-label="Direct link to Index your own Haskell code" title="Direct link to Index your own Haskell code">​</a></h3><p>To index the code of a Cabal package, add the following to your <code>cabal.project</code>:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">package *</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    ghc-options:</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        -fwrite-ide-info</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        -hiedir .hiefiles</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Then</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">$ cabal build</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">$ glean index haskell-hie --db-root /tmp/glean --db mydb/1 .hiefiles</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>and then you can query the new DB in the shell:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">$ glean shell --db-root /tmp/glean --db mydb/1</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_LWe7" id="run-a-glass-server-and-make-some-simple-queries">Run a Glass server and make some simple queries<a href="#run-a-glass-server-and-make-some-simple-queries" class="hash-link" aria-label="Direct link to Run a Glass server and make some simple queries" title="Direct link to Run a Glass server and make some simple queries">​</a></h3><p>Glass is a "symbol server", it provides a higher-level interface to
the Glean data, with operations like <code>documentSymbols</code> for finding all
the symbols in a file, and <code>findReferences</code> for finding all the
references to a symbol. I used Glass to connect VS Code to Glean in
the <a href="https://simonmar.github.io/posts/2025-06-11-Glean-stackage-vscode.html" target="_blank" rel="noopener noreferrer">previous blog
post</a>.</p><p>Glass makes requests to a Glean server, so we need to start both
<code>glean-server</code> and <code>glass-server</code>, like this:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">$ glean-server --db-root /tmp/glean --port 12345</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>and in another terminal:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">$ glass-server --service localhost:12345 --port 12346</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>then we can make requests using <code>glean-democlient</code>, for example to
list the symbols in the file <code>src/Data/Aeson.hs</code> in the <code>aeson-2.1.2.1</code> package:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">$ glass-democlient --service localhost:12346 list stackage/aeson-2.1.2.1/src/Data/Aeson.hs</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">stackage/hs/aeson/Data/Aeson/var/eitherDecodeFileStrict</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">stackage/hs/aeson/Data/Aeson/var/eitherDecodeFileStrict%27</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">stackage/hs/aeson/Data/Aeson/var/eitherDecodeStrict</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">stackage/hs/aeson/Data/Aeson/var/fp/4335/2</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">stackage/hs/aeson/Data/Aeson/var/eitherDecodeStrict%27</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">stackage/hs/aeson/Data/Aeson/tyvar/a/6101/50</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">stackage/hs/aeson/Data/Aeson/tyvar/a/6563/56</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">stackage/hs/aeson/Data/Aeson/var/encodeFile</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">stackage/hs/aeson/Data/Aeson/tyvar/a/7047/61</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">...</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Each of those symbols is a "Symbol ID", which is a string that
uniquely identifies a particular symbol to Glass. Using the Symbol ID
we can find all the references to a symbol:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>]]></content>
        <author>
            <name>Simon Marlow</name>
            <uri>https://simonmar.github.io/</uri>
        </author>
        <category label="glean" term="glean"/>
        <category label="release" term="release"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Incremental indexing with Glean]]></title>
        <id>https://glean.software/blog/incremental</id>
        <link href="https://glean.software/blog/incremental"/>
        <updated>2022-12-01T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[This post describes how Glean supports incremental indexing of]]></summary>
        <content type="html"><![CDATA[<p>  This post describes how Glean supports <em>incremental indexing</em> of
source code, to maintain an up-to-date index of a large repository
using minimal resources. This is an overview of the problems and
some aspects of how we solved them; for the technical details of the
implementation see <a href="/docs/implementation/incrementality/">Implementation Notes:
Incrementality</a>.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="background">Background<a href="#background" class="hash-link" aria-label="Direct link to Background" title="Direct link to Background">​</a></h2><p>Indexing a large amount of source code can take a long time. It's not
uncommon for very large indexing jobs to take multple
hours. Furthermore, a monolithic indexing job produces a large DB that
can be slow to ship around, for example to replicate across a fleet of
Glean servers.</p><p>Source code changes often, and we would like to keep the index up to
date, but redoing the monolithic indexing job for every change is out
of the question. Instead what we would like to do is to update a
monolithic index with the <em>changes</em> between the base revision and the
current revision, which should hopefully be faster than a full
repository indexing job because we only have to look at the things
that have changed. The goal is to be able to index the changes in
<em>O(changes)</em> rather than <em>O(repository)</em>, or as close to that as we
can get.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="how-incrementality-works">How incrementality works<a href="#how-incrementality-works" class="hash-link" aria-label="Direct link to How incrementality works" title="Direct link to How incrementality works">​</a></h2><p>To produce a modified DB, we hide a portion of the data in the
original DB, and then stack a DB with the new data on top. Like this:</p><p><img loading="lazy" alt="Incremental stack" src="/assets/images/stack-f1841961d75a1c2d4d1338f3d18f0179.jpg" width="569" height="458" class="img_ev3q"></p><p>The user asks for the DB "new", and they can then work with the data
in exactly the same way as they would for a single DB. The fact that
the DB is a stack is invisible to a user making queries. Furthermore,
the DB "old" still exists and can be used simultaneously, giving us
access to multiple versions of the DB at the same time. We can even
have many different versions of "new", each replacing a different
portion of "old".</p><p>All of the interesting stuff is in how we hide part of the data in the
old DB.  How do we hide part of the data?</p><p>When facts are added to a Glean DB, the producer of the facts can
label facts with a <em>unit</em>. A unit is just a string; Glean doesn't impose
any particular meaning on units so the indexer can use whatever
convention it likes, but typically a unit might be a filename or
module name. For example, when indexing a file F, the indexer would
label all the facts it produces with the unit F.</p><p>To hide some of the data in a DB, we specify which units to exclude
from the base DB, like this:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">glean create --repo &lt;new&gt; --incremental &lt;old&gt; --exclude A,B,C</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>would create a DB <code>&lt;new&gt;</code> that stacks on top of <code>&lt;old&gt;</code>, hiding units A, B
and C.</p><p>So to index some code incrementally, we would first decide which files
need to be reindexed, create an incremental DB that hides those files
from the base DB and then add the new facts.</p><p>To implement hiding correctly, Glean has to remember which facts are
owned by which units. But it's not quite that simple, because facts
can refer to each other, and the DB contents must be valid (which has
a formal definition but informally means "no dangling
references"). For example, if we have facts <strong>x</strong> and <strong>y</strong>, where <strong>x</strong> refers to
<strong>y</strong>:</p><p><img loading="lazy" alt="Fact dependency" src="/assets/images/factdep1-59c2c6ff2bb70cadfffaa8ed1c3c725c.jpg" width="380" height="229" class="img_ev3q"></p><p>and we hide unit B, then <strong>y</strong> must still be visible, otherwise the
reference from <strong>x</strong> would be dangling and the DB would not be valid.</p><p>So after the indexer has finished producing facts, Glean propagates
all the units through the graph of facts, resulting in a mapping from
facts to <strong>ownership sets</strong>.</p><p><img loading="lazy" alt="Fact dependency with ownership sets" src="/assets/images/factdep2-ee89a8cde4e367c8d2e99fde1b9553a0.jpg" width="389" height="227" class="img_ev3q"></p><p>It turns out that while there are lots of facts, there are relatively
few distinct ownership sets. Furthermore, facts produced together tend
to have the same ownership set, and we can use this to store the
mapping from facts to ownership sets efficiently. To summarise:</p><ul><li>Ownership sets are assigned unique IDs and stored in the DB using <a href="https://github.com/facebook/folly/blob/main/folly/experimental/EliasFanoCoding.h" target="_blank" rel="noopener noreferrer">Elias Fano Coding</a></li><li>The mapping from facts to ownership sets is
stored as an interval map</li></ul><p>As a result, keeping all this information only adds about 7% to the DB
size.</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="what-about-derived-facts">What about derived facts?<a href="#what-about-derived-facts" class="hash-link" aria-label="Direct link to What about derived facts?" title="Direct link to What about derived facts?">​</a></h3><p>Derived facts must also have ownership sets, because we have to know
which derived facts to hide. When is a derived fact visible? When all
of the facts that it was derived from are visible.</p><p>For example, if we have a fact <strong>r</strong> that was derived from <strong>p</strong> and <strong>q</strong>:</p><p><img loading="lazy" alt="Derived fact" src="/assets/images/derived-e46b2a6e3925959133e37b75619495a6.jpg" width="881" height="268" class="img_ev3q"></p><p>The ownership set of <strong>r</strong> is <code>{ P } &amp; { Q }</code>, indicating that it should be
visible if both P and Q are visible. Note that facts might be derived
from other derived facts, so these ownership expressions can get
arbitrarily large. Normalising them to disjunctive normal form would
be possible, but we haven't found that to be necessary so far.</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="performance">Performance<a href="#performance" class="hash-link" aria-label="Direct link to Performance" title="Direct link to Performance">​</a></h3><p>There are three aspects to performance:</p><ul><li><strong>Indexing performance</strong>. We measured the impact of computing
ownership at indexing time to be 2-3% for Python, for Hack it was in
the noise, and we don't
expect other languages to be significantly different.</li><li><strong>Query performance</strong>. Initially query performance for an
incremental stack was much slower because we have to calculate the visibility of every fact discovered during a query. However, with some <a href="/docs/implementation/incrementality/#caching-fact-ownership">caching optimisations</a> we were able to get the overhead to less than 10% for "typical" queries, of the kind that Glass does. Queries that do a lot of searching may be impacted by around 3x, but these are not typically found in production use cases.</li><li><strong>Incremental derivation performance</strong>. We would like derivation in the incremental DB to take time proportional to the number of facts in the increment. We implemented incremental derivation for some kinds of query; optimising queries to achieve this in general is a hard problem that we'll return to probably next year.</li></ul><h3 class="anchor anchorWithStickyNavbar_LWe7" id="stacked-incremental-dbs">Stacked incremental DBs<a href="#stacked-incremental-dbs" class="hash-link" aria-label="Direct link to Stacked incremental DBs" title="Direct link to Stacked incremental DBs">​</a></h3><p>So far we have only been considering how to stack a single increment
on top of a base DB. What if we want to create deeper stacks?</p><p><img loading="lazy" alt="Stacked incremental database" src="/assets/images/stacked-incremental-a591470e7df3a05595e75f76fad1a7ac.jpg" width="564" height="633" class="img_ev3q"></p><p>The DB "newer" stacks on top of "new", and hides some more units. So
there are now portions of both "new" and "old" that need to be hidden
(the darker grey boxes), in addition to the original portion of "old"
that we hid (light grey box).</p><p>As before, we might have multiple versions of "newer" stacked on top
of the same "new", and in general these DB stacks form a tree. All the
intermediate nodes of the tree are usable simultaneously: no data is
being modified, only shared and viewed differently depending on which
node we choose as the top of our stack.</p><p>One interesting aspect that arises when we consider how to track
ownership of facts in this model is fact dependencies across the
boundaries between DBs.  For instance, suppose we have</p><p><img loading="lazy" alt="Stacked dependency" src="/assets/images/stacked-dependency-37bdf977436a2c7f69570eff127cb537.jpg" width="612" height="323" class="img_ev3q"></p><p>If we hide B, then considering the ownership data for "old" alone
would tell us that y is invisible. But we must make it visible,
because x depends on it. So when computing the ownership data for
"new", we have to transitively propagate ownership to facts in the
base DB(s), store that in "new", and consult it when deciding which
facts in "old" are visible.</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="derived-facts-in-stacked-incremental-dbs">Derived facts in stacked incremental DBs<a href="#derived-facts-in-stacked-incremental-dbs" class="hash-link" aria-label="Direct link to Derived facts in stacked incremental DBs" title="Direct link to Derived facts in stacked incremental DBs">​</a></h3><p>A fact might be derived from multiple facts in different DBs in the
stack, and we have to represent its ownership correctly. Therefore</p><ul><li>There must be a single namespace of ownership sets for the whole DB stack. That is, stacked DBs add more sets. (or else we copy ownership sets from the base DB, which doesn't seem attractive).</li><li>Since a fact may be owned multiple different ways (see previous section) we have to take this into account when computing the ownership expression for a derived fact.</li></ul><p>This is the most complex diagram in the post (phew!):</p><p><img loading="lazy" alt="Stacked derived" src="/assets/images/stacked-derived-fc2ba3ed856040135fc526e8a7ff6391.jpg" width="804" height="446" class="img_ev3q"></p><p>Here the dashed arrow means "derived from" and the solid arrow means
"refers to".</p><p>The fact <strong>d</strong> should be visible if both <strong>x</strong> and <strong>y</strong> are visible. The
ownership of <strong>x</strong> is <code>{A}</code> and <strong>y</strong> is <code>{B,C}</code> (because it is referred to from <strong>z</strong>
which has owner B), so the final owner of <strong>d</strong> is <code>{A} &amp;&amp; {B,C}</code>.</p><p>Tracking all this shouldn't be too expensive, but it's tricky to get
right!</p>]]></content>
        <author>
            <name>Simon Marlow</name>
            <uri>https://simonmar.github.io/</uri>
        </author>
        <category label="glean" term="glean"/>
        <category label="incremental" term="incremental"/>
    </entry>
</feed>