
Somebody suggested I post this here if I wanted feedback. So I was thinking about the ReverseState monad I saw mentioned on r/haskell a couple days ago, and playing around with the concept of information flowing two directions when I came up with this function: bifold :: (l -> a -> r -> (r,l)) -> (l,r) -> [a] -> (r,l) bifold _ (l,r) [] = (r,l) bifold f (l,r) (a:as) = (ra,las) where (ras,las) = bifold f (la,r) as (ra,la) = f l a ras (I'm sure someone else has come up with this before, so I'll just say I discovered it, not invented it). Basically, it's a simultaneous left and right fold, passing one value from the start of the list toward the end, and one from the end toward the start. It lets you do some interesting stuff, like filter based on positionor other left-dependent information: evenIndexed :: [a] -> [a] evenIndexed = fst . bifold alternate (0,[]) where alternate 0 x xs = (x:xs, 1) alternate 1 _ xs = (xs, 0) maximums :: (Ord a) => [a] -> [a] maximums [] = [] maximums (a:as) = a : (fst $ bifold (\m a l -> if a > m then (a:l,a) else (l,m)) (a,[]) as) As long as you don't examine the left-to-right value, it can still work on infinite lists: ghci> take 20 $ evenIndexed [0..] [0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38] Also, it can be used for corecursive data (or, at least, doubly-linked lists): data DList a = Start { first :: DList a } | Entry { value :: a, next :: DList a, prev :: DList a } | End { last :: DList a } deriving (Eq) ofList :: [a] -> (DList a, DList a) ofList as = (start,end) where start = Start first end = End last (first,last) = bifold mkEntry (start,end) as mkEntry p v n = let e = Entry v n p in (e,e) It's just been running around my head all night, so I thought I'd share.

On 11/29/10 21:41, Noah Easterly wrote:
Somebody suggested I post this here if I wanted feedback.
So I was thinking about the ReverseState monad I saw mentioned on r/haskell a couple days ago, and playing around with the concept of information flowing two directions when I came up with this function:
bifold :: (l -> a -> r -> (r,l)) -> (l,r) -> [a] -> (r,l) bifold _ (l,r) [] = (r,l) bifold f (l,r) (a:as) = (ra,las) where (ras,las) = bifold f (la,r) as (ra,la) = f l a ras
(I'm sure someone else has come up with this before, so I'll just say I discovered it, not invented it).
Basically, it's a simultaneous left and right fold, passing one value from the start of the list toward the end, and one from the end toward the start. [snip]
Hi Noah,
I've not examined bifold real close, but the description:
passing one value
from the start of the list toward the end, and one from the end toward
the start.
suggested to me that bifold might be similar to the function, Q, of
section 12.5 equation 1) on p. 15 of:
http://www.thocp.net/biographies/papers/backus_turingaward_lecture.pdf
Now Q takes just 1 argument, a function. Also, the f function defined
with Q can be short-circuited, IOW, it may not process all the
elements is a list. Also, it may not even act on a list; however,
that just means it's more general and could be easily specialized to
just act on lists.
Anyway, I'll reproduce some of the reference's section 12.5 here:
f == p -> q; Q(f)
where:
Q(k) == h <*> [i, k<*>j]
and where(by p. 4 of reference):
<*> is the function composition operator:
(f <*> g) x == f(g(x))
and where(by p. 9 of reference):
[f,g] x =

