我有一个数据结构,如表达式树或图形.我想添加一些"度量"函数,例如depth和size.
如何最好地输入这些功能?
我看到以下三种变体具有大致相同的用途:
depth :: Expr -> Intdepth :: Expr -> Integerdepth :: Num a => Expr -> a我有以下几点考虑因素:
我正在看base并fgl作为例子,他们一直使用Int,但Data.List也有genericLength返回类型的多态性等功能,我想也许这些generic功能的添加可能反映了我应该尊重的现代化趋势并加强.
类似的思想运动在一些广泛使用的库中是显而易见的,当用户需要几种可能的返回类型选择时,提供具有相同功能的全面功能集(例如,xml-conduit提供接受懒惰和严格类型的解析器的解析器).任一ByteString或Text).
Integer总体而言Int,这是一个更好的类型,我有时会发现我需要将一个列表的长度投入到一个列表中Integer,比如因为运行的algorighm Integer需要考虑这个长度.
使函数返回Integral意味着这些函数是多态的,并且可能会降低性能.我不太了解所有细节,但是,据我所知,可能会有一些运行时成本,而且多态事物更难记住.
什么是公认的最佳做法?哪个部分是由于遗留和兼容性考虑因素?(即如果Data.List今天设计,那么什么类型的功能length会有?)我是否错过任何优点和缺点?
简答:作为一般规则使用Int,如果您需要将其转换为其他内容,请使用fromIntegral.(如果您发现自己进行了很多转换,请定义fi = fromIntegral以保存输入,或者创建自己的包装器.)
主要考虑因素是绩效.您想编写算法,以便在内部使用有效的整数类型.提供Int的大小足以满足您正在进行的任何计算(标准保证有符号的30位整数,但即使在使用GHC的32位平台上,它也是一个带符号的32位整数),您可以认为它将是一个高 -平台上的速度整数类型,特别是与Integer(它具有无法优化的拳击和bignum计算开销)相比较.请注意,性能差异可能很大.Int与s相比,使用s的简单计数算法通常会快5-10倍Integer.
虽然您可以为您的功能提供不同的签名:
depth :: Expr -> Integer
depth :: (Num a) => Expr -> a
Run Code Online (Sandbox Code Playgroud)
但实际上使用高效Int类型在引擎盖下实现它并在最后进行转换,这使得转换隐含在我看来是糟糕的做法.特别是如果这是一个库函数,Int通过使其成为签名的一部分来明确表示正在内部使用它会使我更加明智.
关于您列出的注意事项:
首先,generic*功能Data.List不是现代的.特别是,genericLength在可用GHC 0.29,七月公布的1996年在某点之前, length已经被定义来讲 genericLength,简单地:
length :: [a] -> Int
length = genericLength
Run Code Online (Sandbox Code Playgroud)
但是在GHC 0.29中,这个定义被注释掉了#ifdef USE_REPORT_PRELUDE,并且几个手动优化的变体length被独立定义.其他generic*功能不在0.29,但GHC 4.02(1998)已经出现了.
最重要的是,当Prelude版本length从列表推广到Foldables时,这是一个相当新的发展(从GHC 7.10开始?),没有人关心做什么genericLength.我也不认为我曾经在任何严肃的Haskell代码中看到过"在野外"使用过的这些函数.在大多数情况下,您可以将它们视为已弃用.
其次,在库中使用lazy/strict和ByteString/ Textvariants代表了一种稍微不同的情况.特别地,conduit-xml用户通常将基于关于正在处理的数据的考虑以及在给定程序的整个类型系统中具有深远影响的算法的构造,在惰性和严格变体之间以及之间ByteString和Text类型之间做出决定.如果使用conduit-xml惰性Text类型的唯一方法是将它逐个转换为严格的ByteStrings,将其传递给库,然后将其拉回并将其转换回惰性Text类型,没有人会接受这种复杂性.相比之下,基于单态Int的工作定义depth很好,因为您只需fromInteger . depth要将其适应任何数字上下文.
第三,如上所述,Integer从在不关心性能的情况下具有任意精度的观点来看,只是一种"更好"的类型.对于类似depth和count在任何实际环境中的表现,性能可能比无限精度更重要.
第四,在这里选择多态或非多态版本时,我不认为运行时成本或失败记忆应该是认真考虑.在大多数情况下,GHC将在memoization没有问题的上下文中生成多态函数的专用版本.
在此基础上,我怀疑如果Data.List今天设计,它仍然会使用Ints.