Haskell使用Stack编译运行程序时出现报错及输出不符合预期问题
Haskell Stack项目常见编译/运行问题排查
初始代码编译报错的核心原因
- 模块顶层文件不支持直接编写REPL专属的交互语句。《Learn You a Haskell》里演示的
let临时绑定、直接写putStrLn求值这类操作,是REPL交互模式特有的语法,用来快速验证表达式;在独立的.hs源码模块里,所有顶层定义必须遵循标准Haskell声明规则:- 顶层变量绑定不需要写
let关键字,直接用变量名 = 表达式的格式就可以完成声明 - 所有可执行的IO代码必须放在具体的函数定义内部,不能散落在模块顶层
- 顶层变量绑定不需要写
- 你最初写的代码末尾直接在顶层放了
putStrLn a,没有归属任何函数体,编译器解析时无法匹配合法的声明结构,就触发了“缩进错误/括号不匹配”的语法解析提示。
修改后运行仅输出someFunc1的核心原因
stack new创建的标准可执行项目,程序默认入口并不是src/Lib.hs。新建项目的可执行入口固定在app/Main.hs文件中,Lib.hs只是作为被导入的逻辑模块存在。- 你运行时看到的
someFunc1输出,来自app/Main.hs里的默认入口逻辑:这个文件的main函数会直接调用Lib模块导出的someFunc,所以只会打印someFunc1。你在Lib.hs里自定义的main函数根本没有被程序加载执行,自然不会输出LUCKY NUMBER SEVEN!的内容。
正确的编译运行操作步骤
- 你可以根据自己的习惯选择两种修改方案,达到预期的运行效果:
- 保留默认入口结构的方案
- 先修改
src/Lib.hs的模块导出列表,把需要给入口调用的函数开放权限:把开头的模块声明改成module Lib (someFunc, lucky, a) where - 再修改
app/Main.hs的入口逻辑,把原本的单函数调用改成多段IO执行:module Main where import Lib import qualified Data.Text.IO as T main :: IO () main = do someFunc -- 保留原本输出someFunc1的逻辑 putStrLn a - 执行
stack build && stack exec first-project-exe就能看到同时输出someFunc1和LUCKY NUMBER SEVEN!
- 先修改
- 把入口放到Lib.hs的方案
- 打开项目根目录下和项目同名的
.cabal配置文件,找到executable first-project-exe配置块,把main-is字段的默认值app/Main.hs改成src/Lib.hs - 确保
Lib.hs里有合法的main函数作为入口定义,重新执行stack build && stack exec first-project-exe即可正常运行
- 打开项目根目录下和项目同名的
- 保留默认入口结构的方案
适配书籍学习的实用技巧
- 书中的纯逻辑函数(比如
lucky这类没有IO操作的函数)完全可以直接写在Lib.hs的顶层做定义,不需要额外包装; - 如果只是想快速验证写好的函数返回值,不需要每次修改都全量编译项目,直接在项目根目录运行
stack ghci就可以进入绑定了当前项目依赖的REPL环境,导入自己写的模块后直接调用函数,和书里的操作体验完全一致,不需要把测试代码硬写到源码的可执行入口里。
内容的提问来源于stack exchange,提问作者blue-sky
相关产品推荐
相关产品推荐

