Chain

To concatenate two Python iterators, we use chain. itertools.chain

>>> it = chain([1,2], [3,4])

The resulting iterator consists of each element from the first argument (1 and 2), followed by each element of the second argument (3 and 4).

>>> list(it)
[1, 2, 3, 4]

The corresponding Haskell function (++) in Data.List and Prelude. is called (++). Equivalently, (++) may be replaced with the more general operator (<>), because conatenation is the semigroup operation for the list type.

λ> [1, 2] ++ [3, 4]
[1,2,3,4]

Variadic vs infix

The chain function is variadic; it can accept any number of arguments, not just two. This makes it easy to chain together any number of iterators without having to use nested function applications. The following two expressions are equivalent:

>>> ''.join(chain("ab", chain("cd", chain("ef", "gh"))))
'abcdefgh'

>>> ''.join(chain("ab", "cd", "ef", "gh"))
'abcdefgh'

Haskell doesn’t have variadic functions, but it does let you define infix operators, Infix operators are not language built-ins, and you can define your own. which can be used to get a similar convenient effect. The (++) function concatenates two lists, but infix notation makes it easy to chain together as many concatenations as you like.

>>> "ab" ++ "cd" ++ "ef" ++ "gh"
"abcdefgh"

We do not have to use parentheses to group the function applications, but if we did, it would look like this:

λ> "ab" ++ ("cd" ++ ("ef" ++ "gh"))
"abcdefgh"

Infinity

If any of the sequences in the chain is infinite, then it follows that the resulting sequence is also infinite. Here we extend the first example to add an infinite number of fives at the end, using the repeat function we showed previously:

>>> it = chain([1,2], [3,4], repeat(5))

>>> list(islice(it, 10))
[1, 2, 3, 4, 5, 5, 5, 5, 5, 5]

Regardless of whether the iterators ever stop, the code looks the same.

λ> xs = [1, 2] ++ [3, 4] ++ repeat 5

λ> take 10 xs
[1,2,3,4,5,5,5,5,5,5]

It only really makes sense to use an infinite list as the last part of a chain. There’s nothing stopping you from concatenating lists after the end of an infinite list, but it is pointless, since they will never be reached.

λ> xs = repeat 5 ++ [1, 2] ++ [3, 4]

λ> take 10 xs
[5,5,5,5,5,5,5,5,5,5]

chain.from_iterable

If you aren’t chaining together some fixed number of lists, but rather a list of lists, you can use chain.from_iterable. itertools.chain.from_iterable

>>> its = [[1, 2], [3, 4], repeat(5)]

>>> it = chain.from_iterable(its)

>>> list(islice(it, 10))
[1, 2, 3, 4, 5, 5, 5, 5, 5, 5]

The Haskell equivalent concat in Data.List, Data.Foldable, and Prelude is called concat. Equivalently, concat may be replaced with the more general fold from Data.Foldable.

λ> xss = [[1, 2], [3, 4], repeat 5]

λ> xs = concat xss

λ> take 10 xs
[1,2,3,4,5,5,5,5,5,5]

Join Type Classes for courses and projects to get you started and make you an expert in FP with Haskell.