I’d like to rewrite such function into F#:
zipWith' :: (a -> b -> c) -> (a -> c) -> (b -> c) -> [a] -> [b] -> [c]
zipWith' _ _ h [] bs = h `map` bs
zipWith' _ g _ as [] = g `map` as
zipWith' f g h (a:as) (b:bs) = f a b:zipWith f g h as bs
My first attempt was:
let inline private map2' (xs : seq<'T>) (ys : seq<'U>) (f : 'T -> 'U -> 'S) (g : 'T -> 'S) (h : 'U -> 'S) =
let xenum = xs.GetEnumerator()
let yenum = ys.GetEnumerator()
seq {
let rec rest (zenum : IEnumerator<'A>) (i : 'A -> 'S) =
seq {
yield i(zenum.Current)
if zenum.MoveNext() then yield! (rest zenum i) else zenum.Dispose()
}
let rec merge () =
seq {
if xenum.MoveNext()
then
if yenum.MoveNext()
then yield (f xenum.Current yenum.Current); yield! (merge ())
else yenum.Dispose(); yield! (rest xenum g)
else
xenum.Dispose()
if yenum.MoveNext()
then yield! (rest yenum h)
else yenum.Dispose()
}
yield! (merge ())
}
However it can hardly be considered idiomatic. I heard about LazyList but I cannot find it anywhere.
As Brian mentioned, F# provides a usual Haskell-style lazy list in the PowerPack, so you can use that. Unfortunately, there is no good way to express this kind of thing using standard F# sequence expressions, because they can only express computations that read data from a single sequence using
for(in your case, you’d need to read from multiple sequences).However, it is possible to write a computation (similar to
seq { .. }) for working withIEnumerator<T>– it is an imperative computation that modifies the enumerator behind the scenes, but it can be used for encoding patterns whenseqisn’t good enough. I’m planing to blog about it, but in the meantime, you can get it here (the code also includes the solution to your problem).Then you can write this:
The core part of the code pretty much copies the structure of the original Haskell solution, which makes this approach very attractive, but you can still use sequences directly, without copying data to some other data structure.