How does the Haskell rec keyword work?

送分小仙女□ 提交于 2019-11-30 00:06:33

This bit of magic works due to haskells laziness. As you might know, Haskell evaluates a value when needed, not when defined. Thus, this works if you don't need the value fed in directly, or maybe later.

rec is implemented using the loop function of ArrowLoop. It is defined as followed:

class Arrow a => ArrowLoop a where
        loop :: a (b,d) (c,d) -> a b c

instance ArrowLoop (->) where
        loop f b = let (c,d) = f (b,d) in c

You can see: The output is just fed back as the input. It will be calculated just once, because Haskell will only evaluate d when it's needed.

Here's an actual example of how to use the loop combinator directly. This function calculates all the powers of it's argument:

powers = loop $ \(x,l) -> (l,x:map(*x)l)

(You could also write it like this instead: powers x = fix $ (x :) . map (*x))

How does it works? Well, the infinite list of powers is in the l argument. The evaluation looks like this:

powers = loop $ \(x,l) -> (l,x:map(*x)l) ==>
powers b = let (c,d) = (\(x,l) -> (l,x:map(*x)l)) (b,d) in c ==>
powers b = let (c,d) = (d,b:map(*b)d) in d ==> -- Now  we apply 2 as an argument
powers 2 = let (c,d) = (d,2:map(*2)d) in d ==>
         = let (c,(2:d)) = (d,2:map(*2)d) in c ==>
         = let (c,(2:4:d)) = ((2:d),2:map(*2)(2:d)) in c ==>
         = let (c,(2:4:8:d)) = ((2:4:d),2:map(*2)(2:4:d)) in  ==> -- and so on

Here is a real-ish example:

loop f b = let (c,d) = f (b,d) in c

f (b,d) = (drop (d-2) b, length b)

main = print (loop f "Hello World")

This program outputs "ld". The function 'loop f' takes one input 'b' and creates one output 'c'. What 'f' is doing is studying 'b' to produce 'length b' which is getting returned to loop and bound to 'd'.

In 'loop' this 'd=length b' is fed into the 'f' where it is used in the calculation in drop.

This is useful for tricks like building an immutable doubly linked list (which may also be circular). It is also useful for traversing 'b' once to both produce some analytic 'd' (such as length or biggest element) and to build a new structure 'c' that depends on 'd'. The laziness avoids having to traverse 'b' twice.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!