在模板 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 x = (sin x, cos x)

我希望我的"微分"函数传递"f"的 AST,让我在编译之前重写它。据我所知,如果不传递函数的完整语法,即"g = 区分罪恶",你就无法在模板 haskell 中做到这一点。

谢谢

你说的是方案中的宏。 答案是否定的。 Haskell函数必须是"引用透明的",这意味着如果你给它两个指称相等的参数,结果必须是指称相等的。 即,每个f都必须有

f (1 + 1) = f 2

如果f是一个宏观,那就不一定是这样了。 然而,这个属性对于语言的"纯度"至关重要——是什么让Haskell如此善于推理和重构。

然而,在 Haskell 中,关于自动微分的工作非常广泛,这些工作都不需要宏系统——抽象建模(以及使其看起来不错的类型类(是必要的。

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

下面是一个使用类型类的黑客示例实现。它适用于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

最新更新