Visitar URL original
hie-bios only loads lazily components other than main by default · Issue #38 · well-typed/haskell-debugger · GitHub
Skip to content

hie-bios only loads lazily components other than main by default #38

Closed

Description

@alt-romes

This is a bad default for the debugger (even though it is a good default for HLS?).
It's surprising that

cradle:
  cabal:
     - path: "./"
        component: "lib:hegg"
     - path: "./test/Test.hs"
        component: "test:hegg-test"

doesn't work, but if we instead force hie-bios to load all units eagerly:

cradle:
  cabal:
    component: "all"

it will.

Note that implicit-hie comes up with the former by default. So, by default, a user cannot set a breakpoint in a non-main unit.

  • Duplicate test which sets breakpoint on internal unit but remove the hie.yaml file in it and make sure it still works.

Activity

  1. added this to the 0.6.0 milestone on Sep 1, 2025
  2. alt-romes commented on Sep 1, 2025

    @alt-romes
    CollaboratorAuthor

    Since this requires cross cutting changes, it's likely impossible to make for 0.6.0/the MuniHac talk.

    It seems we'll have to resort to cradle: cabal: component: "all" for now

  3. added theissue type on Sep 3, 2025
  4. removed this from the 0.6.0 milestone on Sep 4, 2025
  5. Saizan commented on May 7, 2026

    @Saizan
    Collaborator

    It's not so much that the components are loaded lazily, but that only a subset of the components is loaded (depending on the LoadStyle) and any it depends on are pre-compiled and installed in a package-db in hie-bios's cache.

    hie-bios expects to be told all other relevant files in the LoadContext, and will give us multiple units at once if we do so.
    The problem is that we don't yet know what those files will be, because we ask for the flags at the very beginning. Even trying to delay that call we'd be hard pressed to figure out the file for a function breakpoint without loading the session first.

    Shall we add a new LoadStyle to hie-bios so that we can ask to load all components in a multi-cradle at once? Should be possible for multis that are all components in a cabal project, not so obvious otherwise.
    (Often this will be the same as loading an "all" component, but could be a strict subset of them, if the hie.yaml doesn't cover them all).

  6. fendor commented on May 12, 2026

    @fendor
    Collaborator

    Shall we add a new LoadStyle to hie-bios so that we can ask to load all components in a multi-cradle at once?

    Yes, let's do that!

    Current hie-bios design

    Let's briefly think about the current state and then come up with a long term solution.

    There are currently two LoadStyles supported by hie-bios

    1. LoadFile: Load the component for the given FilePath
      • This is the default, and we should consider this broken as it doesn't satisfy the home unit closure property when multiple components are involved.
    2. LoadWithContext: Load all components for which we have FilePaths
      • This ensures the home unit closure property is satisfied but is also "lazy", e.g., reloads whenever new components are "discovered"
      • If the cradle can't load multiple files at once, we fall back to LoadFile mode

    Both these modes have an additional 'Maybe Component' name, which can be used instead of the file path to indicate what to load exactly.
    This mode is used by stack or cradles written like:

    cradle:
      cabal:
        component: "lib:hie-bios"

    We need this, as some tools can't figure out FilePath -> Component (in particular stack and cabal < 3.2).

    This also allows us to trick hie-bios into loading multiple components eagerly via:

    cradle:
      cabal:
        component: "all"

    Paired with LoadWithContext, this implements an eager component loading of the whole project for cabal only (ignoring benchmarks, tests and executables).

    This result in around 5 different ways of setting up the session which feels non-perfect, however, it is important to note that this simply implements the greatest common denominator, allowing us to support stack, cabal and everything else.

    New hie-bios design

    Ok, so how can we improve this?

    Form a bird's eye view, I think we want the following ways of loading a hie-bios session:

    1. Lazily load a single file or component with context
      • Two major modes:
        1. Legacy mode: ignore context
          • simplest interface, cradle impls should avoid this mode
          • Essentially equivalent to: Maybe Component -> FilePath -> IO ComponentOptions
        2. Use context:
          • Cabal invocation: cabal repl --enable-multi-repl
          • Not supported by: stack, bios, direct (direct is always assumed to be a single module)
          • Can gracefully fall back to Legacy Mode if not supported by cradle
      • Lazily means we don't try to load all units in the project but only units that have a filepath witness.
      • HLS loads components on demand
      • Haskell-debugger cannot load components on demand, so this is virtually broken, except for simple cases.
    2. Eagerly load all specified components
      • Equivalent to [Component] -> IO ComponentOptions
      • Eagerly means we load all components in a cradle when the first witness is opened.
      • Cabal invocation: cabal repl --enable-multi-repl component1 component2 ... componentN
      • HLS loads the given components exactly once
      • haskell-debugger loads the given components exactly once
      • Fallback:
        • Fall back to (1) by calling it with the given filepath witnesses.
    3. Eagerly load project
      • Equivalent to IO ComponentOptions
      • Eagerly means we load all components in a cradle when the first witness is opened.
      • Fallback:
        • Fall back to (1) by calling it with the given filepath witnesses.

    This means there are still 4 different ways of loading a project, where only cabal Cradles currently support all session initialisation things.
    bios could be easily thaught about it as well, but stack is very tricky.

    The final API is:

    type Component = String
    
    -- | Always needed for graceful fallback to (1.1) and (1.2)
    data TargetWithContext = TargetWithContext 
        { targetFilePath :: FilePath
        , targetContext :: [String]
        }
    
    data CradleLoadMode
        = LoadFile
        | LoadFileTargetWithContext
        | LoadUnitsInferred
        | LoadUnitsFromCradle
    
    getCompilerOptions :: TargetWithContext -> CradleLoadMode -> IO ComponentOptions

    We ignore details such as logging, working directory and error handling.

    This design mostly affects cabal and perhaps the bios cradle. Stack cradle is unaffected right now.

    Cabal examples

    Given no hie.yaml, and LoadAllEagerly, this is equivalent to:

    cabal repl ... all <filepath> --enable-tests --enable-benchmarks
    

    -- hie.yaml

    cradle:
      cabal:
        cabalProject: "./cabal.project.dev"
    cabal --project-file ./cabal.project.dev repl ... all <filepath>
    

    -- hie.yaml

    cradle:
      cabal:
        cabalProject: "./cabal.project.dev"
        components:
        - path: "./src"
          component: "lib:hie-bios"
        - path: "./exe"
          component: "exe:hie-bios"
        - path: "./tests/BiosTests.hs"
          component: "test:hie-bios"
        - path: "./tests/ParserTests.hs"
          component: "test:parser-tests"
    cabal repl ... lib:hie-bios exe:hie-bios test:hie-bios test:parser-tests
    

    -- hie.yaml

    cradle:
      cabal:
        component: "lib:hie-bios"
    cabal repl ... lib:hie-bios
    
  7. added a commit that references this issue on Jul 2, 2026
    dc399dd
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions