Print and line execution

I find that I am writing a lot of code, for example

  putStr "foo (bar 1) (bar 2) ="
 print $ foo (bar 1) (bar 2)

The problem is that the printed message may go out of sync with the actual executable code. The obvious solution is to automatically generate this code.

One way to do this is to put all the text in a file and write a small program that reads the file and generates the Haskell source code. But another alternative is to use a Haskell template.

Does anyone know how I will write a function that takes a String and generates the above code from it? I guess this should be pretty easy, but TH is poorly documented.

+6
source share
5 answers

You can parse the Haskell code with the haskell-src-meta package. Here is a brief example of how you could combine this with Template Haskell.

 {-# LANGUAGE TemplateHaskell #-} import Language.Haskell.TH import Language.Haskell.TH.Quote import Language.Haskell.Meta runShow = QuasiQuoter { quoteExp = runShowQQ , quotePat = undefined , quoteType = undefined , quoteDec = undefined } runShowQQ :: String -> Q Exp runShowQQ s = do let s' = s ++ " = " Right exp = parseExp s printExp = appE [|print|] (return exp) infixApp [|putStr s'|] [|(>>)|] printExp 

And you would use it like that

 {-# LANGUAGE QuasiQuotes #-} [runShow|foo (bar 1) (bar 2)|] 
+8
source

The Haskell template does not provide a simple means of analyzing arbitrary strings, so the simplest solution is probably to use the C preprocessor. However, the built-in GHC does not support the structure, so we need to pass additional options to use the β€œreal” one instead.

 {-# LANGUAGE CPP #-} {-# OPTIONS_GHC -pgmP cpp #-} #define PRINT_EXP(x) (putStr #x >> putStr " = " >> print (x)) 

Then you can use it as follows:

 PRINT_EXP(foo (bar 1) (bar 2)) 
+4
source

There is an example of an eval -like Haskell code using the GHC API here .

+1
source

Uch. I thought it would be easy, but as far as I can tell, this is impossible.

I expected that there would be a function that turns a string into an expression, but apparently this function does not exist. There is even a function to download more source code from disk. So it seems that this task is actually impossible! I am very surprised by this.

The closest thing I could do is to quote the expression that I want to run, and then build a splice that beautifully prints the quoted expression before running it. However, this brings me to the mercy of a pretty printer's GHC expression. The shortcut does not display exactly the same as I typed it. (In particular, it seems to replace the operators with full names, which just hurts.)

You would think that such a function would be quite trivial. Therefore, the fact that it is not implemented can only be attributed to one of two things:

  • No one needs this feature. (Well, besides me, obviously.)

  • This is not as trivial as it seems. (For example, maybe figure out what context for parsing an expression in Fiddy somehow?)

0
source

You can also use the dump package that was written to handle this particular use case:

 {-# language QuasiQuotes #-} import Debug.Dump main = putStrLn [d| foo (bar 1) (bar 2) |] foo = (+) bar = (+1) 

What prints: (foo (bar 1) (bar 2)) = 5

It also processes more than one expression, separated by commas:

 putStrLn [d| foo (bar 1) (bar 2), map bar [1, 2] |] 

What prints: (foo (bar 1) (bar 2)) = 5 (map bar [1, 2]) = [2,3]

Bonus: If you have nix-shell installed (part of the nix package manager ), you can even quickly test it with this "one-liner":

$ nix-shell -p "nix-shell -p "haskellPackages.ghcWithPackages (p: [p.dump])" --run "echo '{-# language QuasiQuotes #-}; import Debug.Dump; foo = (+); bar = (+1); main = putStrLn [d| foo (bar 1) (bar 2), map bar [1, 2] |]' | runhaskell"

0
source

Source: https://habr.com/ru/post/909626/


All Articles