tfb*_*boy 7 haskell functional-programming
我的意图很简单.我想将类型的函数包装a -> b
成String -> String
(以便可以将一堆异构函数放入列表中).所以我写道:
wrap :: (Read a, Show b) => (a -> b) -> (String -> String)
wrap f = \s -> show $ f (read s :: a)
Run Code Online (Sandbox Code Playgroud)
但是,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
Run Code Online (Sandbox Code Playgroud)
我想知道为什么我的代码不起作用以及需要什么样的黑客来实现我的目标?
谢谢.
Ant*_*sky 13
您的代码将无法工作,因为Haskell不会重用或范围类型变量; 所述a
中wrap :: (Read a, Show b) => (a -> b) -> (String -> String)
是完全不同的a
,从所述一个中read s :: a
(和他们都普遍量化).这是a1
错误消息中的来源; GHC正在将该计划转换为
wrap :: (Read a, Show b) => (a -> b) -> (String -> String)
wrap f = \s -> show $ f (read s :: a1)
Run Code Online (Sandbox Code Playgroud)
但是,f
参数类型是固定的wrap
,所以简单地删除类型注释就可以了.你的功能变了
wrap :: (Read a, Show b) => (a -> b) -> (String -> String)
wrap f = \s -> show $ f (read s)
-- Or wrap f = show . f . read
Run Code Online (Sandbox Code Playgroud)
你可以使用它:
ghci> map ($ "42") [wrap (+ (7 :: Integer)), wrap (* (2.0 :: Double))]
["49","84.0"]
Run Code Online (Sandbox Code Playgroud)
请注意,这意味着它read s
具有您无法记下的类型.在Haskell 2010(或98)中,解决这个问题的唯一方法是使用类似的函数asTypeOf :: a -> a -> a
; asTypeOf
只是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
Run Code Online (Sandbox Code Playgroud)
在GHC,为了避免这种情况,可以打开的ScopedTypeVariables
延伸 ; 有了它,如果你用a明确限定所有类型变量forall
,它们的范围就像值级别的名称.然后你的代码就会变成
{-# LANGUAGE ScopedTypeVariables #-}
wrap :: forall a b. (Read a, Show b) => (a -> b) -> (String -> String)
wrap f = \s -> show $ f (read s :: a)
Run Code Online (Sandbox Code Playgroud)
但请记住,对于这个简单的示例,您根本不需要任何类型的注释.
sha*_*haf 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)
Run Code Online (Sandbox Code Playgroud)
否则,:: a
函数内的注释引用a
与类型签名中的不同类型(它隐含地表示:: forall a. a
).但请注意,您也可以完全删除类型注释:
wrap :: (Read a, Show b) => (a -> b) -> (String -> String)
wrap f = \s -> show $ f (read s)
Run Code Online (Sandbox Code Playgroud)
由于read s
可以推断出类型.这也让你简化了身体
wrap f = show . f . read
Run Code Online (Sandbox Code Playgroud)