All the encoding tutorials, such as this one, matter-of-factly call the
asCsvWriter
method of File
, when File
does not in fact have such a method. This works thanks to the
CsvSink
type class.
What happens is, any type A
such that there exists an implicit instance of CsvSink[A]
in scope will
be enriched with various useful methods for CSV serialization.
Various default implementations are automatically in scope, such as one for Writer
or OutputStream
, but the most
useful one is File
.
Reduced to its simplest expression, a CsvSink
is essentially a A => Writer
- that is, a function that takes an
A
and turns it into a Writer
(note that this currently does not leave room for errors and implementations are
forced to throw. This is something that will be fixed in later releases).
If you can write such a function, you can trivially turn it into a valid instance of CsvSink
- for example,
File
:
import kantan.csv._
import java.io._
import scala.io._
implicit def fileOutput(implicit c: Codec): CsvSink[File] =
CsvSink.from(f => new OutputStreamWriter(new FileOutputStream(f), c.charSet))
Most of the time though, it’s easier to turn the type you wish to provide an instance for into a type that already has
an instance - in our example, File
can easily be turned into an OutputStream
, and OutputStream
already has
a CsvSink
instance. This is achieved through contramap
:
import kantan.csv._
import java.io._
implicit val fileOutput: CsvSink[File] =
CsvSink[OutputStream].contramap(f => new FileOutputStream(f))