I can't sleep! :)
I've written small program building double linked list in Haskell. The basic language's property to make it was lazy evaluation (see the bunch of code below). And my question is can I do the same in a pure functional language with eager evaluation or not? In any case, what properties eager functional language must have to be able to build such structure (impurity?)?
import Data.List
data DLList a = DLNull |
DLNode { prev :: DLList a
, x :: a
, next :: DLList a
}
deriving (Show)
walkDLList :: (DLList a -> DLList a) -> DLList a -> [a]
walkDLList _ DLNull = []
walkDLList f n@(DLNode _ x _) = x : walkDLList f (f n)
-- Returns first and last items.
makeDLList :: [a] -> (DLList a, DLList a)
makeDLList xs = let (first, last) = step DLNull xs in (first, last)
where
step prev [] = (DLNull, prev)
-- Here I use laziness. 'next' is not built yet, it's a thunk.
step prev (x : xs) = let this = DLNode prev x next
(next, last) = step this xs
in (this, last)
testList :: [Int] -> IO ()
testList l = let
(first, last) = makeDLList l
byNext = walkDLList next first
byPrev = walkDLList prev last
in do
putStrLn $ "Testing: " ++ show l
print byNext
print byPrev
main = do
testList []
testList [1, 2, 3, 4]
A doubly-linked list can be implemented in a purely functional way in an eager language as a zipper on a singly-linked list. See, for example, Rosetta Code > Doubly-linked list > OCaml > Functional.
As long as a language has something like closures, lambdas etc. you can always simulate lazyness. You could rewrite that code even in Java (without mutating variables etc), you just need to wrap every "lazy" operation in something like
interface Thunk<A> {
A eval();
}
Of course this would look terrible, but it is possible.
In the non-backtracking subset of Prolog, which can be seen as explicitly set-once eager pure functional language, you can build the doubly-linked lists easily. It's the referential transparency that makes it hard in Haskell, for it forbids the Prolog's explicit setting of the named, explicitly not-yet-set logical variables, and instead forces Haskell to achieve same effect in the warped way of "tying the knot". I think.
Plus, there really isn't much difference between Haskell's guarded recursion under lazy evaluation vs. Prolog's open-ended lists built in tail-recursion modulo cons fashion. IMO. Here's for instance an example of lazy lists in Prolog. The memoized shared storage is used as universal access mediator, so the results of previous calculations can be arranged to be cached.
Come to think of it, you can use C in a restrictive manner as an eager pure functional language, if you never reset any variable nor any pointer once it is set. You still have null pointers, just as Prolog has variables, so it is too, explicitly set-once. And of course you can build doubly-linked lists with it.
So the only question that remains is, do you admit such set-once languages as pure?
来源:https://stackoverflow.com/questions/9277176/lazy-vs-eager-evaluation-and-double-linked-list-building