只要(|>)
和(>>>)
被内联,就不应该有任何区别。让我们编写一个示例,其中使用了四个不同的函数,其中两个采用F#风格,另外两个采用Haskell风格:
import Data.Char (isUpper)
{-# INLINE (|>) #-}
(|>) :: a -> (a -> b) -> b
(|>) x f = f x
{-# INLINE (>>>) #-}
(>>>) :: (a -> b) -> (b -> c) -> a -> c
(>>>) f g x = g (f x)
compositionF :: String -> String
compositionF = filter isUpper >>> length >>> show
applicationF :: String -> String
applicationF x = x |> filter isUpper |> length |> show
compositionH :: String -> String
compositionH = show . length . filter isUpper
applicationH :: String -> String
applicationH x = show $ length $ filter isUpper $ x
main :: IO ()
main = do
getLine >>= putStrLn . compositionF
getLine >>= putStrLn . applicationF
getLine >>= putStrLn . compositionH
getLine >>= putStrLn . applicationH
如果我们使用-ddump-simpl -dsuppress-all -O0
编译代码,则会得到以下结果:
==================== Tidy Core ====================
Result size of Tidy Core = {terms: 82, types: 104, coercions: 0}
>>>_rqe
>>>_rqe =
\ @ a_a1cE @ b_a1cF @ c_a1cG f_aqr g_aqs x_aqt ->
g_aqs (f_aqr x_aqt)
$trModule1_r1gR
$trModule1_r1gR = TrNameS "main"#
$trModule2_r1h6
$trModule2_r1h6 = TrNameS "Main"#
$trModule
$trModule = Module $trModule1_r1gR $trModule2_r1h6
main
main =
>>
$fMonadIO
(>>=
$fMonadIO
getLine
(. putStrLn
(>>>_rqe
(>>>_rqe (filter isUpper) (length $fFoldable[]))
(show $fShowInt))))
(>>
$fMonadIO
(>>=
$fMonadIO
getLine
(. putStrLn
(\ x_a10M ->
show $fShowInt (length $fFoldable[] (filter isUpper x_a10M)))))
(>>
$fMonadIO
(>>=
$fMonadIO
getLine
(. putStrLn
(. (show $fShowInt) (. (length $fFoldable[]) (filter isUpper)))))
(>>=
$fMonadIO
getLine
(. putStrLn
(\ x_a10N ->
show $fShowInt (length $fFoldable[] (filter isUpper x_a10N)))))))
main
main = runMainIO main
如果我们不启用优化,那么>>>
就不会被内联。但是,如果我们启用了优化,你将看不到>>>
或(.)
。由于在这个阶段(.)
没有被内联,所以我们的函数略有不同,但这是可以预料的。
如果我们在函数中添加{-# NOINLINE … #-}
并启用优化,我们会发现这四个函数不会有任何差别:
$ ghc -ddump-simpl -dsuppress-all -O2 Example.hs
[1 of 1] Compiling Main ( Example.hs, Example.o )
==================== Tidy Core ====================
Result size of Tidy Core = {terms: 261, types: 255, coercions: 29}
$trModule2
$trModule2 = TrNameS "main"#
$trModule1
$trModule1 = TrNameS "Main"#
$trModule
$trModule = Module $trModule2 $trModule1
Rec {
$sgo_r574
$sgo_r574 =
\ sc_s55y sc1_s55x ->
case sc1_s55x of _ {
[] -> I# sc_s55y;
: y_a2j9 ys_a2ja ->
case y_a2j9 of _ { C# c#_a2hF ->
case {__pkg_ccall base-4.9.1.0 u_iswupper Int#
-> State# RealWorld -> (# State# RealWorld, Int# #)}_a2hE
(ord# c#_a2hF) realWorld#
of _ { (# ds_a2hJ, ds1_a2hK #) ->
case ds1_a2hK of _ {
__DEFAULT -> $sgo_r574 (+# sc_s55y 1#) ys_a2ja;
0# -> $sgo_r574 sc_s55y ys_a2ja
}
}
}
}
end Rec }
applicationH
applicationH =
\ x_a12X ->
case $sgo_r574 0# x_a12X of _ { I# ww3_a2iO ->
case $wshowSignedInt 0# ww3_a2iO []
of _ { (# ww5_a2iS, ww6_a2iT #) ->
: ww5_a2iS ww6_a2iT
}
}
Rec {
$sgo1_r575
$sgo1_r575 =
\ sc_s55r sc1_s55q ->
case sc1_s55q of _ {
[] -> I# sc_s55r;
: y_a2j9 ys_a2ja ->
case y_a2j9 of _ { C# c#_a2hF ->
case {__pkg_ccall base-4.9.1.0 u_iswupper Int#
-> State# RealWorld -> (# State# RealWorld, Int# #)}_a2hE
(ord# c#_a2hF) realWorld#
of _ { (# ds_a2hJ, ds1_a2hK #) ->
case ds1_a2hK of _ {
__DEFAULT -> $sgo1_r575 (+# sc_s55r 1#) ys_a2ja;
0# -> $sgo1_r575 sc_s55r ys_a2ja
}
}
}
}
end Rec }
compositionH
compositionH =
\ x_a1jF ->
case $sgo1_r575 0# x_a1jF of _ { I# ww3_a2iO ->
case $wshowSignedInt 0# ww3_a2iO []
of _ { (# ww5_a2iS, ww6_a2iT #) ->
: ww5_a2iS ww6_a2iT
}
}
Rec {
$sgo2_r576
$sgo2_r576 =
\ sc_s55k sc1_s55j ->
case sc1_s55j of _ {
[] -> I# sc_s55k;
: y_a2j9 ys_a2ja ->
case y_a2j9 of _ { C# c#_a2hF ->
case {__pkg_ccall base-4.9.1.0 u_iswupper Int#
-> State# RealWorld -> (# State# RealWorld, Int# #)}_a2hE
(ord# c#_a2hF) realWorld#
of _ { (# ds_a2hJ, ds1_a2hK #) ->
case ds1_a2hK of _ {
__DEFAULT -> $sgo2_r576 (+# sc_s55k 1#) ys_a2ja;
0# -> $sgo2_r576 sc_s55k ys_a2ja
}
}
}
}
end Rec }
compositionF
compositionF =
\ x_a1jF ->
case $sgo2_r576 0# x_a1jF of _ { I# ww3_a2iO ->
case $wshowSignedInt 0# ww3_a2iO []
of _ { (# ww5_a2iS, ww6_a2iT #) ->
: ww5_a2iS ww6_a2iT
}
}
Rec {
$sgo3_r577
$sgo3_r577 =
\ sc_s55d sc1_s55c ->
case sc1_s55c of _ {
[] -> I# sc_s55d;
: y_a2j9 ys_a2ja ->
case y_a2j9 of _ { C# c#_a2hF ->
case {__pkg_ccall base-4.9.1.0 u_iswupper Int#
-> State# RealWorld -> (# State# RealWorld, Int# #)}_a2hE
(ord# c#_a2hF) realWorld#
of _ { (# ds_a2hJ, ds1_a2hK #) ->
case ds1_a2hK of _ {
__DEFAULT -> $sgo3_r577 (+# sc_s55d 1#) ys_a2ja;
0# -> $sgo3_r577 sc_s55d ys_a2ja
}
}
}
}
end Rec }
applicationF
applicationF =
\ x_a12W ->
case $sgo3_r577 0# x_a12W of _ { I# ww3_a2iO ->
case $wshowSignedInt 0# ww3_a2iO []
of _ { (# ww5_a2iS, ww6_a2iT #) ->
: ww5_a2iS ww6_a2iT
}
}
...
所有的go
函数都是完全相同的(变量名除外),而application*
与composition*
相同。因此,你可以放心地在Haskell中创建自己的F#预设,不应该存在任何性能问题。