简体   繁体   中英

What is the difference between seq and $ in haskell?

我无法理解seq$的区别, seq强制执行求值, $也做同样的事情。

($) :: (a -> b) -> a -> b takes a function and a value. It returns a thunk. When that thunk is forced, it produces the result of applying the function to the value.

> let x = succ $ (2 :: Int)

> :sprint x
x = _

> x
3

> :sprint x
x = 3

($) is exactly equivalent to ordinary function application, but with lower operator precedence, which can be useful for avoiding some parentheses.

print (take 10 (map (* 2) (filter even [1..])))

print $ take 10 $ map (* 2) $ filter even [1..]

seq :: a -> b -> b is very different: it arranges a dependency between its result and its first argument, so that when the result is forced, the first argument is evaluated first:

> let y = succ (1 :: Int)

> :sprint y
y = _

> let z = y `seq` (3 :: Int)

> :sprint z
z = _

> z
3

> :sprint z
z = 3

> :sprint y
y = 2

Here, y and z are initially unevaluated thunks. But evaluating z has the side effect of also evaluating y , because we've arranged a dependency on y using seq . You can also observe the order of evaluation using trace from Debug.Trace :

> import Debug.Trace

> (trace "a evaluated" ()) `seq` (trace "b evaluated" ())
a evaluated
b evaluated
()

> let p = (trace "a evaluated" (1 :: Int), trace "b evaluated" (2 :: Int))

> :sprint p
p = (_,_)

> snd p
b evaluated
2

> :sprint p
p = (_,2)

> fst p
a evaluated
1

> :sprint p
p = (1,2)

seq is a low-level operation that's mainly useful for performance reasons, because it lets you control when a thunk is evaluated. For example, seq is used in the definition of foldl' to ensure that the result of each step of the fold is evaluated before proceeding to the next step. Its lazy cousin foldl doesn't do this, so it often accumulates a deeply nested series of thunks, which can result in a stack overflow when evaluated.

Haskell is lazily evaluated by default. So a "normal" function with seq 's signature ( a -> b -> b ) would ignore its first argument and then return its second - it couldn't do anything else with either argument because it doesn't know what they are!

But seq is a bit special and does something slightly different. Instead, it strictly evaluates its first argument and then returns its second argument. This is useful for a variety of purposes, such as forcing the order of side-effects, or for preventing large thunks from accumulating during a calculation. You can find out more info here: https://wiki.haskell.org/Seq .

As @palik said, the type signature of $ is completely different, and it does something different: it applies a function to an argument. The difference between $ and ordinary function application is that it has very low operator precedence, which lets you avoid writing a lot of parentheses.

seq and ($) have different type signatures:

λ> :t seq
seq :: a -> b -> b
λ> :t ($)
($) :: (a -> b) -> a -> b
λ> (+1) `seq` 2
2
λ> (+1) $ 2
3

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM