将类型为"a - > b"的函数转换为Haskell中类型为"String - > String"的函数

tfb*_*boy 7 haskell functional-programming

我的意图很简单.我想将类型的函数包装a -> bString -> 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不会重用或范围类型变量; 所述awrap :: (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)