问题
I am trying to create a Haskell program which draws some simple 2d shapes to screen, but when you hover over each shape, it prints the line of source code where the shape was created.
In order to do this I would like to be able to create shapes with parameters for their dimensions and a final parameter which indicates the line number. Something like this:
rect1 = Shape(Rectangle 2 2 lineNumber)
This would create a rectangle of width 2 pixels, height 2 pixels, and use a function lineNumber to store the line this piece of code was written on. Does such a function exist in Haskell? Is it simple to create one?
I have searched stack overflow and found this question where the answerer suggests that the __LINE__ pragma from C++ can be used to achieve a similar effect. Is this the best way to go about it or is there a way to do it in pure Haskell?
回答1:
You can do this with Template Haskell, which is technically yet another GHC extension, but is probably somehow more "pure" than C preprocessor.
Code stolen from here and modified slightly.
{-# LANGUAGE TemplateHaskell #-}
module WithLocation (withLocation) where
import Language.Haskell.TH
withLocation' :: String -> IO a -> IO a
withLocation' s f = do { putStrLn s ; f }
withLocation :: Q Exp
withLocation = withFileLine [| withLocation' |]
withFileLine :: Q Exp -> Q Exp
withFileLine f = do
let loc = fileLine =<< location
appE f loc
fileLine :: Loc -> Q Exp
fileLine loc = do
let floc = formatLoc loc
[| $(litE $ stringL floc) |]
formatLoc :: Loc -> String
formatLoc loc = let file = loc_filename loc
(line, col) = loc_start loc
in concat [file, ":", show line, ":", show col]
Use it like this (from another module):
{-# LANGUAGE TemplateHaskell #-}
module Main where
import WithLocation
main = do
$withLocation $ putStrLn "===oo0=Ü=0oo=== Kilroy was here"
回答2:
Pure Haskell is not aware about source-code-level details. The best solution is still preprocessing a haskell source file with an external preprocessor to embed this information, this is a natural separation of concerns.
Such feature is much more meaningful for dynamic programming systems, like Lisp, where code processing and execution stages are interleaved in time. But AFAIK even Common Lisp does not have such feature, whereas EmacsLisp does (just because its application domain is text editor , not because its creators have decided so).
来源:https://stackoverflow.com/questions/13379356/finding-the-line-number-of-a-function-in-haskell