What happens in this template?

in Data.List, I saw this unfamiliar pattern match:

{-# NOINLINE [1] unsafeTake #-} unsafeTake :: Int -> [a] -> [a] unsafeTake !_ [] = [] unsafeTake 1 (x: _) = [x] unsafeTake m (x:xs) = x : unsafeTake (m - 1) xs 

I understand that ! removes thunks. Good, but _ ignored. There is something I don’t understand. The refinement is appreciated.

+5
source share
2 answers

This is BangPattern . This is an annotation to tell the compiler that this function should behave strictly in its first argument. This is equivalent to the following code:

 unsafeTake :: Int -> [a] -> [a] unsafeTake x [] = x `seq` [] unsafeTake 1 (x: _) = [x] unsafeTake m (x:xs) = m `seq` (x : unsafeTake (m - 1) xs) 

And that the field is strict, means that if the first argument is lower, the program stops:

 unsafeTake (error "kaboom") [] 

This will throw kaboom with an annotation of rigor, but it will not happen if he does not have it.

You can also put Bang patterns in data type definitions:

 data Tree a = Branch (Tree a) !a (Tree a) | Empty 

Then he will always evaluate the field containing a , in its weak head normal form. This means that he will not evaluate the entire structure. From haskell wiki

  • constructor (ultimately applied to the arguments), e.g. True, Just (square 42) or (:) 1
  • the built-in function is applied to arguments that are too small (maybe it doesn’t apply to it), for example (+) 2 or sqrt.
  • or the expression lambda abstraction \ x →.
+4
source

He tells the compiler that even if the second argument is [] , while he knows that he can apply the first option, he still needs to evaluate the first argument to head the normal form. This means that unsafeTake is "strict" in its first argument.

You can see the result if you try to evaluate unsafeTake (last (repeat 0)) [] compared to the same with take .

+1
source

Source: https://habr.com/ru/post/1261523/


All Articles