Different ways to build class tables in Scala?

An example in the form of a table, taken from the Scala programming book:

case class Address(street: String, city: String) case class Person(name: String, address: Address) trait ToJSON { def toJSON(level: Int = 0): String val INDENTATION = " " def indentation(level: Int = 0): (String,String) = (INDENTATION * level, INDENTATION * (level+1)) } implicit class AddressToJSON(address: Address) extends ToJSON { def toJSON(level: Int = 0): String = { val (outdent, indent) = indentation(level) s"""{ |${indent}"street": "${address.street}", |${indent}"city": "${address.city}" |$outdent}""".stripMargin } } implicit class PersonToJSON(person: Person) extends ToJSON { def toJSON(level: Int = 0): String = { val (outdent, indent) = indentation(level) s"""{ |${indent}"name": "${person.name}", |${indent}"address": ${person.address.toJSON(level + 1)} |$outdent}""".stripMargin } } val a = Address("1 Scala Lane", "Anytown") val p = Person("Buck Trends", a) println(a.toJSON()) println() println(p.toJSON()) 

The code works fine, but I have the impression (from some blog posts) that cool classes usually execute this way in scala:

 // src/main/scala/progscala2/implicits/toJSON-type-class.sc case class Address(street: String, city: String) case class Person(name: String, address: Address) trait ToJSON[A] { def toJSON(a: A, level: Int = 0): String val INDENTATION = " " def indentation(level: Int = 0): (String,String) = (INDENTATION * level, INDENTATION * (level+1)) } object ToJSON { implicit def addressToJson: ToJSON[Address] = new ToJSON[Address] { override def toJSON(address: Address, level: Int = 0) : String = { val (outdent, indent) = indentation(level) s"""{ |${indent}"street": "${address.street}", |${indent}"city": "${address.city}" |$outdent}""".stripMargin } } implicit def personToJson: ToJSON[Person] = new ToJSON[Person] { override def toJSON(a: Person, level: Int): String = { val (outdent, indent) = indentation(level) s"""{ |${indent}"name": "${a.name}", |${indent}"address": ${implicitly[ToJSON[Address]].toJSON(a.address, level + 1)} |$outdent}""".stripMargin } } def toJSON[A](a: A, level: Int = 0)(implicit ev: ToJSON[A]) = { ev.toJSON(a, level) } } val a = Address("1 Scala Lane", "Anytown") val p = Person("Buck Trends", a) import ToJSON.toJSON println(toJSON(a)) println(toJSON(p)) 

Which way is better or more correct? Any ideas are welcome.

+5
source share
1 answer

This is simply a call to the first class of ToJSON a "type class" (although it does not look like standardizing these terms, and even your second, more Scala-idiomatic version differs in many important ways, for example, type classes in Haskell).

One of the properties of type classes that I would consider as a definition is that they allow you to restrict common types. Scala provides special syntax to support this in the form of context boundaries, so I can write for example. following:

 import io.circe.Encoder def foo[A: Numeric: Encoder](a: A) = ... 

This limits type A both Numeric and Encoder instances.

This syntax is not available for the first ToJSON , and you will have to use something like viewing restrictions (now deprecated) or implicit implicit conversion options.

There are also many kinds of operations that cannot be provided in the first ToJSON style. For example, suppose we have a Monoid that uses the standard Scala type class encoding:

 trait Monoid[A] { def empty: A def plus(x: A, y: A): A } 

And we wanted to translate it into the first style, where we have a non- Monoid trait that will be the target of implicit conversions from types that we want to consider as monoidal. We were completely unlucky, since we do not have a type parameter that we can refer to our empty and plus signatures.

Another argument: the type classes in the standard library ( Ordering , CanBuildFrom , etc.) use the second style, like most third-party Scala libraries that you come across.

In short, never use the first version. It will work only when you have only operations with the form A => Whatever (for a specific concrete Whatever ), does not have pleasant syntactic support and is usually not considered an idiomatic community.

+18
source

Source: https://habr.com/ru/post/1244136/


All Articles