How to get the literal value of a TemplateHaskell named variable - haskell

If I have a Name in TemplateHaskell and want to find out the value of the variable that it names, provided that the variable is declared as a literal, can this be done?
var = "foo"
-- Can `contentsOf` be defined?
$((contentsOf . mkName $ "var") >>= guard . (== "foo"))

In theory, yes. In practice, no.
Finding out stuff about existing names is done using reify :: Name -> Q Info, and for a definition like that you would get back a VarI value, which includes a Maybe Dec field. This would seem to suggest that you might in some cases be able to get the syntax tree for the declaration of the variable, which would allow you to extract the literal, however current versions of GHC always returns Nothing in this field, so you're out of luck for a pure TH solution.
However, TH does allow arbitrary IO actions to be run, so you could potentially work around this by loading and parsing the module yourself using something like haskell-src-exts, however I suspect that would be more trouble than it's worth.

Related

Haskell error when using the isNothing function in Maybe

I am trying to use isNothing in my code in Haskell, and it is giving me the error
<interactive>:97:23: error:
• Variable not in scope: isNothing :: Maybe t -> Bool
• Perhaps you meant data constructor ‘Nothing’ (imported from Prelude)
The code line I have is as follows -
maybeMap f value = if isNothing (value) then value else Just (f (check value))
this works fine if I replace isNothing value with value == Nothing, so I am confused why the previous is not working.
First thing's first, the key phrase in the error message is:
Variable not in scope: isNothing
Which means that the compiler just isn't aware of anything named isNothing.
That immediately tells you that the code around your your use of isNothing doesn't matter. It's not a problem with types, or anything to do with the actual isNothing function you're trying to call, and there's no way you can change the code around isNothing to get this to work.
Variable not in scope almost always means one of three things:
You haven't imported the name you're trying to use
You have accidentally misspelled the name you're trying to use
You intended to define something with that name, but haven't done so yet
Changing any of the code surrounding your use of isNothing isn't going to change any of those 3 problems no matter which it is. Even looking at that code isn't going to tell you anything relevant; just closely look at the spelling of the name in the error message to confirm you haven't just made a typo, and if not you know you need to look elsewhere.
In this case it's #1. There are a bunch of useful functions that are in the Haskell Prelude which are automatically imported for you, so you're probably used to just using functions without importing them, but the "normal" case is that to use anything that's already defined you have to import it. isNothing isn't in the Prelude, so that means to use it you have to find out which module it is in and add an import declaration to make it available. (If that module is in a package that isn't already installed, you will also have to obtain the package; that's a question I'm not going to address here)
isNothing comes from the Data.Maybe module (in the base package, which is always installed as part of installing GHC, so no worries there). So you need to use:
import Data.Maybe
If you're working in a file you need to add that to the top of the file (just after the module header, but before you define any names yourself; all imports must come before any of your own code). If you're using the interpreter you can just enter the import as a command.
That will bring all of the names defined in Data.Maybe into scope. If you want more control, you can explicitly import only some of the names, like this:
import Data.Maybe ( isNothing, isJust, listToMaybe )
The function isNothing is not part of the standard prelude. Rather, it's distributed as part of the Data.Maybe module. To use isNothing, you'll need to explicitly import that module:
import Data.Maybe

How can I get GHC to emit a warning for a given function?

Suppose I've decided that everywhere in a given code base (package) I want to use a custom getCurrentTimeMicroseconds rather than getCurrentTime. Is there a way for me to get GHC to emit warnings me about uses of getCurrentTime, only in that code base? (Not for anything upstream or downstream.)
Bonus question, suppose I want to selectively allow usages with an explicit annotation at the use site (preferably not module-wide). Is this also possible?
This is what types are for. The type you use for times should reflect the constraints you want to place on the values it represents.
For example, you could wrap UTCTime like so:
newtype UTCTimeMicroseconds = UTCTimeMicroseconds { picos :: UTCTime }
microsecondsFromPicos :: UTCTime -> UTCTimeMicroseconds
microsecondsFromPicos = ...
getCurrentTimeMilliseconds :: IO UTCTimeMicroseconds
getCurrentTimeMilliseconds = microsecondsFromPicos <$> getCurrentTime
And use the new type everywhere in your package that you need times to have this property.
If you want to be strict about it, don't export the UTCTimeMicroseconds constructor, so the only way to get one of these values is to use microsecondsFromPicos, which enforces your requirement.
That makes any misuse an error, not a warning, but in most cases that's what you want anyway.
When you do want to use UTCTime with the full resolution, or just don't care, you can just use that type as usual. It'll be easy to find the places in your code base where that happens, because they'll be the only places where UTCTime is used.
I can't think of a way to do this right now, but I think the closest you can get is something like:
create a new package my-time that depends on time
re-export your shim functions annotated with warnings, like
import qualified Data.Time as Time
{-# WARNING getCurrentTime "you should prefer getCurrentTimeMicroseconds" #-}
getCurrentTime = Time.getCurrentTime
depend on my-time in your packages
obviously this doesn't give you a way to enforce not importing Data.Time.getCurrentTime, and is even less satisfying when the code you want to shim is in Prelude or base.

Is there a way to lift Template Haskell Names?

Since Name has a Show instance and strings can be used to generate a corresponding Name, I thought about using the following method:
\ name -> [e| mkName $(lift (show name)) |]
This works fine in ghci. But in compiled code, the actual compiled variables look like p_a4hEk, whereas the strings produced by Show look like p_6989586621680030942.
What is happening here? Is there a way to lift a Name into a Template Haskell ExpQ?
Use case: I am writing a TH API that can build complex and efficient Haskell functions based on human-readable user descriptions. I am attaching to the returned function a documentation that explains the purpose and type of each parameter in human readable terms. It would be nice if the documentation could also contain the Name used for that parameter in the code, so that the user can review the spliced code as needed and recognize which parameter is which.

Obtaining TH.Name for '[] without -XTemplateHaskell

Is there a way to obtain (import from base modules or write expression) a value of type Language.Haskell.TH.Name that represents '[] without enabling -XTemplateHaskell?
A good reason to do so is that tools like hlint do not play well with TH and being able to avoid it therefore has a benefit. Then I could put a definition
nilName :: Name
nilName = '[]
in a separate file and import it, but this only makes sense if there is no standard name by which it can be imported or called. Furthermore, nilName cannot be used in pattern matches. Is there such a thing?
import Language.Haskell.TH.Syntax
nilName = mkNameG DataName "ghc-prim" "GHC.Types" "[]"
is an equivalent definition of nilName, even though it is ugly. It can be expanded to a form that admits to pattern matching yielding to
nilName = Name (OccName "[]") (NameG DataName (PkgName "ghc-prim") (ModName "GHC.Types"))
which is not nicer nor robust. It seems that the best route forward is a combination of the above nilName defined in a separate TH-enabled module together with (== nilName) instead of pattern matching.

Alpha conversion on a Haskell expression

Given a Haskell expression, I'd like to perform alpha conversion, ie. rename some of the non free variables.
I've started implementing my own function for this, which works on a haskell-src-exts Exp tree, however it turns out to be surprisingly nontrivial, so I can't help wondering - is there an established easy-to-use library solution for this kind of source conversion? Ideally, it should integrate with haskell-src-exts.
This is one of the problems where the "Scrap Your Boilerplate" style generic libraries shine!
The one I'm most familiar with is the uniplate package, but I don't actually have it installed at the moment, so I'll use the (very similar) functionality found in the lens package. The idea here is that it uses Data.Data.Data (which is the best qualified name ever) and related classes to perform generic operations in a polymorphic way.
Here's the simplest possible example:
alphaConvert :: Module -> Module
alphaConvert = template %~ changeName
changeName :: Name -> Name
changeName (Ident n) = Ident $ n ++ "_conv"
changeName n = n
The (%~) operator is from lens and just means to to apply the function changeName to everything selected by the generic traversal template. So what this does is find every alphanumeric identifier and append _conv to it. Running this program on its own source produces this:
module AlphaConv where
import Language.Haskell.Exts
import Control.Lens
import Control.Lens.Plated
import Data.Data.Lens
instance Plated_conv Module_conv
main_conv
= do ParseOk_conv md_conv <- parseFile_conv "AlphaConv.hs"
putStrLn_conv $ prettyPrint_conv md_conv
let md'_conv = alphaConvert_conv md_conv
putStrLn_conv $ prettyPrint_conv md'_conv
alphaConvert_conv :: Module_conv -> Module_conv
alphaConvert_conv = template_conv %~ changeName_conv
changeName_conv :: Name_conv -> Name_conv
changeName_conv (Ident_conv n_conv)
= Ident_conv $ n_conv ++ "_conv"
changeName_conv n_conv = n_conv
Not terribly useful since it doesn't distinguish between identifiers bound locally and those defined in an outside scope (such as being imported), but it demonstrates the basic idea.
lens may seem a bit intimidating (it has a lot more functionality than just this); you may find uniplate or another library more approachable.
The way you'd approach your actual problem would be a multi-part transformation that first selects the subexpressions you want to alpha-convert inside of, then uses a transformation on those to modify the names you want changed.

Resources