haskell - 长度索引链表的融合
问题描述
我有标准Vect
类型:
module Vect where
data Nat = Z | S Nat
data Vect (n :: Nat) (a :: Type) where
VNil :: Vect Z a
VCons :: a -> Vect n a -> Vect (S n) a
我有这些功能:
foldVect :: forall (p :: Nat -> Type) n a.
p Z ->
(forall m. a -> p m -> p (S m)) ->
Vect n a -> p n
foldVect n c = go
where go :: forall l. Vect l a -> p l
go VNil = n
go (VCons x xs) = x `c` go xs
newtype FVect a n = FVect { unFVect :: Vect n a }
buildVect :: forall n a.
(forall (p :: Nat -> Type).
p Z ->
(forall m. a -> p m -> p (S m)) ->
p n
) -> Vect n a
buildVect f = unFVect $ f (FVect VNil) $ \x (FVect xs) -> FVect $ x `VCons` xs
我试图重新创建(部分)base
允许列表融合的机器:
instance Functor (Vect n) where
fmap = mapVect
{-# INLINE fmap #-}
mapVect :: forall n a b. (a -> b) -> (Vect n a -> Vect n b)
mapVect _f VNil = VNil
mapVect f (VCons x xs) = f x `VCons` mapVect f xs
mapFB :: forall (p :: Nat -> Type) n a b. (forall m. b -> p m -> p (S m)) -> (a -> b) -> a -> p n -> p (S n)
mapFB cons f = \x ys -> cons (f x) ys
{-# INLINE [0] mapFB #-}
{-# NOINLINE [0] mapVect #-}
{-# RULES "mapVect" [~1] forall f xs. mapVect f xs = buildVect (\nil cons -> foldVect nil (mapFB cons f) xs) #-}
{-# INLINE [0] foldVect #-}
-- base has this; I don't think it does anything without a "refolding" rule on mapVect
{-# INLINE [0] buildVect #-}
{-# RULES "foldVect/buildVect" forall (nil :: p Z)
(cons :: forall m. a -> p m -> p (S m))
(f :: forall (q :: Nat -> Type).
q Z ->
(forall m. a -> q m -> q (S m)) ->
q n
).
foldVect nil cons (buildVect f) = f nil cons
#-}
进而
module Test where
import Vect
test :: Vect n Int -> Vect n Int
test = fmap (*5) . fmap (+2)
没有融合发生。如果我通过-ddump-simpl
了,我会看到foldVect
并且buildVect
已经被内联了,但是......
- s 是分阶段的
INLINE
,因此它们无论如何都不会干扰融合。(毕竟,这是怎么base
回事[]
) 用 s替换
INLINE [0]
sNOINLINE
绘制了一个相当惊人的图像:test = \ (@ (n_a141 :: Nat)) (x_X1lK :: Vect n_a141 Int) -> buildVect @ n_a141 @ Int (\ (@ (p_X1jl :: Nat -> *)) (nil_X11K [OS=OneShot] :: p_X1jl 'Z) (cons_X11M [OS=OneShot] :: forall (m :: Nat). Int -> p_X1jl m -> p_X1jl ('S m)) -> foldVect @ p_X1jl @ n_a141 @ Int nil_X11K (\ (@ (m_a1i5 :: Nat)) (x1_aYI :: Int) (ys_aYJ [OS=OneShot] :: p_X1jl m_a1i5) -> cons_X11M @ m_a1i5 (case x1_aYI of { GHC.Types.I# x2_a1l5 -> GHC.Types.I# (GHC.Prim.*# x2_a1l5 5#) }) ys_aYJ) (buildVect @ n_a141 @ Int (\ (@ (p1_a1i0 :: Nat -> *)) (nil1_a10o [OS=OneShot] :: p1_a1i0 'Z) (cons1_a10p [OS=OneShot] :: forall (m :: Nat). Int -> p1_a1i0 m -> p1_a1i0 ('S m)) -> foldVect @ p1_a1i0 @ n_a141 @ Int nil1_a10o (\ (@ (m_a1i5 :: Nat)) (x1_aYI :: Int) (ys_aYJ [OS=OneShot] :: p1_a1i0 m_a1i5) -> cons1_a10p @ m_a1i5 (case x1_aYI of { GHC.Types.I# x2_a1lh -> GHC.Types.I# (GHC.Prim.+# x2_a1lh 2#) }) ys_aYJ) x_X1lK)))
一切都在那里,但简化器只是没有它。
如果我检查规则本身,我会看到这个
"foldVect/buildVect"
forall (@ (p_aYG :: Nat -> *))
(@ (n_aYJ :: Nat))
(@ a_aYH)
(nil_aYD :: p_aYG 'Z)
(cons_aYE :: forall (m :: Nat). a_aYH -> p_aYG m -> p_aYG ('S m))
(f_aYF
:: forall (q :: Nat -> *).
q 'Z -> (forall (m :: Nat). a_aYH -> q m -> q ('S m)) -> q n_aYJ).
foldVect @ p_aYG
@ n_aYJ
@ a_aYH
nil_aYD
cons_aYE
(buildVect
@ n_aYJ
@ a_aYH
(\ (@ (p1_a156 :: Nat -> *))
(ds_d1io :: p1_a156 'Z)
(ds1_d1ip
:: forall (m :: Nat). a_aYH -> p1_a156 m -> p1_a156 ('S m)) ->
f_aYF @ p1_a156 ds_d1io ds1_d1ip))
= f_aYF @ p_aYG nil_aYD cons_aYE
看来问题在于参数buildVect
需要是非常特定形式的 lambda 抽象,而我在构建最终发生的重写系统时遇到了麻烦。
如何让融合发挥作用?
(我不知道这是否有用甚至正确;我只是这样做看看我是否可以。)
解决方案
像往常一样,newtype
只要编译器被牛逼,就可以节省一天的时间:
module Vect where
-- everything else the same...
newtype VectBuilder n a = VectBuilder { runVectBuilder :: forall (p :: Nat -> Type).
p Z ->
(forall m. a -> p m -> p (S m)) ->
p n
}
buildVect' :: forall n a. VectBuilder n a -> Vect n a
buildVect' f = unFVect $
runVectBuilder f (FVect VNil) $ \x (FVect xs) -> FVect $ x `VCons` xs
{-# INLINE [0] buildVect' #-}
buildVect :: forall n a.
(forall (p :: Nat -> Type).
p Z ->
(forall m. a -> p m -> p (S m)) ->
p n
) -> Vect n a
buildVect f = buildVect' (VectBuilder f)
{-# INLINE buildVect #-}
{-# RULES "foldVect/buildVect'" forall (nil :: p Z)
(cons :: forall m. a -> p m -> p (S m))
(f :: VectBuilder n a).
foldVect nil cons (buildVect' f) = runVectBuilder f nil cons
#-}
-- compiler no longer has a chance to muck up the LHS by eta expanding f because
-- f "isn't" a function anymore
-- rule for mapVect goes unchanged, so I guess that's evidence that this is totally transparent
module Test where
import Vect
test :: Vect n Int -> Vect n Int
test = fmap (*5) . fmap (+2)
Rec {
-- RHS size: {terms: 19, types: 31, coercions: 13, joins: 0/0}
Test.test_go [Occ=LoopBreaker]
:: forall (l :: Nat). Vect l Int -> FVect Int l
[GblId, Arity=1, Caf=NoCafRefs, Str=<S,1*U>]
Test.test_go
= \ (@ (l_a14W :: Nat)) (ds_d1jk :: Vect l_a14W Int) ->
case ds_d1jk of {
VNil co_a14Y -> (Vect.$WVNil @ Int) `cast` <Co:4>;
VCons @ n2_a151 co_a152 x_aYE xs_aYF ->
(Vect.VCons
@ ('S n2_a151)
@ Int
@ n2_a151
@~ <Co:2>
(case x_aYE of { GHC.Types.I# x1_a1xr ->
GHC.Types.I# (GHC.Prim.*# (GHC.Prim.+# x1_a1xr 2#) 5#) -- success!
})
((Test.test_go @ n2_a151 xs_aYF) `cast` <Co:3>))
`cast` <Co:4>
}
end Rec }
-- RHS size: {terms: 4, types: 5, coercions: 0, joins: 0/0}
Test.test1 :: forall (n :: Nat). Vect n Int -> FVect Int n
[GblId,
Arity=1,
Caf=NoCafRefs,
Str=<S,1*U>,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=True)}]
Test.test1
= \ (@ (n_a1wd :: Nat)) (x_X1xa :: Vect n_a1wd Int) ->
Test.test_go @ n_a1wd x_X1xa
-- RHS size: {terms: 1, types: 0, coercions: 9, joins: 0/0}
test :: forall (n :: Nat). Vect n Int -> Vect n Int
[GblId,
Arity=1,
Caf=NoCafRefs,
Str=<S,1*U>,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=0,unsat_ok=True,boring_ok=True)}]
test = Test.test1 `cast` <Co:9>
故事的寓意:排名足够高的类型会使RULES
系统崩溃,因此请给 GHC 一些帮助newtype
s,即使它们不是必需的。
推荐阅读
- java - 在 XMLInputFactory 中禁用功能安全处理
- c# - 忽略从以 XML 格式输出的 CSV 文件中读取的数组的标题行
- image - 如何从 Active Directory 中检索图像?
- json - 每次我从电话簿中删除人时都会出错 - React JS + Axios
- flutter - 单身人士被处置
- c - 十六进制除以 10
- ios - TVOS UITableViewCell _UIFloatingContentView 它是什么以及为什么?
- php - 如何使用模型中的函数使用总和分组元素?Laravel - 雄辩
- android - 如何启用选项以在卸载提示时保留应用程序的数据?
- database - 在哪里可以找到我尝试使用 Hibernate 访问的数据库?