我正在尝试在haskell中编写一个带有两个参数和一个函数的函数。根据这两个参数,它将执行给定的功能。问题是给定的函数可以具有不同的类型签名。在伪代码中:
functionB:: String -> IO()
functionC:: String -> String -> IO()
functionA :: String -> String ->(???)-> IO()
functionA parm1 parm2 f = if parm1 == parm2
then f
else hPutStrLn "error"
FunctionA应该能够使用函数C或B,正如您所看到的,B和C具有不同的类型签名。你能在Haskell做到这一点吗?如果是这样,functionA的类型签名是什么?
答案 0 :(得分:3)
一个函数可能有两个不同的签名"基于一个论点。该函数实际上只有一个最常见的签名,但你可以伪造它,所以它就像那样。
functionA :: String -> String -> a -> a
functionA parm1 parm2 f = if parm1 == parm2
then f
else putStrLn "error" -- uh oh
签名表示这将返回与第三个参数相同的东西。所以:
functionA "foo" "bar" functionB :: String -> IO ()
functionA "foo" "bar" functionC :: String -> String -> IO ()
换句话说,第一行需要一个额外的参数(总共四个),第二行需要两个(总共五个)。
问题在于它不起作用,因为putStrLn "error"
没有这些类型中的任何一种类型,我们需要它具有"两者"。
通过这种方法的一种方法是创建一个类型类,它表征了这两种类型所需的操作。在这种情况下,打印错误是你想要的吗?
{-# 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
请注意,我已将第二个实例递归,因此CanPrintError
不仅包含String -> IO ()
和String -> String -> IO ()
的实例,它具有所有多个arg函数结束的实例在IO ()
中,例如
String -> Int -> (Int -> Maybe Bool) -> IO ()
可以针对这两种特定类型制作它,但它会让我质疑你的动机。
现在我们只需要为functionA
的签名添加必要的约束:
functionA :: (CanPrintError a) => String -> String -> a -> a
functionA parm1 parm2 f = if parm1 == parm2
then f
else printError "error"
您可以想象用printError
替换您需要做的任何操作。这几乎是类型类的用途。 : - )
但是,我建议您发布一个问题,其中包含您问题的更多具体细节,因为它的味道可能更清晰。
答案 1 :(得分:3)
函数const
创建一个新函数,无论传递给它的是什么,它总是返回第一个参数。它的类型签名是:
const :: a -> b -> a
这意味着我们可以创建以下内容:
> const 4 "String"
4
> let fn = const (\x -> print x)
> :t fn
fn :: Show a => b -> a -> IO ()
> fn [1, 2, 3, 4, 5] (Just 3)
Just 3
在您的示例中,您可以使用此功能将“const
”版本的functionB
传递给functionA
:
functionB x = putStrLn x
functionC x y = putStrLn x >> putStrLn y
functionA :: String -> String -> (String -> String -> IO()) -> IO()
functionA parm1 parm2 f = if parm1 == parm2
then f parm1 parm2
else putStrLn "error"
> functionA "Foo" "Foo" (const functionB)
Foo
> functionA "Foo" "Foo" functionC
Foo
Foo
> functionA "Foo" "Bar" undefined
error
答案 2 :(得分:0)
给定的代码不会按预期执行。如上所述,hPutStrln
需要文件句柄,您打算putStrLn
吗?如果是,则putStrLn
的签名为String -> IO ()
,因此putStrLn "error"
仅为IO ()
。由于行else f
没有为f提供参数,因此f的类型签名必须为IO ()
- 类型签名为functionA :: String -> String -> IO () -> IO ()
(实际上,它可能是functionA :: Eq t => t -> t -> IO () -> IO ()
,因为没有什么需要字符串了)。
问题是,如果你不能做到这样,f可以接受一个或两个输入,它只能是或另一个。您可以将else f
替换为else f parm1
,然后f将为String -> IO ()
类型或else parm1 parm2
类型,然后f将为String -> String -> IO ()
类型。