How can I print an arbitrary Haskell expression?

Hello, community. Based on my online research, I think I have a solution for printing a Haskell expression. I tried to use this code:

{-# LANGUAGE CPP #-}
{-# OPTIONS_GHC -pgmP cpp #-}

#define PRINT_EXP(x) (putStr #x >> putStr " = " >> print (x))

main = PRINT_EXP(null [1,2,3])

to produce this output:

"null [1,2,3]"

However, I get the following error:

/Users/detach/Dropbox/vsc-files/Haskell/HaskellMetaTest/app/Main.hs:1:1: error:
    `cpp' failed in phase `C pre-processor'. (Exit code: 1)
  |
1 | {-# LANGUAGE CPP #-}
  | ^
*** Exception: ExitFailure 1

Apparently, this pragma fails:

{-# OPTIONS_GHC -pgmP cpp #-}

I have two questions:

  • How can I solve this issue ?
  • What’s the ‘#’ symbol purpose in front of ‘x’ variable ? Does it have to do with C pre-processor ?

Thank you for any assistance.

1 Like

If someone with more experience could step in, I’m just going off the docs, but here goes:

First, the # operator in the C pre-processor denotes stringification.

Second, the {-# LANGUAGE CPP #-} already turns on the c pre-processor, so if you need to use OPTIONS_GHC, then according to the docs, -pgmP <cmd> is for replacing the c preprocessing phase with a given command such as cpp2hs.

You have just supplied cpp, as an argument to -pgmP, so it will look for an executable named cpp to run as the c pre-processor, which is where it’s failing*. However, -cpp is actually its own flag (for which you omitted the -) that enables the c pre-processor, the same as {-# LANGUAGE CPP #-} so OPTIONS_GHC is potentially entirely unnecessary.

*Unless you were intending to pass the cpp executable to -pgmP after enabling it using {-# LANGUAGE CPP #-} instead of -cpp, but that should already be the default c pre-processor, which would again make it unnecessary.

However, also remember that this occurs during compilation, as a pre-processor step, so if you pass in null [1,2,3] it will print "null [1,2,3]" but if you do let a = 1, b = 2, c = 3 in PRINT_EXP(null [a,b,c]), it is going to print "null [a,b,c]", not "null [1,2,3]", unless it inlines the values before the pre-processor step, which it won’t, because its a pre- processor.

1 Like

Thank you so much !!

If I leave out,

{-# OPTIONS_GHC -pgmP cpp #-}

I get

     error: too many arguments provided to function-like macro invocation
  |
5 | main = PRINT_EXP(null [1,2,3])
  |                          ^
main = PRINT_EXP(null [1,2,3])

Full code:

{-# LANGUAGE CPP #-}

#define PRINT_EXP(x) (putStr #x >> putStr " = " >> print (x))

main = PRINT_EXP(null [1,2,3])

I used (even though seems to be unnecessary, per your explanation):

{-# OPTIONS_GHC -pgmP cpp #-}

because the explanation on the web said:

The simplest solution is probably to use the C preprocessor. However, the built-in one in GHC does not support stringification, so we need to pass extra options to use the “real” one instead.

Oh, that’ll do it - I didn’t know GHC’s built-in CPP was restricted / didn’t support stringification, so in your case using {-# OPTIONS_GHC -pgmP cpp #-} to call the system cpp executable found via PATH is the way to go.

Thank you again. However, if I leave that, I get:

`cpp' failed in phase `C pre-processor'. (Exit code: 1)

What happens if you call cpp manually?

When I do it, I get the same error that they did earlier:

error: too many arguments provided to function-like macro invocation
main = PRINT_EXP(null [1,2,3])
                         ^

This got me digging, and… @unlocked2412 you don’t happen to be using MacOS, do you?

stringification may be broken on the built-in MacOS cpp executable:

It’s an old question, but I can confirm it still in effect, because I use MacOS and just checked. Notably, if I replace PRINT_EXP(x) (putStr #x >> putStr " = " >> print (x)) with PRINT_EXP(...) (putStr #__VA_ARGS__ >> putStr " = " >> print (__VA_ARGS__)), it actually spits out:

{-# OPTIONS_GHC -pgmP cpp  #-}

module Main where

main = (putStr #null [1,2,3] >> putStr " = " >> print (null [1,2,3]))

Now, stringification is still broken, but it didn’t error because I used __VA_ARGS__. I don’t know if this helps?

2 Likes

Thank you for your investigation ! I do use macOS !

If I call manually I get the same error.

This works on my mac:

{-# LANGUAGE CPP #-}
{-# OPTIONS_GHC -pgmP "clang -E" #-}

#define PRINT_EXP(...) (putStr #__VA_ARGS__ >> putStr " = " >> print (__VA_ARGS__))

main = PRINT_EXP(null [1, 2, 3])
2 Likes

This works…perfectly…Fell off my chair.

Thank you so much @jaror

3 Likes