自定义Haskell严格应用操作符$!为何内存表现糟糕?
$! Leads to High Memory Usage Let's break down the issue step by step—your code looks almost right, but there's a subtle fixity problem that's throwing everything off.
First, let's recall how Haskell parses operator expressions. When you define a custom operator like $! without specifying its fixity (priority and associativity), Haskell uses default rules: left-associative with priority 9. But here's the catch: the + operator has priority 6, which is lower than 9. That means your line:
go (x:xs) total = go xs $! total + x
isn't being parsed the way you think it is. Instead of:
go xs $! (total + x) -- What you intended
it's actually being parsed as:
(go xs $! total) + x -- What Haskell is doing
This changes everything. Your $! definition uses x seq f x, so in this parsed version, we're forcing total (the old accumulator) to WHNF, then calling go xs total (which returns a thunk, since it's a recursive call), and then adding x to that thunk. Each recursive step builds up a new thunk instead of evaluating the accumulator immediately, leading to a huge chain of unevaluated expressions in memory—hence the high memory usage.
The Fix
You need to match the fixity of the standard library's $!, which is right-associative with priority 0 (the lowest possible, so it gets parsed last). Add this line right after your import:
infixr 0 $!
With this fixity declaration, Haskell will parse go xs $! total + x as go xs $! (total + x)—exactly what you wanted. Now, $! will force total + x to WHNF (a fully evaluated Int, since numeric types' WHNF is their concrete value) before passing it to the next go call. This keeps the accumulator evaluated at every step, preventing thunk buildup and keeping memory usage low.
Verify the Corrected Code
Here's the fixed version for reference:
import Prelude hiding ( ($!) ) infixr 0 $! -- Add this fixity declaration ($!) :: (a -> b) -> a -> b ($!) f x = x `seq` f x mysum :: [Int] -> Int mysum list0 = go list0 0 where go [] total = total go (x:xs) total = go xs $! total + x main = print $ mysum [1..1000000]
This should now run with constant memory usage, just like you expected.
内容的提问来源于stack exchange,提问作者Marronnier

