No implicit argument of type Nothing?

Hi, I recently started learning scala and I’m amazed by its cool features :slight_smile:
One thing I’m stumbling upon is how implicit works.

I read this cool article on generic programming in scala 3 and tried to implement a utils package to convert a case class to a list of string so I can write it into csv. Basically I just copy and pasted the code in the article, and tried to make it into a package.

Here’s the package I made at src/main/scala/myutils/caseclass2csv.scala

package myutils.caseclass2csv

class CaseClass2CSV:

  type Row = List[String]

  trait FieldEncoder[A]:
    def encodeField(a: A): String

  given FieldEncoder[Int] with
    def encodeField(x: Int) = x.toString

  given FieldEncoder[Boolean] with
    def encodeField(x: Boolean) = if x then "true" else "false"

  given FieldEncoder[String] with
    def encodeField(x: String) = x

  trait RowEncoder[A]:
    def encodeRow(a: A): Row

  given RowEncoder[EmptyTuple] with
    def encodeRow(empty: EmptyTuple) =
      List.empty

  given [H: FieldEncoder, T <: Tuple: RowEncoder]: RowEncoder[H *: T] with
    def encodeRow(tuple: H *: T) =
      summon[FieldEncoder[H]].encodeField(tuple.head) :: summon[RowEncoder[T]]
        .encodeRow(tuple.tail)
  
  def tupleToCsv[X <: Tuple: RowEncoder](tuple: X): List[String] =
    summon[RowEncoder[X]].encodeRow(tuple)

This is my main/scala/Main.scala

import myutils.caseclass2csv.CaseClass2CSV

@main def hello: Unit = {
  println(CaseClass2CSV().tupleToCsv((42, true, "Hello")))
}

When I run Main.scala, I get the following error.

<path to my project>/src/main/scala/Main.scala:4:58
no implicit argument of type Nothing was found for an implicit parameter of method tupleToCsv in class CaseClass2CSV
  println(CaseClass2CSV().tupleToCsv((42, true, "Hello")))

I guess I’m making a trivial mistake only beginners make, but I could not resolve the issue after several days.
What am I doing wrong here, and how can I fix it?

Also is this the right way to make a package in scala? I come from Python and have less knowledge in Java, so I feel I may have not followed the best practices of making a package (such as, should I separate the traits in a different file, or how to make it possible to import only the function tupleToCsv, those kind of stuffs).

Thanks in advance!

Given instances must be in scope on the call site, but wrapping the given declarations in a class like this, they’ll only be in scope inside this very class.

The simplest fix probably is to switch CaseClass2CSV from class to object (and consequently the invocation to CaseClass2CSV.tupleToCsv(...)).

Note that you get the error when compiling, not when running - that’s a very important distinction in Scala. :slight_smile:

As for code organization… The main class should be part of a package, as well - could be the myutils.caseclass2csv, as well (no import required, then), or any other. Putting everything together in one single entity (i.e. object) is fine for an example like this, but aggregating cohesive parts in self-contained units (as hinted in the article: BaseEncoders, TupleEncoders,…) is a good idea in general, preparing for bigger code bases. (You may have to tinker with more imports, particularly given imports, then, depending on the concrete code organization.)

2 Likes

Thank you very much! I was able to fix it by following your suggestion.
I must study the difference between class and object :sweat_smile:

Yeah, as you say it’s pretty nice that the compiler can tell me which part should be fixed before running, which never happens in Python :laughing:

Next I will try to split files into myutils/baseencoders.scala and myutils/tupleencoders.scala! Thank you for the help!

Here’s what I did for better code organization.

├── Main.scala
└── myutils
    ├── BaseEncoder.scala
    ├── CaseClass2CSV.scala
    ├── FieldEncoder.scala
    ├── RowEncoder.scala
    └── TupleEncoder.scala

Each file is as follows.

BaseEncoder.scala

package myutils
package encoder
package instances

given FieldEncoder[Int] with
  def encodeField(x: Int) = x.toString

given FieldEncoder[Boolean] with
  def encodeField(x: Boolean) = if x then "true" else "false"

given FieldEncoder[String] with
  def encodeField(x: String) = x

CaseClass2CSV.scala

package myutils
package encoder
package instances
package func
 
def tupleToCsv[X <: Tuple: RowEncoder](tuple: X): List[String] =
  summon[RowEncoder[X]].encodeRow(tuple)

FieldEncoder.scala

package myutils
package encoder 

trait FieldEncoder[A]:
  def encodeField(a: A): String

RowEncoder.scala

package myutils
package encoder 

type Row = List[String]

trait RowEncoder[A]:
  def encodeRow(a: A): Row

TupleEncoder.scala

package myutils
package encoder 
package instances

given RowEncoder[EmptyTuple] with
  def encodeRow(empty: EmptyTuple) =
    List.empty

given [H: FieldEncoder, T <: Tuple: RowEncoder]: RowEncoder[H *: T] with
  def encodeRow(tuple: H *: T) =
    summon[FieldEncoder[H]].encodeField(tuple.head) :: summon[RowEncoder[T]]
      .encodeRow(tuple.tail)

I ran the following main and it works nicely :slight_smile:

import myutils.encoder.instances.func.tupleToCsv
import myutils.encoder.instances.given

@main def hello: Unit = {
  println(tupleToCsv((42, true, "Hello")))
}

Any suggestion to make it better is welcome! I’m very new to scala and it would be lovely to have some advice from experienced scala users.

Looks fine at first glance. The granularity may be a bit excessive (lots of very small files), but this will even out with bigger projects.

Although perhaps not strictly required by the Scala compiler, I would stick to the convention that packages should mirror the directory structure - i.e. source files declaring package x.y.z should reside in directory x/y/z (or vice versa). Generic Java/JVM tooling may get confused otherwise.

Documentation is a bit difficult right now, I think. Scala 3 documentation is at least in parts still work in progress, and it assumes some familiarity with Scala 2. Scala 2 documentation in some parts assumes some familiarity with Java and JVM conventions. But this shouldn’t be a showstopper. Just go with the Scala 3 documentation and do a web search/check Scala 2 docs/ask here when things get blurry.

2 Likes

Thanks a lot! I organized the file paths as advised.
I appreciate your help, I’m glad there is a place I can ask questions so I can learn the new language!

1 Like