类型为Read的类,以其名称读取函数是否可行?

Dam*_*ero 0 haskell typeclass

假设我有这个功能:

squared x = x ** 2
Run Code Online (Sandbox Code Playgroud)

我想从控制台读取它并应用它。可以做这样的事情吗:

gchi> (read("squared) :: Int -> Int) 4
Run Code Online (Sandbox Code Playgroud)

bra*_*drn 5

没有; 通常,这是不可能的。要了解原因,请考虑存在此功能时可能发生的几种情况:

  • 假设您重新定义(+) a b = a - b。现在,应该read "(+)"是(+)Prelude中的还是重新定义的(+)运算符?
  • 如果这样做read "foobar",foobar但尚未将其定义为函数,那么应该返回什么?然后,如果foobar在模块中的其他位置定义了该命令,
  • 如果您将import Prelude ()程序添加到程序的顶部(从Prelude中删除所有导入),应该read "preludeFunctionName"停止工作吗?

现在,这些问题本身都不是破坏交易的因素-用另一种语言,您仍然可以定义一个read考虑这些问题的函数。在这种情况下,问题是参照透明性:IO给定相同的输入,非函数应始终返回相同的输出的属性。由于的类型read是Read a => String -> a,而不是Read a => String -> IO a,因此您无法根据需要创建一个实例read,因为上述函数可以根据运行它的确切上下文给出不同的答案。(例如read "foobar"和let foobar = (+1) in read "foobar"将对同一函数调用给出不同的答案。)

另一方面,如果您仍想拥有此功能,则有一种获取它的方法。该hint程序包允许在运行时解释Haskell代码,因此您可以使用此功能编写如下函数:

-- Warning: the following code is untested
-- Please comment if you find any errors!
import Language.Haskell.Interpreter

readFn :: Typeable a
       => [ModuleName]   -- ^ Modules to search
       -> String         -- ^ Function to read
       -> a              -- ^ Witness for the type a
       -> IO (Either InterpreterError a)
readFn m f w = runInterpreter $ do
    setImports m
    interpret f w
Run Code Online (Sandbox Code Playgroud)

上面的功能可以如下使用:

readFn ["Prelude"] "(+)" (as :: Num a => a -> a -> a)
Run Code Online (Sandbox Code Playgroud)

哪里as是Language.Haskell.Interpreter。