问题
It's a followup question of Functions to Polymorphic data types
Data type Question
models a question/answer with a Message
(the text of the question) and a function (String -> a
) that maps user's input to the result of the question:
data Question where
Simple :: (Typeable a, Show a) => Message -> (String -> a) -> Question
This CLI program should first gets the name of the Question
, find an instance using getQuestion
function and then run the Question
and print out the result.
{-# LANGUAGE GADTs #-}
import Data.Typeable
type Message = String
data Question where
Simple :: (Typeable a, Show a) => Message -> (String -> a) -> Question
-- more constructors
yourName :: Question
yourName = Simple "Your name?" id
yourWeight :: Question
yourWeight = Simple "What is your weight?" (read :: String -> Int)
getQuestion :: String -> Question
getQuestion "name" = yourName
getQuestion "weight" = yourWeight
runQuestion :: (Typeable a, Show a) => Question -> IO a
runQuestion (Simple message parser) = do
putStrLn message
ans <- getLine
return $ parser ans
main = getLine >>= (runQuestion . getQuestion) >>= print
Type checking fails at here: runQuestion :: (Typeable a, Show a) => Question -> IO a
with No instance for (Typeable a0) arising from a use of ‘runQuestion’
.
If I remove the class constraints (runQuestion :: Question -> IO a
) then I get No instance for (Show a0) arising from a use of ‘print
.
回答1:
This type
Question -> IO a
means "a function that accepts a Question
and returns an IO a
for whatever a
the caller wants". This is obviously wrong; some questions have an Int
answer and some have a String
answer, but no question has an answer that can on demand be Int
, String
, or whatever else we may want.
If all you need from the answer is the ability to show itself, just return a shown answer as an IO String
.
type Message = String
data Question = Simple Message (String -> String)
-- more constructors
yourName :: Question
yourName = Simple "Your name?" show
yourWeight :: Question
yourWeight = Simple "What is your weight?" (show . (read :: String -> Int))
getQuestion :: String -> Question
getQuestion "name" = yourName
getQuestion "weight" = yourWeight
runQuestion :: Question -> IO String
runQuestion (Simple message parser) = do
putStrLn message
ans <- getLine
return $ parser ans
main = getLine >>= (runQuestion . getQuestion) >>= putStrLn
Otherwise you can move existentiality to the answer, which you need to encapsulate in a new GADT:
type Message = String
data Question where
Simple :: Message -> (String -> Answer) → Question
-- more constructors
data Answer where
Easy :: (Typeable a, Show a) => a -> Answer
instance Show Answer where
show (Easy a) = show a
yourName :: Question
yourName = Simple "Your name?" Easy
yourWeight :: Question
yourWeight = Simple "What is your weight?" (Easy . (read :: String -> Int))
getQuestion :: String -> Question
getQuestion "name" = yourName
getQuestion "weight" = yourWeight
runQuestion :: Question -> IO Answer
runQuestion (Simple message parser) = do
putStrLn message
ans <- getLine
return $ parser ans
main = getLine >>= (runQuestion . getQuestion) >>= print
but this is IMHO overkill.
回答2:
The error you report is not the only error.
Let's put on the special spectacles which show the things usually kept invisible by "type inference".
Firstly, the data constructor:
Simple :: forall a. (Typeable a, Show a) =>
Message -> (String -> a) -> Question
Effectively, a value of type Question
looks like
Simple {a}{typeableDict4a}{showDict4a} message parser
where I've written the invisible things in braces. The constructor packs up a type and the two typeclass dictionaries that give the implementations for the members of Typeable
and Show
.
Now let's have the main program. I've renamed the type variable to make a point.
runQuestion :: forall b. (Typeable b, Show b) => Question -> IO b
The type to be given back is chosen by the caller of runQuestion
, separately from whatever type is packed inside the argument of type Question
. Now let's fill in the invisible components in the program itself.
runQuestion {b}{typeableDict4b}{showDict4b}
(Simple {a}{typeableDict4a}{showDict4a} message parser) = do
-- so parser :: String -> a
putStrLn message -- ok, as message :: String
ans <- getLine -- ensures ans :: String
return $ parser ans -- has type IO a, not IO b
The parser
computes a value of the type a
packed up in the Question
, which is totally separate from the type b
passed directly to runQuestion
. The program does not typecheck because there's a conflict between two types which can be made different by the program's caller.
Meanwhile, let's look at print
print :: forall c. Show c => c -> IO ()
When you write
main = getLine >>= (runQuestion . getQuestion) >>= print
you get
main = getLine >>=
(runQuestion {b}{typeableDict4b}{showDict4b} . getQuestion) >>=
print {b}{showDict4b}
and as the return type of runQuestion {b}
is IO b
, it must be the case that print
's c
type is the same as runQuestion
's b
type, but other than that, there is nothing to determine which type b
is, or why it is an instance either of Typeable
or Show
. With the type annotation, the need for Typeable
shows up first (in the runQuestion
call); without, the need for Show
in print
causes the complaint.
The real problem, is that somehow, you seem to want runQuestion
to deliver a thing in whatever type is hidden inside the question, as if you could somehow write a (dependently typed) program like
typeFrom :: Question -> *
typeFrom (Simple {a}{typeableDict4a}{showDict4a} message parser) = a
runQuestion :: (q :: Question) -> IO (typeFrom q)
That's a perfectly sensible thing to want, but it isn't Haskell: there's no way to name "the type packed up inside that argument". Everything which involves that type has to live in the scope of the case analysis or pattern match which exposes it. It's your attempt to do the print
outside that scope that won't be allowed.
来源:https://stackoverflow.com/questions/39147517/functions-of-gadts