模板元编程的Haskell变体

lob*_*ism 4 haskell template-haskell

我是Haskell的新手.鉴于Haskell的整个前提是函数将始终返回相同的值,我希望有一些方法可以在编译时计算常量的fibonacci值,就像我可以在C++中使用模板元编程一样,但我可以看不出怎么做.有办法吗?

Joh*_*n L 9

编辑:Daniel Fischer指出你可以将一个普通的表达式提升到Template Haskell并在编译时评估结果,受输出类型的某些约束,通过一个普通的函数fib然后拼接

$(let x = fib 1000 in [|x|])
Run Code Online (Sandbox Code Playgroud)

原始答案如下.

正如评论中所指出的,模板Haskell是实现这一目标的方法.对于像斐波那契这样的归纳函数,它非常简单.您编写类似于标准定义的代码,但返回ExpQ值.由于拼接限制,您需要使用2个模块.

{-# LANGUAGE TemplateHaskell #-} 
module TH where

import Language.Haskell.TH

fibTH :: Int -> ExpQ
fibTH 0 = [| 0 |]
fibTH 1 = [| 1 |]
fibTH n = [| $(fibTH (n-1)) + $(fibTH (n-2)) |]
Run Code Online (Sandbox Code Playgroud)

{-# LANGUAGE TemplateHaskell #-}
module Main where

import TH

y :: Int
y = $(fibTH 10)

main = print y
Run Code Online (Sandbox Code Playgroud)

为了确认工作是在编译时执行的,我们可以编译-ddump-simpl以查看确认它的核心.

Main.y :: GHC.Types.Int
[GblId,
 Caf=NoCafRefs,
 Str=DmdType m,
 Unf=Unf{Src=<vanilla>, TopLvl=True, Arity=0, Value=True,
         ConLike=True, WorkFree=False, Expandable=True,
         Guidance=IF_ARGS [] 10 20}]
Main.y = GHC.Types.I# 55
Run Code Online (Sandbox Code Playgroud)

  • 在另一个模块中有一个普通的'fib`函数,然后拼接`y = $(让x = fib 1000 in [| x |])`,这不是更好吗? (8认同)

Gab*_*lez 5

有一个伟大的文章由唐·斯图尔特,他表明,使用LLVM后端与标志的正确选择在编译时将预先计算的某些功能,并用常数替换它们.