我有一些结构与此等效的代码:
import Debug.Trace
newtype SomeExpensiveHiddenType = SCHT Double
expensive :: Double -> Double -> SomeExpensiveHiddenType
expensive a b = SCHT $ trace "call expensive" (*) a b
cheap :: SomeExpensiveHiddenType -> Double -> Double
cheap (SCHT x) c = trace "call cheap" (+) x c
f1 :: Double -> Double -> Double -> Double
f1 a b c = let x = expensive a b in cheap x c
ief1
是一个基于前两个参数计算昂贵结果的函数,然后将其与第三个参数一起使用。我曾希望前两个参数的部分应用,然后重复应用第三个参数会导致昂贵的计算只运行一次。不幸的是,这种情况并非如此:
test1 = do
putStrLn "test 1"
let p = f1 2 3
print (p 0.1)
print (p 0.2)
print (p 0.3)
结果是:
*Main> test1
test 1
call cheap
call expensive
6.1
call cheap
call expensive
6.2
call cheap
call expensive
6.3
*Main>
我想出了似乎是一个解决方案:
newtype X a = X { unX :: a }
f2 :: Double -> Double -> X (Double -> Double)
f2 a b = let x = expensive a b in X (cheap x)
test2 = do
putStrLn "test 2"
let p = unX $ f2 2 3
print (p 0.1)
print (p 0.2)
print (p 0.3)
导致:
*Main> test2
test 2
call cheap
call expensive
6.1
call cheap
6.2
call cheap
6.3
*Main>
但这似乎很混乱。有没有一种更简洁的方法可以避免对昂贵计算的冗余调用?