链接Haskell函数的数据类型

时间:2013-01-06 07:02:24

标签: haskell ghc

假设我有以下内容:

data FuncAndValue v res = FuncAndValue (v -> res) v

chain :: (res -> new_res) -> FuncAndValue v res -> FuncAndValue v new_res
chain new_f (FuncAndValue old_f v) = FuncAndValue (new_f . old_f) v  

GHC是否可以通过内联将函数new_fold_f合并到一个函数中?

基本上,在数据类型中存储函数无论如何都会抑制优化。

我希望GHC能够轻松地将函数链组合成一个(也就是说,我的结构上的“总和”不涉及重复调用代表(+)的thunk,而只是内联(+)所以它像for循环一样运行。我希望将函数存储在数据类型中,然后稍后访问它们不会抑制它。

1 个答案:

答案 0 :(得分:4)

  

GHC是否可以通过内联将函数new_fold_f合并到一个函数中?

是的,如果没有干预FuncAndValue,它可以做同样的事情。当然,需要提供功能的展开,或者无论如何也不会有任何内联的机会。但是如果有可能的话,将函数包装在FuncAndValue中就没什么区别了。

但是让我们问一下GHC本身。首先是类型和非常简单的chain

module FuncAndValue where

data FuncAndValue v res = FuncAndValue (v -> res) v

infixr 7 `chain`

chain :: (res -> new_res) -> FuncAndValue v res -> FuncAndValue v new_res
chain new_f (FuncAndValue old_f v) = FuncAndValue (new_f . old_f) v

apply :: FuncAndValue v res -> res
apply (FuncAndValue f x) = f x

trivia :: FuncAndValue Int (Int,Int)
trivia = FuncAndValue (\x -> (2*x - 1, 3*x + 2)) 1

composed :: FuncAndValue Int Int
composed = chain (uncurry (+)) trivia

和我们为triviacomposed获得的核心(有趣的部分):

FuncAndValue.trivia1 =
  \ (x_af2 :: GHC.Types.Int) ->
    (case x_af2 of _ { GHC.Types.I# y_agp ->
     GHC.Types.I# (GHC.Prim.-# (GHC.Prim.*# 2 y_agp) 1)
     },
     case x_af2 of _ { GHC.Types.I# y_agp ->
     GHC.Types.I# (GHC.Prim.+# (GHC.Prim.*# 3 y_agp) 2)
     })

FuncAndValue.composed2 =
  \ (x_agg :: GHC.Types.Int) ->
    case x_agg of _ { GHC.Types.I# y_agp ->
    GHC.Types.I#
      (GHC.Prim.+#
         (GHC.Prim.-# (GHC.Prim.*# 2 y_agp) 1)
         (GHC.Prim.+# (GHC.Prim.*# 3 y_agp) 2))
    }

内联公平,没有(.)可见。 case中的两个trivia已加入,因此composed中只有一个\x -> (2*x-1) + (3*x+2)。除非有人教GHC足够的代数来简化\x -> 5*x + 1apply composed,否则这就像你希望的那样好。 6在编译时减少到until,即使在单独的模块中也是如此。

但那是非常简单,让我们给它一个更难以破解的坚果。

until的可嵌入版本(module WWUntil where wwUntil :: (a -> Bool) -> (a -> a) -> a -> a wwUntil p f = recur where recur x | p x = x | otherwise = recur (f x) 的当前定义是递归的,因此GHC不会内联它),

collatzStep :: Int -> Int
collatzStep n
    | n .&. 1 == 0  = n `unsafeShiftR` 1
    | otherwise     = 3*n + 1

另一个简单的功能就是它自己的模块,

module Hailstone (collatzLength, hailstone) where

import FuncAndValue
import CollatzStep
import WWUntil

data P = P {-# UNPACK #-} !Int {-# UNPACK #-} !Int

fstP :: P -> Int
fstP (P x _) = x

sndP :: P -> Int
sndP (P _ y) = y

hailstone :: Int -> FuncAndValue Int Int
hailstone n = sndP `chain` wwUntil ((== 1) . fstP) (\(P n k) -> P (collatzStep n) (k+1))
                   `chain` FuncAndValue (\x -> P x 0) n

collatzLength :: Int -> Int
collatzLength = apply . hailstone

最后,坚果

(,)

我通过使用严格的一对帮助了严格性分析器。使用香草Rec { Hailstone.$wrecur [Occ=LoopBreaker] :: GHC.Prim.Int# -> GHC.Prim.Int# -> (# GHC.Prim.Int#, GHC.Prim.Int# #) [GblId, Arity=2, Caf=NoCafRefs, Str=DmdType LL] Hailstone.$wrecur = \ (ww_sqq :: GHC.Prim.Int#) (ww1_sqr :: GHC.Prim.Int#) -> case ww_sqq of wild_Xm { __DEFAULT -> case GHC.Prim.word2Int# (GHC.Prim.and# (GHC.Prim.int2Word# wild_Xm) (__word 1)) of _ { __DEFAULT -> Hailstone.$wrecur (GHC.Prim.+# (GHC.Prim.*# 3 wild_Xm) 1) (GHC.Prim.+# ww1_sqr 1); 0 -> Hailstone.$wrecur (GHC.Prim.uncheckedIShiftRA# wild_Xm 1) (GHC.Prim.+# ww1_sqr 1) }; 1 -> (# 1, ww1_sqr #) } end Rec } lvl_rsz :: GHC.Types.Int -> GHC.Types.Int [GblId, Arity=1, Caf=NoCafRefs] lvl_rsz = \ (x_iog :: GHC.Types.Int) -> case x_iog of _ { GHC.Types.I# tpl1_B4 -> case Hailstone.$wrecur tpl1_B4 0 of _ { (# _, ww2_sqH #) -> GHC.Types.I# ww2_sqH } } ,第二个组件将在每个步骤中添加1后取消装箱并重新装箱,我不能忍受这样的浪费;)但是否则没有相关的区别。

核心GHC的(有趣的部分)产生:

FuncAndValue

这就是没有{{1}}的情况。一切都很好地内嵌,一个美丽的紧密循环。

  

基本上,在数据类型中存储函数无论如何都会抑制优化。

如果你将功能包装在足够的层下,是的。但它与其他价值观相同。