#Aborting a search through a list# We said that the sorted-list implementation of a set was more efficient than the unsorted-list implementation, because as you were searching through the list, you could come to a point where you knew the element wasn't going to be found. So you wouldn't have to continue the search. If your implementation of lists was, say v1 lists plus the Y-combinator, then this is exactly right. When you get to a point where you know the answer, you can just deliver that answer, and not branch into any further recursion. If you've got the right evaluation strategy in place, everything will work out fine. But what if we wanted to use v3 lists instead? > Why would we want to do that? The advantage of the v3 lists and v3 (aka "Church") numerals is that they have their recursive capacity built into their very bones. So for many natural operations on them, you won't need to use a fixed point combinator. > Why is that an advantage? Well, if you use a fixed point combinator, then the terms you get won't be strongly normalizing: whether their reduction stops at a normal form will depend on what evaluation order you use. Our online [[lambda evaluator]] uses normal-order reduction, so it finds a normal form if there's one to be had. But if you want to build lambda terms in, say, Scheme, and you wanted to roll your own recursion as we've been doing, rather than relying on Scheme's native `let rec` or `define`, then you can't use the fixed-point combinators `Y` or `Θ`. Expressions using them will have non-terminating reductions, with Scheme's eager/call-by-value strategy. There are other fixed-point combinators you can use with Scheme (in the [week 3 notes](/week3/#index7h2) they were `Y′` and `Θ′`. But even with them, evaluation order still matters: for some (admittedly unusual) evaluation strategies, expressions using them will also be non-terminating. > The fixed-point combinators may be the conceptual stars. They are cool and mathematically elegant. But for efficiency and implementation elegance, it's best to know how to do as much as you can without them. (Also, that knowledge could carry over to settings where the fixed point combinators are in principle unavailable.) So again, what if we're using v3 lists? What options would we have then for aborting a search or list traversal before it runs to completion? Suppose we're searching through the list `[5;4;3;2;1]` to see if it contains the number `3`. The expression which represents this search would have something like the following form: .................. ~~> .................. false ~~> ............. ~~> ............. false ~~> ......... ~~> ......... true ~~> ? Of course, whether those reductions actually followed in that order would depend on what reduction strategy was in place. But the result of folding the search function over the part of the list whose head is `3` and whose tail is `[2; 1]` will *semantically* depend on the result of applying that function to the more rightmost pieces of the list, too, regardless of what order the reduction is computed by. Conceptually, it will be easiest if we think of the reduction happening in the order displayed above. Once we've found a match between our sought number `3` and some member of the list, we'd like to avoid any further unnecessary computations and just deliver the answer `true` as "quickly" or directly as possible to the larger computation in which the search was embedded. With a Y-combinator based search, as we said, we could do this by just not following a recursion branch. But with the v3 lists, the fold is "pre-programmed" to continue over the whole list. There is no way for us to bail out of applying the search function to the parts of the list that have head `4` and head `5`, too. We *can* avoid *some* unneccessary computation. The search function can detect that the result we've accumulated so far during the fold is now `true`, so we don't need to bother comparing `4` or `5` to `3` for equality. That will simplify the computation to some degree, since as we said, numerical comparison in the system we're working in is moderately expensive. However, we're still going to have to traverse the remainder of the list. That `true` result will have to be passed along all the way to the leftmost head of the list. Only then can we deliver it to the larger computation in which the search was embedded. It would be better if there were some way to "abort" the list traversal. If, having found the element we're looking for (or having determined that the element isn't going to be found), we could just immediately stop traversing the list with our answer. **Continuations** will turn out to let us do that. We won't try yet to fully exploit the terrible power of continuations. But there's a way that we can gain their benefits here locally, without yet having a fully general machinery or understanding of what's going on. The key is to recall how our implementations of booleans and pairs worked. Remember that with pairs, we supply the pair "handler" to the pair as *an argument*, rather than the other way around: pair (\x y. add x y) or: pair (\x y. x) to get the first element of the pair. Of course you can lift that if you want:
``extract_fst ≡ \pair. pair (\x y. x)``