I got an argument parser working using getOpt which is great, but I do have one question. When using ReqArg in an option like:
Option ['c'] ["config"] (ReqArg (\f opts -> opts { configFile = f }) "FILE")
"use a custom configuration file"
what does it use that second argument for (in this case, "FILE")? I have not experienced any kind of difference in behavior when specifying another string.
It's for the auto-generated usage message. Same with OptArg. Run usageInfo on your OptDescr list and see what comes back.
Related
I have many files that must be processed automatically. Each file holds the response of one student to an exercise which asks the student to give definitions for some functions given a type for each function.
My idea is to have an Haskell script that loads each student file, and verifies if each function has the expected type.
A constraint is that the student files are not defined as modules.
How can I do this?
My best alternative so far is to spawn a GHCi process that will read stdin from a "test file" with GHCi commands, for example:
:load student1.hs
:t g
... and so on ...
then parse the returned output from GHCi to find the types of the functions in the student file.
Is there another clean way to load an arbitrary Haskell file and introspect its code?
Thanks
Haskell does not save type information at runtime. In Haskell, types are used for pre-runtime type checking at the static analysis phase and are later erased. You can read more about Haskell's type system here.
Is there a reason you want to know the type of a function at runtime? maybe we can help with the problem itself :)
Edit based on your 2nd edit:
I don't have a good solution for you, but here is one idea that might work:
Run a script that for each student module will:
Take the name of the module and produce a file Test.hs:
module Test where
import [module-name]
test :: a -> b -> [(b,a)]
test = g
run ghc -fno-code Test.hs
check the output does not contain type errors
write results into a log file
I think if you have a dynamically determined number of .hs files, which you need to load, parse and introspect, you could/should use the GHC API instead.
See for example:
Using GHC API to compile Haskell sources to CORE and CORE to binary
https://mail.haskell.org/pipermail/haskell-cafe/2009-April/060705.html
These might not be something you can use directly — and I haven't done anything like this myself so far either — but these should get you started.
See also:
https://wiki.haskell.org/GHC/As_a_library
https://hackage.haskell.org/package/hint
The closest Haskell feature to that is Data.Typeable.typeOf. Here's a GHCi session:
> import Data.Typeable
> typeOf (undefined :: Int -> Char)
Int -> Char
> typeOf (undefined :: Int -> [Char])
Int -> [Char]
> typeOf (undefined :: Int -> Maybe [Char])
Int -> Maybe [Char]
> :t typeOf
typeOf :: Typeable a => a -> TypeRep
Under the hood, the Typeable a constraint forces Haskell to retain some type tags until runtime, so that they can be retrieved by typeOf. Normally, no such tags exist at runtime. The TypeRep type above is the type for such tags.
That being said, having such information is almost never needed in Haskell. If you are using typeOf to implement something, you are likely doing it wrong.
If you are using that to defer type checks to run time, when they could have been performed at compile time, e.g. using a Dynamic-like type for everything, then you are definitely doing it wrong.
If the function is supposed to be exported with a specific name, I think probably the easiest way would be to just write a test script that calls the functions and checks they return the right results. If the test script doesn't compile, the student's submission is incorrect.
The alternative is to use either the GHC API (kinda hard), or play with Template Haskell (simpler, but still not that simple).
Yet another possibility is to load the student's code into GHCi and use the :browse command to dump out everything that's exported. You can then grep for the term you're interested in. That should be quite easy to automate.
There's a catch, however: foo :: x -> x and foo :: a -> a are the same type, even though textually they don't match at all. You might contemplate trying to normalise the variable names, but it's worse: foo :: Int -> Int and foo :: Num x => x -> x don't look remotely the same, yet one type is an instance of the other.
...which I guess means I'm saying that my answer is bad? :-(
This question already has answers here:
Can GHCi tell me the type of a local Haskell function?
(3 answers)
Closed 7 years ago.
Is it possible to find the type of functions defined in a where statement with GHCI ?
For example, considering this function :
myFunction a b c = case result of
2 -> ...
where
subFunctA = ...
_ -> ...
where
subFunctB = ...
subFunctC = ...
The type of myFunction can be find with :t myFunction.
Is it possible to find the types of subFunctA, subFuncB and subFunctC with GHCI ?
You could try using typed holes if you have a new enough version of GHCi.
Basically, if you write an underscore in the code, the compiler will spit out an error message telling you what type that subexpression should have. In your example, just replace "..." with "_".
No. GHCi does not have access to the source of its functions. This is true even if you defined the function yourself in GHCi, or in a source file.
As an alternative: since you have access to the source of the function you're wondering about, you can copy the functions into the global scope of your source file, compile them, and see what type GHC assigns them. I do this a lot. (Also, you can paste them into GHCi directly.) Once you've done this, you can call :t on them all you want. This does run the risk of producing more general types than the actual sub-functions have, but it could still help you.
Is it possible to set aliases in the ghci.conf file?
For example I have alias sbh='cd Desktop/Sandbox/Haskell' in bash.bashrc which lets me quickly jump to the specified folder. Is the same thing possible in ghci by putting an alias in the ghci.conf file?
I already have a few commands in ghci.conf but I would like to have multiple aliases set up to jump to folder locations without having to use :cd home/sandbox/foo/bar all of the time. I cant find anything on google so either its never been considered before or am just missing something very simple.
The :def command can do this:
:def sbh const $ return ":cd Desktop/Sandbox/Haskell"
As you can see it is a little more complicated than just giving a substitution string: It takes a Haskell function of type String -> IO String which the newly defined command applies to its argument string to calculate new commands to run.
Then in GHCI :sbh to invoke.
GHCI macros should give you what you're looking for. See: https://www.haskell.org/ghc/docs/7.6.2/html/users_guide/ghci-commands.html as a reference.
Search for "macros" (or :def, which is the command to define macros). You can put these in the ghci.conf file.
For example (from the same URL indicated above):
Prelude> let mycd d = Directory.setCurrentDirectory d >> return ""
Prelude> :def mycd mycd
Prelude> :mycd ..
I hope this helps.
Possible not exactly what you need, but in case the quick-jumping function suffices try this as a first fix (invoked by :sbh):
:def sbh (\arg -> return ("System.Directory.setCurrentDirectory \"Desktop/Sandbox/Haskell\""))
Your later solution might make use of the arg reference like in:
:def sbh (\arg -> return ("System.Directory.setCurrentDirectory " ++ "\"" ++ args ++ "\""))
Invoke the latter which by :sbh Desktop/Sandbox/Haskell then.
This is something of an extension to this question:
Dispatching to correct function with command line arguments in Haskell
So, as it turns out, I don't have a good solution yet for dispatching "commands" from the command line to other functions. So, I'd like to extend the approach in the question above. It seems cumbersome to have to manually add functions to the table and apply the appropriate transformation function to each function so that it takes a list of the correct size instead of its normal arguments. Instead, I'd like to build a table where I'll add functions and "tag" them with the number of arguments it needs to take from the command line. The "add" procedure, should then take care of composing with the correct "takesXarguments" procedure and adding it to the table.
I'd like to be able to install "packages" of functions into the table, which makes me think I need to be able to keep track of the state of the table, since it will change when packages get installed. Is the Reader Monad or the State Monad what I'm looking for?
No monad necessary. Your tagging idea is on the right track, but that information is encoded probably in a different way than you expected.
I would start with a definition of a command:
type Command = [String] -> IO ()
Then you can make "command maker" functions:
mkCommand1 :: (String -> IO ()) -> Command
mkCommand2 :: (String -> String -> IO ()) -> Command
...
Which serves as the tag. If you don't like the proliferation of functions, you can also make a "command lambda":
arg :: (String -> Command) -> Command
arg f (x:xs) = f x xs
arg f [] = fail "Wrong number of arguments"
So that you can write commands like:
printHelloName :: Command
printHelloName = arg $ \first -> arg $ \last -> do
putStrLn $ "Hello, Mr(s). " ++ last
putStrLn $ "May I call you " ++ first ++ "?"
Of course mkCommand1 etc. can be easily written in terms of arg, for the best of both worlds.
As for packages, Command sufficiently encapsulates choices between multiple subcommands, but they don't compose. One option here is to change Command to:
type Command = [String] -> Maybe (IO ())
Which allows you to compose multiple Commands into a single one by taking the first action that does not return Nothing. Now your packages are just values of type Command as well. (In general with Haskell we are very interested in these compositions -- rather than packages and lists, think about how you can take two of some object to make a composite object)
To save you from the desire you have surely built up: (1) there is no reasonable way to detect the number of arguments a function takes*, and (2) there is no way to make a type depend on a number, so you won't be able to create a mkCommand which takes as its first argument an Int for the number of arguments.
Hope this helped.
In this case, it turns out that there is, but I recommend against it and think it is a bad habit -- when things get more abstract the technique breaks down. But I'm something of a purist; the more duct-tapey Haskellers might disagree with me.
1. Motivation
I'm writing my own mini-wiki. I want to be able to easily convert from markdown to LATEX/HTML and vice versa. After some searching I discovered Pandoc, which is written in Haskell and that I could use the FastCGI module to run a Haskell program on my Apache server.
2. Problem/ Question
I'm not sure how to what protocol I should use to send my FastCGI script the input/output variables (POST/GET?) and how this is done exactly. Any ideas, suggestions, solutions?
3. Steps taken
3.1 Attempt
Here is what I've done so far (based on example code). Note, I have no experience in Haskell and at the moment I don't have too much time to learn the language. I'd just love to be able to use the pandoc text format conversion tool.
module Main ( main ) where
import Control.Concurrent
import Network.FastCGI
import Text.Pandoc
--initialize Variables/ functions
fastcgiResult :: CGI CGIResult
markdownToHTML:: String -> String
--implement conversion function
markdownToHTML s = writeLaTeX defaultWriterOptions {writerReferenceLinks = True} (readMarkdown defaultParserState s)
--main action
fastcgiResult = do
setHeader "Content-type" "text/plain"
n <- queryString
output $ (markdownToHTML n)
main :: IO ()
main = runFastCGIConcurrent' forkIO 10 fastcgiResult
This code reads the string after the question mark in the request url. But this is not a good solution as certain characters are omitted (e.g. '#' ) and spaces are replaced by "/20%".
Thanks in advance.
3.2 Network.CGI
Documentation found here. Under the heading "Input" there are a number of methods to get input. Which one is right for me?
Is it :
Get the value of an input variable, for example from a form. If the variable has multiple values, the first one is returned. Example:
query <- getInput "query"
So lets say I have a HTML POST form with name='Joe' can I grab this using getInput? And if so how do I handle the Maybe String type?
The fastCGI package is actually a extension of the cgi package, which includes the protocol types for receiving request data and returning result pages. I'd suggest using CGI to start with, and then move to fastCGI once you know what you are doing.
You might also want to look at this tutorial.
Edit to answer questions about the tutorial:
"Maybe a" is a type that can either contain "Just a" or "Nothing". Most languages use a null pointer to indicate that there is no data, but Haskell doesn't have null pointers. So we have an explicit "Maybe" type instead for cases when the data might be null. The two constructors ("Just" and "Nothing") along with the type force you to explicitly allow for the null case when it might happen, but also let you ignore it when it can't happen.
The "maybe" function is the universal extractor for Maybe types. The signature is:
maybe :: b -> (a -> b) -> Maybe a -> b
Taking the arguments from front to back, the "Maybe a" third argument is the value you are trying to work with. The second argument is a function called if the third argument is "Just v", in which case the result is "f v". The first argument is the default, returned if the third is "Nothing".
In this case, the trick is that the "cgiMain" function is called twice. If it finds an input field "name" then the "mn" variable will be set to (Just "Joe Bloggs"), otherwise it will be set to (Nothing). (I'm using brackets to delimit values now because quotes are being used for strings).
So the "maybe" call returns the page to render. The first time through no name is provided, so "mn" is (Nothing) and the default "inputForm" page is returned for rendering. When the user clicks Submit the same URL is requested, but this time with the "name" field set, so now you get the "greet" function called with the name as an argument, so it says "Hello Joe Bloggs".