我对Parsec的一个常见问题是,如果它出现在"正确"的位置,它往往会忽略无效输入.
作为一个具体的例子,假设我们有integer :: Parser Int,我写
expression = sepBy integer (char '+')
Run Code Online (Sandbox Code Playgroud)
(暂时忽略空白问题.)
这正确解析类似"123 + 456 + 789"的内容.但是,如果我喂它"123 + 456-789",它会愉快地忽略非法的" - "字符和表达式的尾部; 我实际上想要一条错误消息告诉我有关无效输入的信息,而不仅仅是让它无声地忽略该部分.
我明白为什么会这样; 我不确定的是如何解决它.设计使用所有提供的输入的解析器的一般方法是什么?只有当所有输入都是有效表达式时才能成功?
标准库提供了一个printf功能,可以成为varadic.然而,它没有做的是编译时检查参数类型是否与格式字符串匹配.这需要依赖类型,听起来真的很难实现!(你必须使用类型签名来解析整个格式字符串...哎呀!)
但经过反思,现在我想知道......我们可以用模板Haskell做到这一点吗?事实上,有没有人为此写过一个库?
(看起来你可以很容易地编写一个准引脚来读取printf格式规范并生成必要的N-arg函数......)
我正在编写一个Haskell包.我希望它在Windows和Unix上表现不同.什么是最"正确"的方法来检测正在编译我的代码的OS?
我确实尝试过使用CPP #ifdef _WIN32,但这似乎根本不起作用.
还有另一个宏吗?我可以让Cabal告诉我吗?我需要完全尝试别的吗?
我刚才意识到写这个是合法的:
let _ = sum [1..100]
in "Hello"
Run Code Online (Sandbox Code Playgroud)
let-binding似乎绝对没有任何作用.
但现在我想知道这里的确切语义.可以编写一个包含_绑定的程序,但删除该绑定会明显改变所述程序的含义吗?
基本上,我想知道自动删除这样的绑定是否安全.据我所知,这种绑定的价值不可能影响任何事情.然而,似乎假设它的类型可能会影响其他东西.任何人都可以构建一个例子吗?
所以我坐在这里想知道为什么我找不到我正在寻找的功能,事实证明它在我安装的软件包版本中不存在.
那么我如何让Hackage告诉我该功能的添加版本是什么?
(特别是,我可以在我的Cabal描述中指定正确的包边界.)
GHC的最新版本具有新的“插件”功能,您可以在其中编写普通的Haskell代码,像往常一样对其进行编译,然后将其插入到编译器中,以使其能够处理GHC的内部状态。
这是非常酷。但是,有一个小障碍:要执行此操作,必须已编译插件(似乎很明显)并将其注册为软件包DB中的软件包!
插件完成后就可以了;将其打包并放在Hackage上,以供所有人欣赏。但是,在尝试开发软件包时如何解决呢?在每次编辑时,您都必须手动注销旧软件包,构建新软件包并进行注册,您的edit-compile-execute周期如何工作?
基本上,在尝试开发插件时,有什么方法可以避开此要求?
我花了一两个星期编写一个简单的逻辑解算器.构建它之后,我发现自己想知道它所解决的语言是否是图灵完备的.因此,我编写了一小组方程式,它们接受SKI组合子演算中的任何有效表达式,并生成包含该表达式的正常形式的结果集.由于SKI 是 Turing-complete,证明我的语言可以执行SKI将证明其图灵完整性.
然而,有一个小故障.解算器不会按正常顺序减少表达式.实际上它的作用是尝试每一个可能的减少订单.这意味着解决方案集通常很大.如果存在正常形式,它将存在于某处,但很难分辨到哪里.
这让我有两个问题:
我的语言图灵完整吗?或者我需要找到更好的证据吗?
解决方案的数量是输入的可计算函数吗?
(起初我假设解决方案集的大小在输入大小中是指数或因子.但仔细观察,这不是真的.你可以编写已经处于正常形式的巨大表达式,以及不终止的小表达式我有一种感觉,确定解决方案集的大小可能等于解决停机问题,但我不完全确定......)
Haskell提供了一个par组合器,它将一个"火花"排队,以便与当前线程并行进行可能的评估.它还提供了一个pseq组合器,它强制评估纯代码以特定顺序发生.
Haskell似乎没有提供的是产生几个火花的方法,然后等待它们全部完成.使用显式并发实现这一点非常简单,但纯粹的火花似乎是不可能的.
在某种程度上,这可能是因为火花的预期用例.它们似乎是为投机评估而设计的.也就是说,做可能需要但可能不需要的工作.因此,火花仅在核心上运行,否则它们是空闲的.
但是,这不是我的用例.我知道很多结果,事实上很快就会需要.如果我在火花爆发之前开始尝试处理结果,我将再次以一堆失败的火花结束单线程.
当然,par 等待火花完成,它不会达到任何并行性!但如果有某种方法可以产生几个火花然后等待它们全部完成,那将是非常有用的.我找不到任何方法可以做到这一点.
有没有人有任何有用的建议?(显然,除了"使用显式并发"之外).
我目前正试图围绕在Haskell中使用异常的正确方法.例外如何运作是直截了当的; 我试图清楚地了解解释它们的正确方法.
基本的立场是,在设计良好的应用程序中,异常不应该逃到顶层.任何例外显然都是设计者没有预料到的例外 - 即程序错误(例如,除以零),而不是异常的运行时发生(例如,未找到文件).
为此,我编写了一个简单的顶级异常处理程序,它捕获所有异常并打印一条消息,stderr说"这是一个错误"(在重新抛出异常以终止程序之前).
但是,假设用户按下Ctrl + C. 这会导致抛出异常.显然,这不是任何程序错误.但是,如果没有预料到并且对此用户中止做出反应可能会被视为错误.所以也许程序应该抓住它并适当地处理它,在退出之前做任何必要的清理.
但问题是......处理此问题的代码将捕获异常,释放任何资源或其他内容,然后重新抛出异常!因此,如果异常使其达到顶级,那并不一定意味着它未被处理.这只是意味着我们想要快速退出.
所以,我的问题:是否应该以这种方式将异常用于流量控制?是否每个明确捕获的函数都UserInterrupt使用显式流控制结构来手动退出而不是重新抛出异常?(但那么来电者如何知道也退出?)是否可以UserInterrupt达到顶级?但在那种情况下,ThreadKilled同样的论点也可以吗?
简而言之,中断处理程序应该为UserInterrupt(并且可能ThreadKilled)做出特殊情况吗?怎么样?HeapOverflow或者StackOverflow?是这一个错误吗?或者是"超出计划控制范围的情况"?
我们偶尔会有人询问在Haskell中实现无类型的lambda演算.[当然,我现在找不到任何这些问题,但我确定我已经看过了!]只是为了咯咯笑,我以为我会花一些时间玩这个.
做一些像这样的事情是微不足道的
i = \ x -> x
k = \ x y -> x
s = \ f g x -> (f x) (g x)
Run Code Online (Sandbox Code Playgroud)
这非常有效.但是,只要你尝试做类似的事情
s i i
Run Code Online (Sandbox Code Playgroud)
类型检查员正确地抱怨无限类型.基本上,无类型lambda演算中的所有东西都是一个函数 - 这实际上意味着所有函数都具有无限的特征.但是Haskell只允许有限度的函数.(因为,真的,为什么你想要无限的灵魂?)
好吧,事实证明我们可以很容易地采取这种限制:
data Term = T (Term -> Term)
T f ! x = f x
i = T $ \ x -> x
k = T $ \ x -> T $ \ y -> x
s = T $ \ …Run Code Online (Sandbox Code Playgroud)