Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New approach for pretty printers #17

Merged
merged 9 commits into from
Nov 16, 2018
Merged

Conversation

pepegar
Copy link
Member

@pepegar pepegar commented Oct 20, 2018

clone of #14 . I don't really know when I closed it :)

src/main/scala/Printer.scala Outdated Show resolved Hide resolved
*/

package skeuomorph
package freestyle
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to create some tickets to rename this and also moving this project to the hk organization (under higherkindness.io group id). Makes sense?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I'll tackle those in #18

""".stripMargin
case TProduct(name, fields) =>
val printFields = fields.map(f => s"${f.name}: ${f.tpe}").mkString(", ")
s"@message case class $name($printFields)"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

final case class?

src/main/scala/freestyle/print.scala Outdated Show resolved Hide resolved
def service[T](implicit T: Basis[FreesF, T]): Printer[Service[T]] =
((konst("@service(") *< serializationType >* konst(") trait ")) >*<
(string >* konst("[F[_]] {") >* newLine) >*<
(mkList(operation, "\n") >* newLine >* konst("}"))).contramap(serviceTuple)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or at least, I'd suggest for extracting that out to a val

@pepegar pepegar force-pushed the new-approach-for-pretty-printers branch from 6fb6621 to 8a2d213 Compare November 14, 2018 15:19

import cats.Contravariant

trait Divisible[F[_]] extends Contravariant[F] {
Copy link

@diesalbla diesalbla Nov 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this related to Ed Kmett's Divisible library? If it is, then a similar thing has already been added to cats, under the inconspicuous name of ContravariantMonoidal.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, it's a port of kmett's Divisible indeed. But ContravariantMonoidal is lacking divide right?

Copy link

@diesalbla diesalbla Nov 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not lacking it. It just so happens that, in a very curious leap of abstraction, they use Semigroupal[F].product to represent both the divide method from the Divisible type-class, and the product (the old |@|) from the Applicative class.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the same token, the method conquer has a very similar signature to ContravariantMonoidal.trivial. Nevertheless, even if you base the code of cats, It may be easier to keep the Divisible syntax.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I'm keeping the syntax but dropping the typeclass in favor of ContravariantMonoidal. Thanks for pointing me to it Diego!

Copy link
Member

@JesusMtnez JesusMtnez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job 👍

import cats.syntax.compose._
import cats.instances.function._

case class Printer[A](print: A => String)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was there a reason for using a case class? Libraries like circe usually write the encoders or printers as traits with the abstract method:

trait Printer[A]{
    def print(a: A): String

    def contraMap[B](f: B => A): Printer[B]
}

There are some benefits of these approach. First, you save a bit of efficiency, but turning two objects (case class and functions) into a single one. Also, in Scala 2.12, thanks to the SAM syntax, you can assign a lambda to it

val print: Printer[Person] = { x => s"${x.name}, ${x.age} years." } 

On another matter: should we make A contravariant, trait Printer[- A]?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make it more performant? I didn't know it but I like your idea and the fact that we can benefit from SAM, I'm changing Printer to a trait.

I don't see the benefit of explicitly making the type param contravariant, I don't want to encourage subtyping :)

Copy link

@diesalbla diesalbla left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few little comments on the Printer section. Great work!

def print(a: A): String = f(a)
}

def konst(str: String): Printer[Unit] =

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that, with SAM, this is just

def konst(str: String): Printer[Unit] = _ => str

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't use SAM in the library, since I crosscompile it to 2.11 too :(

def optional[A](p: Printer[A]): Printer[Option[A]] =
Printer(_.fold("")(p.print))

def mkList[A](p: Printer[A], sep: String): Printer[List[A]] =

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following the naming of monadic parsing libraries (which is just the reverse), I would call this sepBy.

def konst(str: String): Printer[Unit] =
Printer(Function.const(str))

def space = konst(" ")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This def can be val. Since it is a public method, I would add the type of it.


def space = konst(" ")

def newLine = konst("\n")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also can be a val.


def newLine = konst("\n")

val string: Printer[String] = Printer(identity)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add a val empty: Printer[Unit] = _ => ""?

Copy link
Member Author

@pepegar pepegar Nov 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure!

implicit val divisiblePrinter: Decidable[Printer] = new Decidable[Printer] {
def contramap[A, B](fa: Printer[A])(f: B => A): Printer[B] =
Printer(f >>> fa.print)
def conquer[A]: Printer[A] =

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can refer the empty printer from the previous comment.

Printer(_.fold("")(p.print))

def mkList[A](p: Printer[A], sep: String): Printer[List[A]] =
Printer {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, by SAM, you do not need to add the Printer here. It will take the pattern-match as a literal for PartialFunction, which in Scala is a Function (with some controversy I know).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't introduce SAM inside the lib because crosscompiling to 2.11...

@pepegar pepegar merged commit ceaa107 into master Nov 16, 2018
@pepegar pepegar deleted the new-approach-for-pretty-printers branch November 16, 2018 10:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants