I have a program that so far can read its input either from stdin or from a
file given on the command line.
It is parsed line by line in an iterator, as follows:
iterator parseFastqs(input: File): Fastq =
var
nameLine: string
nucLine: string
quaLine: string
while not input.endOfFile:
nameLine = input.readLine()
nucLine = input.readLine()
discard input.readLine()
quaLine = input.readLine()
yield makeFastq(nameLine, nucLine, quaLine)
This iterator is then passed to a procedure that further deals with the Fastq
things. So it appears that I need to wrap it into a closure iterator. I use the
following solution found on stackoverflow:
# https://stackoverflow.com/a/35697846
template toClosure(inlineIterator): auto =
## Wrap an inline iterator in a first-class closure iterator.
iterator closureIterator: type(inlineIterator) {.closure.} =
for elem in inlineIterator:
yield elem
closureIterator
(I was not able do directly make it a closure iterator. See
[https://stackoverflow.com/q/48260487/1878788](https://stackoverflow.com/q/48260487/1878788))
So after processing the command-line options, I proceed as follows:
when isMainModule:
let args = docopt(doc)
var
inFastqFilename: Value
# [declare other variables]
# [process args and set variable]
let inputFqs =
if not inFastqFilename:
toClosure(parseFastqs(stdin))
else:
let fh = open($inFastqFilename)
toClosure(parseFastqs(fh))
# [pass inputFqs and other variables to the function that does the main
processing]
Now I want to be able to process gzipped input, still either from stdin or from
a command-line passed file. I wanted to use this:
[https://github.com/nim-lang/zip/blob/master/zip/gzipfiles.nim](https://github.com/nim-lang/zip/blob/master/zip/gzipfiles.nim)
This seems to be based on streams rather than files. The two objects (File and
Stream) seem to have things in common in their interface, such as the
possibility to be parsed line by line using readLine. However, on other
aspects, they differ, which make them not completely interchangeable: With a
File, I can use endOfFile while with a Stream, I need to replace this with
atEnd.
So, first questions: **What are streams, what are they used for?**
The documentation is not really helpful for me:
> This module provides a stream interface and two implementations thereof: the
> FileStream and the StringStream which implement the stream interface for Nim
> file objects (File) and strings. Other modules may provide other
> implementations for this standard stream interface.
Anyway, I tried to use streams instead of files, and my code seemed to work
provided I changed endOfFile to atEnd:
let inputFqs =
if not inFastqFilename:
toClosure(parseFastqs(newFileStream(stdin)))
else:
let fh = open($inFastqFilename)
toClosure(parseFastqs(newFileStream(fh)))
But when I try to add gzipped input in the game:
let inputFqs =
if not inFastqFilename:
if openGZ:
# Not sure this is the correct way to deal with
# the absence of a newGZFileStream accepting a File:
toClosure(parseFastqs(newGZFileStream("stdin")))
else:
toClosure(parseFastqs(newFileStream(stdin)))
else:
if openGZ: # Error on this line
toClosure(parseFastqs(newGZFileStream($inFastqFilename)))
else:
let fh = open($inFastqFilename)
toClosure(parseFastqs(newFileStream(fh)))
I run into troubles:
Error: type mismatch: got (iterator (): Fastq{.closure, locks: 0.}) but
expected 'iterator (): Fastq{.closure, gcsafe, locks: 0.}'
So here is my second question: **What is happening, and what can I do about
it?**