It is possible for a function to have "two different signatures" based on an argument. The function will really only have one most general signature, but you can fake it so it works like that.
functionA :: String -> String -> a -> a
functionA parm1 parm2 f = if parm1 == parm2
then f
else putStrLn "error" -- uh oh
The signature says that this will return the same kind of thing as its third argument. So:
functionA "foo" "bar" functionB :: String -> IO ()
functionA "foo" "bar" functionC :: String -> String -> IO ()
In other words, the first line takes one additional argument (four total), and the second line takes two (five total).
The trouble is that it doesn't work, because putStrLn "error"
doesn't have either of those types, and we need it to have "both".
One way through this is to make a typeclass which characterizes the operations you need on both of those types. In this case, maybe printing an error is the thing you want?
{-# LANGUAGE FlexibleInstances #-}
class CanPrintError a where
printError :: String -> a
instance CanPrintError (IO ()) where
-- specialized to:
-- printError :: String -> IO ()
printError = putStrLn
instance (CanPrintError b) => CanPrintError (a -> b) where
-- specialized to:
-- printError :: (CanPrintError b) => String -> a -> b
printError err _ = printError err
Notice that I have made the second instance recursive, so CanPrintError
doesn't only have instances for String -> IO ()
and String -> String -> IO ()
, it has instances for all multiple-arg functions ending in IO ()
such as
String -> Int -> (Int -> Maybe Bool) -> IO ()
It is possible to just make it for the two specific types in question, though it makes me question your motivations.
Now we just add the necessary constraint to functionA
's signature:
functionA :: (CanPrintError a) => String -> String -> a -> a
functionA parm1 parm2 f = if parm1 == parm2
then f
else printError "error"
You can imagine substituting for printError
whatever operation you need to do. This is pretty much what typeclasses are for. :-)
However, I do suggest posting a question with more specific details of your problem, since it smells like something that might have a cleaner solution.
String -> IO()
or aString -> String -> IO()
as parameter. You'd needEither (String -> IO()) (String -> String -> IO())
. (But over-reliance ofIO ()
is a strong sign that you're trying to write a program in a different language.) – molbdnilohPutStrLn
takes aHandle
as its first argument, not aString
. Assuming you meant to useputStrLn
, the type of the expression in theelse
isIO ()
, and the type of the expression in thethen
must have the same type. Therefore, if you changehPutStrLn
toputStrLn
, the(???)
must have typeIO ()
, so neitherfunctionB
norfunctionC
can be passed. – patf :: (String -> a) -> IO ()
would accept eitherString -> IO ()
orString -> String -> IO ()
(as well as any other function whose first argument was of typeString
). Now, whether you could make a useful function with that type is another matter! – pat