在第8章与思维类型我了解到fmap Sum的一部分
fastSum :: [Int] -> Int
fastSum = getSum . mconcat . fmap Sum
Run Code Online (Sandbox Code Playgroud)
有O(n)运行时成本,而使用coerce则避免了这种开销。
我知道newtypes 没有表示开销,但我不明白的是将 newtype 构造函数映射到列表上的运行时效果是什么。我认为这只会有编译时开销,它应该只是O(1),因为编译器只需要知道fmap SomeNewtypeCtr表达式的类型。
由于优化,很难理解 Haskell 在这种情况下到底做了什么。Haskell 只要求结果是什么,而不是如何获得结果。
一些可能性:
fmap Sum 执行列表扫描,并复制每个单元格和每个元素;fmap Sum 执行列表扫描,并复制每个单元格但不复制元素(新单元格指向旧元素);fmap Sum 根本不扫描列表并自动优化为无操作。我尝试过godbolt.org检查生成的Core 和程序集。请注意,它仍然使用旧的 GHC 8.6.2。不过,我打开优化 ( -O2) 并编译
foo :: [Int] -> [Sum Int]
foo = fmap Sum
Run Code Online (Sandbox Code Playgroud)
并获得
foo = Example.foo1 `cast` (.....)
Example.foo1 = \ (v_a1iF :: [Int]) -> v_a1iF
Run Code Online (Sandbox Code Playgroud)
因此foo成为恒等函数,适当地强制,产生装配
movq %r14,%rbx
andq $-8,%rbx
jmp *(%rbx)
Run Code Online (Sandbox Code Playgroud)
粗略地说,这应该相当于 GHC 运行时系统中的立即返回。
结论:Data.Coerce.coerce非常好,因为它确保了无操作,但即使是普通的 Haskell,经过优化,也能出奇地高效。