编译时代码重写是否在模板haskell范围之外?

编译时代码重写是否在模板haskell范围之外?,haskell,template-haskell,Haskell,Template Haskell,是否可以创建一个函数,在编译时从模板haskell引号之外重写haskell代码 例如: differentiate :: Floating a => (a -> a) -> a -> (a,a) differentiate = -- what goes here? f :: Num a => a -> a f = sin g :: Num a => a -> (a,a) g = differentiate f 在编译时,它会将g转换为: g

是否可以创建一个函数,在编译时从模板haskell引号之外重写haskell代码

例如:

differentiate :: Floating a => (a -> a) -> a -> (a,a)
differentiate = -- what goes here?

f :: Num a => a -> a
f = sin

g :: Num a => a -> (a,a)
g = differentiate f
在编译时,它会将g转换为:

g x = (sin x, cos x)
我希望我的“differention”函数被传递给“f”的AST,让我在编译之前重写它。据我所知,在模板haskell中,如果不向其传递函数的完整语法,即“g=differenting sin”,则无法执行此操作


谢谢

您正在谈论scheme中的宏。答案是否定的。Haskell函数必须是“引用透明的”,这意味着如果你给它两个表示相等的参数,结果必须表示相等。即,每个
f
必须具有

f (1 + 1) = f 2
如果
f
是一个宏,则不一定如此。然而,这个属性对于语言的“纯洁性”是必不可少的——是什么让Haskell如此易于推理和重构


然而,Haskell中有大量的工作要做,其中没有一个需要宏系统——抽象建模(以及使其看起来更漂亮的类型类)是所有必要的

如果您愿意使用自己的数学函数和数字集,理论上可以这样做。您需要做的是创建一个类型系统来跟踪每个函数的计算方式。这将反映在表达式的类型中。使用模板haskell和具体化函数,或者使用类型类代码,您可以在编译时生成正确的代码

下面是一个使用类型类的黑客示例实现。它与sin、cos、常量和加法一起工作。实施全套操作需要大量的工作。此外,代码中存在相当多的重复,如果您计划使用这种方法,则应尝试解决该问题:

{-# LANGUAGE ScopedTypeVariables, UndecidableInstances, FlexibleInstances, MultiParamTypeClasses, FunctionalDependencies #-}
module TrackedComputation where
import Prelude hiding (sin, cos, Num(..))
import Data.Function (on)
import qualified Prelude as P    

-- A tracked computation (TC for short).
-- It stores how a value is computed in the computation phantom variable
newtype TC newComp val = TC { getVal :: val }
    deriving (Eq)

instance (Show val) => Show (TC comp val) where
    show = show . getVal


data SinT comp = SinT
data CosT comp = CosT

data AddT comp1 comp2 = AddT

data ConstantT = ConstantT

data VariableT = VariableT

sin :: (P.Floating a) => TC comp1 a -> TC (SinT comp1) a
sin = TC . P.sin . getVal
cos :: (P.Floating a) => TC comp1 a -> TC (CosT comp1) a
cos = TC . P.cos . getVal

(+) :: (P.Num a) => TC comp1 a -> TC comp2 a -> TC (AddT comp1 comp2) a
(TC a) + (TC b) = TC $ (P.+) a b

toNum :: a -> TC ConstantT a
toNum = TC

class Differentiate comp compRIn compROut | comp compRIn -> compROut where
    differentiate :: P.Floating a => (TC VariableT a -> TC comp a) -> (TC compRIn a -> TC compROut a)


instance Differentiate ConstantT compIn ConstantT where
    differentiate _ = const $ toNum 0

instance Differentiate (SinT VariableT) compIn (CosT compIn) where
    differentiate _ = cos
instance Differentiate VariableT compIn (ConstantT) where
    differentiate _ = const $ toNum 1

instance (Differentiate add1 compIn add1Out, Differentiate add2 compIn add2Out) =>
    Differentiate (AddT add1 add2) compIn (AddT add1Out add2Out) where
    differentiate _ (val :: TC compROut a) = result where
        first = differentiate (undefined :: TC VariableT a -> TC add1 a) val :: TC add1Out a
        second = differentiate (undefined :: TC VariableT a -> TC add2 a) val :: TC add2Out a
        result = first + second

instance P.Num val => P.Num (TC ConstantT val) where
    (+) = (TC .) . ((P.+) `on` getVal)
    (*) = (TC .) . ((P.*) `on` getVal)
    abs = (TC) . ((P.abs) . getVal)
    signum = (TC) . ((P.signum) . getVal)
    fromInteger = TC . P.fromInteger

f x = sin x

g = differentiate f

h x = sin x + x + toNum 42 + x

test1 = f . toNum
test2 = g . toNum
test3 = differentiate h . toNum

不,你不能这样做——这会使单独编译变得不可能,因为
f
很可能是从其他地方导入的函数——但是你可能想用谷歌来“自动区分”这个特殊问题。这是有道理的。我知道现有的广告包,但不幸的是没有一个对我来说足够快。我目前的方法是将我的符号表达式类型实例为Floating/etc,并将其传递给函数,构建AST。然后我计算它(太慢)或生成C代码。我也可以生成haskell代码,但这似乎是一种迂回的方式。如果你只为一阶导数制作一个特殊用途的广告包,它真的那么慢吗?确保成对构造函数是严格的,这样你就可以得到好的代码了。我不是想抨击它,但它太慢了。上次我做的时候,它至少比C++包慢了20倍。然而,它是非常普遍和美好的有可用的。我的软件包非常专业,只支持我自己的张量类型。你说的“它”是什么意思?你做了一个特殊用途的哈斯凯尔包吗?这个包太慢了。我已经做了很多工作,但还没有准备好进行黑客攻击。我没有想到在类型级别进行区分,它几乎像是一个定理证明器。谢谢你的回答。