我的意图很简单。我想将a -> b
类型的函数包装到String -> String
中(以便将一堆异构函数放入列表中)。所以我写道:
wrap :: (Read a, Show b) => (a -> b) -> (String -> String)
wrap f = \s -> show $ f (read s :: a)
然而,ghc
投诉:
Could not deduce (Read a1) arising from a use of `read'
from the context (Read a, Show b)
bound by the type signature for
wrap :: (Read a, Show b) => (a -> b) -> String -> String
我想知道为什么我的代码不起作用以及实现我的目标需要什么样的黑客?
感谢。
答案 0 :(得分:13)
您的代码无效,因为Haskell不会重用或范围类型变量; a
中的wrap :: (Read a, Show b) => (a -> b) -> (String -> String)
与a
中的read s :: a
完全不同a1
(并且它们都被普遍量化)。这是错误消息中wrap :: (Read a, Show b) => (a -> b) -> (String -> String)
wrap f = \s -> show $ f (read s :: a1)
的来源; GHC正在将程序转换为
f
但是,wrap
的参数类型在wrap :: (Read a, Show b) => (a -> b) -> (String -> String)
wrap f = \s -> show $ f (read s)
-- Or wrap f = show . f . read
内是固定的,因此只需删除类型注释即可。你的功能变为
ghci> map ($ "42") [wrap (+ (7 :: Integer)), wrap (* (2.0 :: Double))]
["49","84.0"]
你可以使用它:
read s
请注意,这意味着asTypeOf
具有您无法记下的类型。在Haskell 2010(或98)中,解决这个问题的唯一方法是使用类似asTypeOf :: a -> a -> a
的函数; const
只是a
,但由于它的类型签名,它将第一个返回的参数约束为与第二个类型相同的类型。然后,当然,你必须想出一个wrap :: (Read a, Show b) => (a -> b) -> (String -> String)
wrap f = \s -> show $ f (read s `asTypeOf` fInput)
where fInput = undefined
fOutput = f fInput -- I still can't give this a type signature
类型的变量。以下内容适用于此:
forall
在GHC中,为了避免这种情况,您可以启用the ScopedTypeVariables
extension;如果您使用{-# LANGUAGE ScopedTypeVariables #-}
wrap :: forall a b. (Read a, Show b) => (a -> b) -> (String -> String)
wrap f = \s -> show $ f (read s :: a)
明确限定所有类型变量,那么它们的范围就像值级别名称一样。那么你的代码就会变成
{{1}}
但请记住,对于这个简单的例子,你根本不需要任何类型的注释。
答案 1 :(得分:10)
要明确指定read s
的类型,您需要ScopedTypeVariables
之类的内容:
{-# LANGUAGE ScopedTypeVariables #-}
...
wrap :: forall a b. (Read a, Show b) => (a -> b) -> (String -> String)
wrap f = \s -> show $ f (read s :: a)
因为否则函数内的:: a
注释引用与类型签名中的a
不同的类型(它隐含地表示:: forall a. a
)。但请注意,您也可以完全删除类型注释:
wrap :: (Read a, Show b) => (a -> b) -> (String -> String)
wrap f = \s -> show $ f (read s)
因为可以推断出read s
的类型。这也让你简化了身体
wrap f = show . f . read