On Tue, Nov 30, 2010 at 9:37 AM, Larry Evans
suggested to me that bifold might be similar to the function, Q, of section 12.5 equation 1) on p. 15 of:
http://www.thocp.net/biographies/papers/backus_turingaward_lecture.pdf
Now Q takes just 1 argument, a function. Also, the f function defined with Q can be short-circuited, IOW, it may not process all the elements is a list. Also, it may not even act on a list; however, that just means it's more general and could be easily specialized to just act on lists.
Anyway, I'll reproduce some of the reference's section 12.5 here:
f == p -> q; Q(f)
where:
Q(k) == h <*> [i, k<*>j]
and where(by p. 4 of reference): <*> is the function composition operator: (f <*> g) x == f(g(x)) and where(by p. 9 of reference): [f,g] x =
and where(by p. 8 of reference) is a sequence of objects, x1,x2,...xn (like haskell's tuple: (x1,x2,...,xn) and where(by p. 8 of reference): (p -> g; h)(x) means "if p(x) then do g(x) else do h(x)" for any functions, k, p, g, h, i, j.
p. 16 provides a nice shorthand for the result of that function:
Q^n(g) = /h<*>[i,i<*>j,...,i<*>j^(n-1),g<*>J^n
where:
f^n = f<*>f<*>....<*> f for n applications of f /h<*>[f1,f2,...,fn] is defined on p. 13 of reference.
[snip] Thanks, Larry, this is some interesting stuff. I'm not sure yet whether Q is equivalent - it may be, but I haven't been able to thoroughly grok it yet. For uniformity, I shifted the notation you gave to Haskell: (.^) :: (a -> a) -> Int -> a -> a f .^ 0 = id f .^ n = f . (f .^ (n - 1)) (./) :: (b -> c -> c) -> [a -> b] -> (a->c) -> a -> c (./) = flip . foldr . \h f g -> h <$> f <*> g _Q_ :: (b -> c -> c) -> (a -> b) -> (a -> a) -> (a -> c) -> a -> c _Q_ h i j k = h <$> i <*> (k . j) So the shorthand just states the equivalence of (_Q_ h i j) .^ n and (./) h [ i . (j .^ m) | m <- [0 .. n-1] ] . ( . (j .^ n)) Looking at it that way, we can see that (_Q_ h i j) .^ n takes some initial value, unpacks it into a list of size n+1 (using i as the iterate function), derives a base case value from the final value (and some function k) maps the initial values into a new list, then foldrs over them. The _f_ function seems to exist to repeat _Q_ until we reach some stopping condition (rather than n times) _f_ :: (b -> c -> c) -> (a -> b) -> (a -> a) -> (a -> Bool) -> (a -> c) -> a -> c _f_ h i j p q a = if p a then q a else _Q_ h i j (_f_ h i j p q) a No simple way to pass values from left to right pops out at me, but I don't doubt that bifold could be implemented in foldr, and therefore there should be *some* way. I'll have to give the paper a thorough reading (which, I apologize, I haven't had time to do yet). Thanks again!

On 11/30/10 13:43, Noah Easterly wrote:
On Tue, Nov 30, 2010 at 9:37 AM, Larry Evans
mailto:cppljevans@suddenlink.net> wrote: suggested to me that bifold might be similar to the function, Q, of section 12.5 equation 1) on p. 15 of:
http://www.thocp.net/biographies/papers/backus_turingaward_lecture.pdf
[snip]
[snip]
Thanks, Larry, this is some interesting stuff.
I'm not sure yet whether Q is equivalent - it may be, but I haven't been able to thoroughly grok it yet.
For uniformity, I shifted the notation you gave to Haskell:
(.^) :: (a -> a) -> Int -> a -> a f .^ 0 = id f .^ n = f . (f .^ (n - 1))
(./) :: (b -> c -> c) -> [a -> b] -> (a->c) -> a -> c (./) = flip . foldr . \h f g -> h <$> f <*> g
_Q_ :: (b -> c -> c) -> (a -> b) -> (a -> a) -> (a -> c) -> a -> c _Q_ h i j k = h <$> i <*> (k . j)
So the shorthand just states the equivalence of (_Q_ h i j) .^ n and (./) h [ i . (j .^ m) | m <- [0 .. n-1] ] . ( . (j .^ n))
Looking at it that way, we can see that (_Q_ h i j) .^ n takes some initial value, unpacks it into a list of size n+1 (using i as the iterate function), derives a base case value from the final value (and some function k) maps the initial values into a new list, then foldrs over them.
The _f_ function seems to exist to repeat _Q_ until we reach some stopping condition (rather than n times)
_f_ :: (b -> c -> c) -> (a -> b) -> (a -> a) -> (a -> Bool) -> (a -> c) -> a -> c _f_ h i j p q a = if p a then q a else _Q_ h i j (_f_ h i j p q) a
No simple way to pass values from left to right pops out at me, but I don't doubt that bifold could be implemented in foldr, and therefore there should be *some* way.
Hi Noah, The attached is my attempt at reproducing your code and also contains an alternative attempt at emulating the code in section 12.5 of: http://www.thocp.net/biographies/papers/backus_turingaward_lecture.pdf However, ghci compilation of bifold produces an error message: BifoldIfRecur.hs:20:19: parse error on input `=' OTOH, when this code is commented out and the test variable is printed, the output is: [1,2,3,999,3,2,1] [3,2,1,999] [1,2,3,999] [(),(),()] The first line is for a call to if_recur. The other two are for foldl and foldr where the binary operator is (flip (:)) and (:), respectively. The suffix after 999 of the 1st line suggests to me that if_recur does something like foldr with the else_ function is called, after which something like foldr is done, as indicated by the [1,2,3] prefix before 999 of the 1st line. So it seems that both foldr and foldl are being done during if_recur, IOW, it's a kinda bifold also. Hopefully this sheds some light on how section 12.5 is related to bifold; however, I'm still not completely sure what that relation is :( -regards, Larry

On 12/01/10 21:35, Larry Evans wrote:
On 11/30/10 13:43, Noah Easterly wrote: [snip]
Thanks, Larry, this is some interesting stuff.
I'm not sure yet whether Q is equivalent - it may be, but I haven't been able to thoroughly grok it yet.
[snip]
Hi Noah,
The attached is my attempt at reproducing your code and also contains an alternative attempt at emulating the code in section 12.5 of:
http://www.thocp.net/biographies/papers/backus_turingaward_lecture.pdf
The attached code is a revision of my previous if_recur attempt at emulated the section 12.5 code. This revision added the i function from the seciton 12.5 (instead of delegating that task to the h function). The 2nd attachment shows the output. It shows that by modifying the args to if_recur, you can reproduce the output from foldl or foldr.

On 12/01/10 21:35, Larry Evans wrote: [snip]
Hi Noah,
The attached is my attempt at reproducing your code [snip] However, ghci compilation of bifold produces an error message:
BifoldIfRecur.hs:20:19: parse error on input `='
[snip] Apparently I had some extra leading spaces in the last line. Taking those out (as shown in attached) results in a good reading of the file by ghci. -Larry
participants (2)
-
Larry Evans
-
Noah Easterly