Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 8614609
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 12, 20262026-06-12T05:09:38+00:00 2026-06-12T05:09:38+00:00

Haskell Stack Overflow layout preprocessor module StackOverflow where — yes, the source of this

  • 0

Haskell Stack Overflow layout preprocessor

module StackOverflow where  -- yes, the source of this post compiles as is

Skip down to What to do to get it working if you want to play with this first (1/2 way down).
Skip down to What I would like if I witter on a bit and you just want to find out what help I’m seeking.

TLDR Question summary:

  1. Can I get ghci to add filename completion to the :so command I defined in my ghci.conf?
  2. Could I somehow define a ghci command that returns code for compilation instead of returning a ghci command, or
    does ghci instead have a better way for me to plug in Haskell code as a
    file-extension-specific pre-processor, so :l would work for .hs and .lhs files as usual, but use my handwritten preprocessor for .so files?

Background:

Haskell supports literate programming in .lhs source files, two ways:

  • LaTeX style \begin{code} and \end{code}.
  • Bird tracks: Code starts with > , anything else is a comment.
    There must be a blank line between code and comments (to stop trivial accidental misuse of >).

Don’t Bird tracks rules sound similar to StackOverflow’s code blocks?

References: 1. The .ghci manual
2. GHCi haskellwiki
3. Neil Mitchell blogs about :{ and :} in .ghci

The preprocessor

I like writing SO answers in a text editor, and I like to make a post that consists of code that works,
but end up with comment blocks or >s that I have to edit out before posting, which is less fun.

So, I wrote myself a pre-processor.

  • If I’ve pasted some ghci stuff in as a code block, it usually starts with * or :.
  • If the line is completely blank, I don’t want it treated as code, because otherwise
    I get accidental code-next-to-comment-line errors because I can’t see the 4 spaces I accidentally
    left on an otherwise blank line.
  • If the preceeding line was not code, this line shouldn’t be either, so we can cope with StackOverflow’s
    use of indentation for text layout purposes outside code blocks.

At first we don’t know (I don’t know) whether this line is code or text:

dunnoNow :: [String] -> [String]
dunnoNow [] = []
dunnoNow (line:lines)
  | all (==' ') line = line:dunnoNow lines     -- next line could be either
  | otherwise = let (first4,therest) = splitAt 4 line in 
     if first4 /="    "                 -- 
        || null therest                 -- so the next line won't ever crash
        || head therest `elem` "*:"     -- special chars that don't start lines of code.
     then line:knowNow False lines      -- this isn't code, so the next line isn't either
     else ('>':line):knowNow True lines -- this is code, add > and the next line has to be too

but if we know, we should keep in the same mode until we hit a blank line:

knowNow :: Bool -> [String] -> [String]
knowNow _ [] = []
knowNow itsCode (line:lines) 
  | all (==' ') line = line:dunnoNow lines
  | otherwise = (if itsCode then '>':line else line):knowNow itsCode lines

Getting ghci to use the preprocessor

Now we can take a module name, preprocess that file, and tell ghci to load it:

loadso :: String -> IO String
loadso fn = fmap (unlines.dunnoNow.lines) (readFile $ fn++".so") -- so2bird each line
        >>= writeFile (fn++"_so.lhs")                     -- write to a new file
        >> return (":def! rso (\\_ -> return \":so "++ fn ++"\")\n:load "++fn++"_so.lhs")

I’ve used silently redefining the :rso command becuase my previous attemts to use
let currentStackOverflowFile = .... or currentStackOverflowFile <- return ...
didn’t get me anywhere.

What to do to get it working

Now I need to put it in my ghci.conf file, i.e. in appdata/ghc/ghci.conf
as per the instructions

:{
let dunnoNow [] = []
    dunnoNow (line:lines)
      | all (==' ') line = line:dunnoNow lines     -- next line could be either
      | otherwise = let (first4,therest) = splitAt 4 line in 
         if first4 /="    "                 -- 
            || null therest                 -- so the next line won't ever crash
            || head therest `elem` "*:"     -- special chars that don't start lines of code.
         then line:knowNow False lines      -- this isn't code, so the next line isn't either
         else ('>':line):knowNow True lines -- this is code, add > and the next line has to be too
    knowNow _ [] = []
    knowNow itsCode (line:lines) 
      | all (==' ') line = line:dunnoNow lines
      | otherwise = (if itsCode then '>':line else line):knowNow itsCode lines
    loadso fn = fmap (unlines.dunnoNow.lines) (readFile $ fn++".so") -- convert each line
        >>= writeFile (fn++"_so.lhs")                            -- write to a new file
        >> return (":def! rso (\\_ -> return \":so "++ fn ++"\")\n:load "++fn++"_so.lhs")
:}
:def so loadso

Usage

Now I can save this entire post in LiterateSo.so and do lovely things in ghci like

*Prelude> :so StackOverflow
[1 of 1] Compiling StackOverflow    ( StackOverflow_so.lhs, interpreted )
Ok, modules loaded: StackOverflow.

*StackOverflow> :rso
[1 of 1] Compiling StackOverflow    ( StackOverflow_so.lhs, interpreted )
Ok, modules loaded: StackOverflow.

*StackOverflow>

Hooray!

What I would like:

I would prefer to enable ghci to support this more directly. It would be nice to get rid of the intermediate .lhs file.

Also, it seems ghci does filename completion starting at the shortest substring of :load that determines
you’re actually doing load, so using :lso instead of :so doesn’t fool it.

(I would not like to rewrite my code in C. I also would not like to recompile ghci from source.)

TLDR Question reminder:

  1. Can I get ghci to add filename completion to the :so command I defined in my ghci.conf?
  2. Could I somehow define a ghci command that returns code for compilation instead of returning a ghci command, or
    does ghci instead have a better way for me to plug in Haskell code as a
    file-extension-specific pre-processor, so :l would work for .hs and .lhs files as usual, but use my handwritten preprocessor for .so files?
  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-12T05:09:39+00:00Added an answer on June 12, 2026 at 5:09 am

    I would try to make a standalone preprocessor that runs SO preprocessing code or the standard literary preprocessor, depending on file extension. Then just use :set -pgmL SO-preprocessor in ghci.conf.

    For the standard literary preprocessor, run the unlit program, or use Distribution.Simple.PreProcess.Unlit.

    This way, :load and filename completion just work normally.

    GHCI passes 4 arguments to the preprocessor, in order: -h, the label, the source file name, and the destination file name. The preprocessor should read the source and write to the destination. The label is used to output #line pragmas. You can ignore it if you don’t alter the line count of the source (i.e. replace “comment” lines with -- comments or blank lines).

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

What are the usual causes of Error C stack overflow in the Hugs Haskell
My code (below) falls over with a stack overflow exception. Im assuming F# isnt
This code is used in Learn You A Haskell to explain how (++) can
I want to create a module, and load it in the haskell library. I
I asked this on Haskell-cafe, and the only answer I got was -- try
i'm playing a little with Haskell and i'm stuck with this error, using the
I have a huge haskell file which compiles and runs without any problem. I
I am new to Haskell programming, Foreign Function Interface and Stackoverflow. I am trying
Right to the point, in https://stackoverflow.com/questions/4696618/is-haskell-a-lisp?answertab=votes#tab-top , there is a comment by Kevin Cantu
Based on some advice I found on StackOverflow, I'm digging into Haskell. I was

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.