如何内省一个Haskell文件以获得其定义的类型



我有许多文件必须自动处理。

我的想法是有一个Haskell脚本来加载每个学生文件,并验证每个函数是否具有预期的类型。

约束是学生文件不能定义为模块。

我该怎么做?

到目前为止,我最好的替代方案是生成一个GHCi进程,它将使用GHCi命令从"测试文件"中读取stdin,例如:
:load student1.hs
:t g
... and so on ...

然后解析从GHCi返回的输出,以查找学生文件中的函数类型。

是否有另一种干净的方式来加载任意的Haskell文件并自省其代码?

谢谢

Haskell在运行时不保存类型信息。在Haskell中,类型用于静态分析阶段的运行前类型检查,然后擦除。你可以在这里阅读更多关于Haskell类型系统的信息。

你想在运行时知道函数的类型是有原因的吗?也许我们可以帮助解决这个问题。

根据您的第二次编辑编辑:

我没有一个好的解决方案,但这里有一个想法可能有用:

为每个学生模块运行一个脚本:

  1. 取模块名,生成文件Test.hs:

    module Test where
    import [module-name]
    test :: a -> b -> [(b,a)]
    test = g
  • run ghc -fno-code Test.hs
  • 检查输出是否包含类型错误
  • 将结果写入日志文件
  • 我认为如果你有一个动态确定的.hs文件的数量,你需要加载,解析和内省,你可以/应该使用GHC API代替。

    参见:

    • 使用GHC API将Haskell源代码编译为CORE,将CORE编译为二进制
    • https://mail.haskell.org/pipermail/haskell-cafe/2009-April/060705.html

    这些可能不是你可以直接使用的东西——我自己到目前为止也没有做过这样的事情——但是这些应该可以让你开始使用。

    参见:

    • https://wiki.haskell.org/GHC/As_a_library
    • https://hackage.haskell.org/package/hint

    与此最接近的Haskell功能是Data.Typeable.typeOf。这是一个GHCi会话:

    > import Data.Typeable
    > typeOf (undefined :: Int -> Char)
    Int -> Char
    > typeOf (undefined :: Int -> [Char])
    Int -> [Char]
    > typeOf (undefined :: Int -> Maybe [Char])
    Int -> Maybe [Char]
    > :t typeOf
    typeOf :: Typeable a => a -> TypeRep
    

    在底层,Typeable a约束强制Haskell保留一些类型标签直到运行时,以便它们可以被typeOf检索。通常,在运行时不存在这样的标记。上面的TypeRep类型是这类标签的类型。

    也就是说,在Haskell中几乎不需要这样的信息。如果你使用typeOf来实现某些东西,你很可能做错了。

    如果你使用它将类型检查推迟到运行时,当它们可以在编译时执行时,例如使用Dynamic类类型的所有内容,那么你肯定做错了。

    如果函数应该以特定的名称导出,我认为可能最简单的方法是编写一个调用函数并检查它们返回正确结果的测试脚本。如果测试脚本没有编译,那么学生的提交是不正确的。

    另一种选择是使用GHC API(有点难),或者使用Template Haskell(更简单,但仍然不是那么简单)。

    另一种可能性是将学生的代码加载到GHCi中,并使用:browse命令转储导出的所有内容。然后你可以搜索你感兴趣的术语。这应该很容易实现自动化。

    然而,这里有一个问题:foo :: x -> xfoo :: a -> a相同的类型,尽管它们在文本上根本不匹配。您可能会考虑尝试规范化变量名,但情况更糟:foo :: Int -> Intfoo :: Num x => x -> x看起来根本不一样,但其中一种类型是另一种类型的实例。

    …我猜这意味着我的答案是错的?: - (

    最新更新