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

Add some libraries of plain old design patterns #81

Open
Atry opened this Issue Sep 5, 2017 · 46 comments

Comments

8 participants
@Atry

Atry commented Sep 5, 2017

Recently I reinvented an old approach to encode type class, which, in my honest opinion, obsoletes cats, spire-math and all libraries that use implicit parameter representation of type classes (including my Binding.scala).

The point of this approach is to create facades from abstract factories instead of implicit parameters, where facade and abstract factory are two design patterns described in GoF's book Design Patterns. The factory pattern part of this approach has been widely used in Scala 2's nsc compiler many years ago.

I have created a prototype of this approach at https://github.com/ThoughtWorksInc/DesignPattern.scala . This approach is very straightforward. I expect Typelevel maintainers and contributors may quickly understand the point, if you have a look at the source file of covariant functors.

The advantage of this approach including:

  • Libraries created in this approach are available in Java language.
  • Almost zero boilerplate code.
  • No obscure implicit resolution error message.
  • Almost zero cost abstractions. Facades can be AnyVals. As a result, it runs twice faster than Scalaz in this benchmark.
  • Fast compilation. The user code compiles twice faster than Scalaz in that benchmark, even if complicated Scalaz types are already hiden in a case class.

I am glad to contribute my work as a prototype, but I'm not intending to submit a project to Typelevel. Instead, I am here to ask existing typelevel projects to evolve into something new, not only for Scala hackers, but also influencing the entire Java world.

Share your thought seeing what we can build in the future, please.

@milessabin

This comment has been minimized.

Show comment
Hide comment
@milessabin

milessabin Sep 5, 2017

Member

I think it would help me to understand your proposal if you gave some simple examples of code using the existing Cats type class encoding and showed how that would be translated into your new scheme. I was looking for a simple Functor-based example in your project but couldn't see one ... apologies if I missed it.

Member

milessabin commented Sep 5, 2017

I think it would help me to understand your proposal if you gave some simple examples of code using the existing Cats type class encoding and showed how that would be translated into your new scheme. I was looking for a simple Functor-based example in your project but couldn't see one ... apologies if I missed it.

@Atry

This comment has been minimized.

Show comment
Hide comment

Atry commented Sep 5, 2017

@Atry

This comment has been minimized.

Show comment
Hide comment
@Atry

Atry Sep 5, 2017

Note that the concept of type class corresponds to factory pattern in GoF's design pattern, and transformer corresponds to decorator pattern, thus the name FactoryDecorator refers to monad transformer.

Atry commented Sep 5, 2017

Note that the concept of type class corresponds to factory pattern in GoF's design pattern, and transformer corresponds to decorator pattern, thus the name FactoryDecorator refers to monad transformer.

@Atry

This comment has been minimized.

Show comment
Hide comment
@milessabin

This comment has been minimized.

Show comment
Hide comment
@milessabin

milessabin Sep 5, 2017

Member

Those are all somewhat elaborate examples. A lot of the power of type classes comes from their use "in the small" ... so it would help to see a comparison on some simpler examples.

Member

milessabin commented Sep 5, 2017

Those are all somewhat elaborate examples. A lot of the power of type classes comes from their use "in the small" ... so it would help to see a comparison on some simpler examples.

@Atry

This comment has been minimized.

Show comment
Hide comment
@Atry

Atry Sep 5, 2017

Atry commented Sep 5, 2017

@SystemFw

This comment has been minimized.

Show comment
Hide comment
@SystemFw

SystemFw Sep 5, 2017

Here are some things I'd like to see (hope you don't mind the many questions, your proposal seems ambitious so I'd like to understand it :) )

  • How to declare type classes, and type classes hierarchies (this is covered in the covariant snippet I feel)
  • How to declare type class instances (The IO example is quite elaborate)
  • How to write code that uses a specific data type, and calls a type class method on it
  • How to write code that is generic in the type class instance (e.g. F[_]: Monad)
  • How to write an instance that depends on other instances (i.e. instance (Foo a) => instance Bar a)
  • How to write code with return type polymorphism (e..g 3.pure[IO])

I'm also curious to see if your approach can replace advanced uses of type classes like type level programming a la shapeless

SystemFw commented Sep 5, 2017

Here are some things I'd like to see (hope you don't mind the many questions, your proposal seems ambitious so I'd like to understand it :) )

  • How to declare type classes, and type classes hierarchies (this is covered in the covariant snippet I feel)
  • How to declare type class instances (The IO example is quite elaborate)
  • How to write code that uses a specific data type, and calls a type class method on it
  • How to write code that is generic in the type class instance (e.g. F[_]: Monad)
  • How to write an instance that depends on other instances (i.e. instance (Foo a) => instance Bar a)
  • How to write code with return type polymorphism (e..g 3.pure[IO])

I'm also curious to see if your approach can replace advanced uses of type classes like type level programming a la shapeless

@milessabin

This comment has been minimized.

Show comment
Hide comment
@milessabin

milessabin Sep 5, 2017

Member

Just a simple Functor example would help.

Member

milessabin commented Sep 5, 2017

Just a simple Functor example would help.

@kailuowang

This comment has been minimized.

Show comment
Hide comment
@kailuowang

kailuowang Sep 5, 2017

Member

I completely agree with @SystemFw. Having all these examples in one place would be of great help for the discussion. e.g. I couldn't find an example of "How to write code that is generic in the type class instance (e.g. F[_]: Monad)" in your code base. That's a main concern for implicit based type class encoding.

Member

kailuowang commented Sep 5, 2017

I completely agree with @SystemFw. Having all these examples in one place would be of great help for the discussion. e.g. I couldn't find an example of "How to write code that is generic in the type class instance (e.g. F[_]: Monad)" in your code base. That's a main concern for implicit based type class encoding.

@SystemFw

This comment has been minimized.

Show comment
Hide comment
@SystemFw

SystemFw Sep 5, 2017

I'm also really curious how you would cover one of the main strengths of type classes, namely type-directed program synthesis, without some form of implicits/proof search/native type class functionality.

SystemFw commented Sep 5, 2017

I'm also really curious how you would cover one of the main strengths of type classes, namely type-directed program synthesis, without some form of implicits/proof search/native type class functionality.

@Atry

This comment has been minimized.

Show comment
Hide comment
@Atry

Atry Sep 5, 2017

Atry commented Sep 5, 2017

@Atry

This comment has been minimized.

Show comment
Hide comment
@Atry

Atry Sep 5, 2017

Atry commented Sep 5, 2017

@kailuowang

This comment has been minimized.

Show comment
Hide comment
@kailuowang

kailuowang Sep 5, 2017

Member

Not sure if we are talking about the same thing. I am not talking about complex types like EitherT[ContT[Trampoline, Unit, ?], Throwable, ?].
a simple

def myProgram[F: Monad, A](fa: F[A]): XXX

And if you say don't do that, what's the alternative, don't abstract over F?

Member

kailuowang commented Sep 5, 2017

Not sure if we are talking about the same thing. I am not talking about complex types like EitherT[ContT[Trampoline, Unit, ?], Throwable, ?].
a simple

def myProgram[F: Monad, A](fa: F[A]): XXX

And if you say don't do that, what's the alternative, don't abstract over F?

@Atry

This comment has been minimized.

Show comment
Hide comment
@Atry

Atry Sep 5, 2017

Atry commented Sep 5, 2017

@milessabin

This comment has been minimized.

Show comment
Hide comment
@milessabin

milessabin Sep 5, 2017

Member

OK, but could you show us how you would rewrite @kailuowang's simple example into your new encoding?

Member

milessabin commented Sep 5, 2017

OK, but could you show us how you would rewrite @kailuowang's simple example into your new encoding?

@Atry

This comment has been minimized.

Show comment
Hide comment
@Atry

Atry Sep 5, 2017

Atry commented Sep 5, 2017

@Atry

This comment has been minimized.

Show comment
Hide comment
@Atry

Atry Sep 5, 2017

Or more OO style:

trait MyPlugin extends MonadFactory {
  type Facade[+A] <: MyMonad[+A]
  trait MyMonad[+A] extends Monad[+A] {
    def myProgram[A] = ???
  }
}

Atry commented Sep 5, 2017

Or more OO style:

trait MyPlugin extends MonadFactory {
  type Facade[+A] <: MyMonad[+A]
  trait MyMonad[+A] extends Monad[+A] {
    def myProgram[A] = ???
  }
}
@tpolecat

This comment has been minimized.

Show comment
Hide comment
@tpolecat

tpolecat Sep 5, 2017

Member

How would you represent something with more than one abstracted type constructor?

def sequence[F[_]: Traverse, G[_]: Applicative, A](fa: F[G[A]]): G[F[A]] = ...
Member

tpolecat commented Sep 5, 2017

How would you represent something with more than one abstracted type constructor?

def sequence[F[_]: Traverse, G[_]: Applicative, A](fa: F[G[A]]): G[F[A]] = ...
@Atry

This comment has been minimized.

Show comment
Hide comment
@Atry

Atry Sep 5, 2017

@tpolecat, good question, it's a scenario I haven't covered in my current implementation.

I guess it can be written as something like this:

trait TraverseFactory {
  type Facade[+A] <: Traverse[A]
  trait Traverse[+A] extends Functor[A] {
    def sequence[That[+A] <: ApplicativeFactory#Facade[A]](
      implicit asFa: this.type <:< Facade[That[A]]
    ): That[Facade[A]]
  }
}

Atry commented Sep 5, 2017

@tpolecat, good question, it's a scenario I haven't covered in my current implementation.

I guess it can be written as something like this:

trait TraverseFactory {
  type Facade[+A] <: Traverse[A]
  trait Traverse[+A] extends Functor[A] {
    def sequence[That[+A] <: ApplicativeFactory#Facade[A]](
      implicit asFa: this.type <:< Facade[That[A]]
    ): That[Facade[A]]
  }
}
@SystemFw

This comment has been minimized.

Show comment
Hide comment
@SystemFw

SystemFw Sep 5, 2017

That's declaration site, but what about call site? Do you need to manually mix in all the Traits carrying the instances? The fact that this is done for you by implicits is a major strength of type classes (type directed program synthesis) :

  • Simple example, Json serialisers for list, tuple and option allow you to cover all the combinations of these types
  • Complex example, basically all of shapeless

SystemFw commented Sep 5, 2017

That's declaration site, but what about call site? Do you need to manually mix in all the Traits carrying the instances? The fact that this is done for you by implicits is a major strength of type classes (type directed program synthesis) :

  • Simple example, Json serialisers for list, tuple and option allow you to cover all the combinations of these types
  • Complex example, basically all of shapeless
@Atry

This comment has been minimized.

Show comment
Hide comment
@Atry

Atry Sep 5, 2017

That's declaration site, but what about call site? Do you need to manually mix in all the Traits carrying the instances?

If you are using a built-in type, then the instance was there, UnitContinuation for example.

If you are creating some custom types, then, yes, you may have to choose the features you need manually. There is a syntactic sugar called Factory for it. You may have a look at this tutorial for usage.

At the moment Factory should work with the example implementation. However it is only able to create class Facade as an AnyRef by nested @inject Factory, which will performance a bit worse than manually created AnyVals.

Atry commented Sep 5, 2017

That's declaration site, but what about call site? Do you need to manually mix in all the Traits carrying the instances?

If you are using a built-in type, then the instance was there, UnitContinuation for example.

If you are creating some custom types, then, yes, you may have to choose the features you need manually. There is a syntactic sugar called Factory for it. You may have a look at this tutorial for usage.

At the moment Factory should work with the example implementation. However it is only able to create class Facade as an AnyRef by nested @inject Factory, which will performance a bit worse than manually created AnyVals.

@Atry

This comment has been minimized.

Show comment
Hide comment
@Atry

Atry Sep 5, 2017

I haven't discovered the combination of this factory approach with dependent-type type class.
It may be possible, maybe not, maybe need other helper macros.

Atry commented Sep 5, 2017

I haven't discovered the combination of this factory approach with dependent-type type class.
It may be possible, maybe not, maybe need other helper macros.

@Atry

This comment has been minimized.

Show comment
Hide comment
@Atry

Atry Sep 5, 2017

Atry commented Sep 5, 2017

@SystemFw

This comment has been minimized.

Show comment
Hide comment
@SystemFw

SystemFw Sep 5, 2017

SystemFw commented Sep 5, 2017

@Atry

This comment has been minimized.

Show comment
Hide comment
@Atry

Atry Sep 5, 2017

Atry commented Sep 5, 2017

@Atry

This comment has been minimized.

Show comment
Hide comment
@Atry

Atry Sep 5, 2017

@SystemFw Auto derivation is definely the case that requires implicit. Unfortunately this feature is not available in Java.

Fortunately this factory approach can also provide those kind of implicit methods.

In DeepLearning.scala, You can mixin plugins to manage implicits like this:

val hyperparameters = Factory[INDArrayWeights with DoubleLiterals with FloatLayers].newInstance()
import hyperparameters.implicits._
// Now you have DeepLearning type class implicit methods for DeepLearning.Aux[INDArrayWeight], DeepLearning.Aux[DoubleLiterals], DeepLearning.Aux[FloatLayers] 

See https://static.javadoc.io/com.thoughtworks.deeplearning/deeplearning_2.11/2.0.1/com/thoughtworks/deeplearning/plugins/Builtins.html and you can see it contains implicits that come from different transitive plugin dependencies.

Atry commented Sep 5, 2017

@SystemFw Auto derivation is definely the case that requires implicit. Unfortunately this feature is not available in Java.

Fortunately this factory approach can also provide those kind of implicit methods.

In DeepLearning.scala, You can mixin plugins to manage implicits like this:

val hyperparameters = Factory[INDArrayWeights with DoubleLiterals with FloatLayers].newInstance()
import hyperparameters.implicits._
// Now you have DeepLearning type class implicit methods for DeepLearning.Aux[INDArrayWeight], DeepLearning.Aux[DoubleLiterals], DeepLearning.Aux[FloatLayers] 

See https://static.javadoc.io/com.thoughtworks.deeplearning/deeplearning_2.11/2.0.1/com/thoughtworks/deeplearning/plugins/Builtins.html and you can see it contains implicits that come from different transitive plugin dependencies.

@Atry

This comment has been minimized.

Show comment
Hide comment
@Atry

Atry Sep 5, 2017

Have a look at https://static.javadoc.io/com.thoughtworks.deeplearning/deeplearning_2.11/2.0.1/com/thoughtworks/deeplearning/plugins/Builtins$Implicits.html , you may notice those methods are defined in different traits. For example, some of them are from INArrayLayers.ImplicitApi, some of them are from DoubleLayers.ImplicitApi.

Atry commented Sep 5, 2017

Have a look at https://static.javadoc.io/com.thoughtworks.deeplearning/deeplearning_2.11/2.0.1/com/thoughtworks/deeplearning/plugins/Builtins$Implicits.html , you may notice those methods are defined in different traits. For example, some of them are from INArrayLayers.ImplicitApi, some of them are from DoubleLayers.ImplicitApi.

@Atry

This comment has been minimized.

Show comment
Hide comment
@Atry

Atry Sep 5, 2017

That is to say, I still keep dependent-type type classes as implicits inside factories. While other features are turned into normal OO methods without the requirement of implicits.

Atry commented Sep 5, 2017

That is to say, I still keep dependent-type type classes as implicits inside factories. While other features are turned into normal OO methods without the requirement of implicits.

@Atry

This comment has been minimized.

Show comment
Hide comment
@Atry

Atry Sep 5, 2017

From result of some random searches for "scala type class":

I don't know, I've never minded the java/scala type class approach, but I have weird opinions about boilerplate!

https://twitter.com/aaronmblevin/status/903118605394604032

Atry commented Sep 5, 2017

From result of some random searches for "scala type class":

I don't know, I've never minded the java/scala type class approach, but I have weird opinions about boilerplate!

https://twitter.com/aaronmblevin/status/903118605394604032

@aaronlevin

This comment has been minimized.

Show comment
Hide comment
@aaronlevin

aaronlevin Sep 5, 2017

oh, hey, that's me quoted! Having given several talks on "typeclass induction" I have to say I'm firmly in the camp of "type directed program synthesis," and it's unclear how this would work in your method.

But, let me say: it's really cool to see people coming up with new solutions to the typeclass encoding problem! What's super exciting about this space is I don't think we've really exhausted all the points in the design space, and your ideas are a great example of an approach many haven't considered!

With the above in mind, I think what's challenging about your idea is that the examples you point towards are quite complex. It would be really helpful to have an example of, like, the definition of Functor, an implementation of Functor for Option.

I also want to echo that the program @tpolecat pasted (def sequence[F[_]: Traverse, G[_]: Applicative, A](fa: F[G[A]]): G[F[A]] = ...) is a pretty test for your design.

I think there are interesting ideas in what you've drafted, so it would be cool to scope down the examples so we can get to the heart of your idea!!

aaronlevin commented Sep 5, 2017

oh, hey, that's me quoted! Having given several talks on "typeclass induction" I have to say I'm firmly in the camp of "type directed program synthesis," and it's unclear how this would work in your method.

But, let me say: it's really cool to see people coming up with new solutions to the typeclass encoding problem! What's super exciting about this space is I don't think we've really exhausted all the points in the design space, and your ideas are a great example of an approach many haven't considered!

With the above in mind, I think what's challenging about your idea is that the examples you point towards are quite complex. It would be really helpful to have an example of, like, the definition of Functor, an implementation of Functor for Option.

I also want to echo that the program @tpolecat pasted (def sequence[F[_]: Traverse, G[_]: Applicative, A](fa: F[G[A]]): G[F[A]] = ...) is a pretty test for your design.

I think there are interesting ideas in what you've drafted, so it would be cool to scope down the examples so we can get to the heart of your idea!!

@TomasMikula

This comment has been minimized.

Show comment
Hide comment
@TomasMikula

TomasMikula Sep 6, 2017

So you rewrote

def myProgram[F: Monad, A](fa: F[A]): XXX

to

trait MyPlugin extends MonadFactory {
  type Facade[+A] <: Monad[+A]
  def myProgram[A](fa: Facade[A]) = ???
}

Now you can only ever call myProgram on a value that implements the Monad interface. So I can't call myProgram on, say, a List[Int], because List is not a subtype of Monad.

Could your approach model that sort of ad-hoc polymorphism, giving behavior (e.g. monad) to a type I don't own (e.g. List)?

TomasMikula commented Sep 6, 2017

So you rewrote

def myProgram[F: Monad, A](fa: F[A]): XXX

to

trait MyPlugin extends MonadFactory {
  type Facade[+A] <: Monad[+A]
  def myProgram[A](fa: Facade[A]) = ???
}

Now you can only ever call myProgram on a value that implements the Monad interface. So I can't call myProgram on, say, a List[Int], because List is not a subtype of Monad.

Could your approach model that sort of ad-hoc polymorphism, giving behavior (e.g. monad) to a type I don't own (e.g. List)?

@Atry

This comment has been minimized.

Show comment
Hide comment
@Atry

Atry Sep 6, 2017

@TomasMikula

  1. There is an implicit method to create a Facade for that purpose: https://github.com/ThoughtWorksInc/FunctionalPattern/blob/9f1a44c/designpattern/src/main/scala/com/thoughtworks/designpattern/covariant.scala#L18
  2. Facades are able to implement other traits, so it is possible to create something like:
trait SeqTraverseFactory {
  type Facade[+A] <: SeqTraverse[A]
  trait SeqTraverse[+A] extends Traverse[A] with Monad[A] with Seq[A] {
    // Forward Seq calls to underlying Seq
  }
}

Atry commented Sep 6, 2017

@TomasMikula

  1. There is an implicit method to create a Facade for that purpose: https://github.com/ThoughtWorksInc/FunctionalPattern/blob/9f1a44c/designpattern/src/main/scala/com/thoughtworks/designpattern/covariant.scala#L18
  2. Facades are able to implement other traits, so it is possible to create something like:
trait SeqTraverseFactory {
  type Facade[+A] <: SeqTraverse[A]
  trait SeqTraverse[+A] extends Traverse[A] with Monad[A] with Seq[A] {
    // Forward Seq calls to underlying Seq
  }
}
@Atry

This comment has been minimized.

Show comment
Hide comment
@Atry

Atry Sep 6, 2017

@TomasMikula The situation is like ArrayOps in Scala standard library, where ArrayOps extends SeqLike. Maybe we can create a Monad as well.

Atry commented Sep 6, 2017

@TomasMikula The situation is like ArrayOps in Scala standard library, where ArrayOps extends SeqLike. Maybe we can create a Monad as well.

@SystemFw

This comment has been minimized.

Show comment
Hide comment
@SystemFw

SystemFw Sep 6, 2017

I'm getting a bit confused. I thought a big part of the rationale for this proposal was to eschew implicits, but I've seen them pop up in three different occasions:

  • To wrap in Facade
  • To have type class induction
  • To have multiple parameters with constraints (the implicit asFa: this.type <:< Facade[That[A] bit)

SystemFw commented Sep 6, 2017

I'm getting a bit confused. I thought a big part of the rationale for this proposal was to eschew implicits, but I've seen them pop up in three different occasions:

  • To wrap in Facade
  • To have type class induction
  • To have multiple parameters with constraints (the implicit asFa: this.type <:< Facade[That[A] bit)
@Atry

This comment has been minimized.

Show comment
Hide comment
@Atry

Atry Sep 6, 2017

  • To wrap in Facade

It's a lightweight, optional usage. Actually I prefer manually wrap it to Facade in most cases. For example, I manually wrap the function literal to a Facade, instead of let it implicitly converts to Facade.

Facade { continue =>
  ...
}

https://github.com/ThoughtWorksInc/FunctionalPattern/blob/a3cb2ab/benchmark-asyncio-designpattern/src/main/scala/com/thoughtworks/designpattern/benchmark/asyncio/designpattern.scala#L21

Atry commented Sep 6, 2017

  • To wrap in Facade

It's a lightweight, optional usage. Actually I prefer manually wrap it to Facade in most cases. For example, I manually wrap the function literal to a Facade, instead of let it implicitly converts to Facade.

Facade { continue =>
  ...
}

https://github.com/ThoughtWorksInc/FunctionalPattern/blob/a3cb2ab/benchmark-asyncio-designpattern/src/main/scala/com/thoughtworks/designpattern/benchmark/asyncio/designpattern.scala#L21

@Atry

This comment has been minimized.

Show comment
Hide comment
@Atry

Atry Sep 6, 2017

  • To have type class induction

If you mean type class auto derivation for serialization, I admit this usage is not covered by this factory pattern.

However, the point of auto derivation is not type class. It's code generator which matters. The task requires macros regardless you use or not use implicit parameters. For example, I created some code generators for serialization many years. All of them generate static functions instead of type class:

Atry commented Sep 6, 2017

  • To have type class induction

If you mean type class auto derivation for serialization, I admit this usage is not covered by this factory pattern.

However, the point of auto derivation is not type class. It's code generator which matters. The task requires macros regardless you use or not use implicit parameters. For example, I created some code generators for serialization many years. All of them generate static functions instead of type class:

@Atry

This comment has been minimized.

Show comment
Hide comment
@Atry

Atry Sep 6, 2017

  • To have multiple parameters with constraints (the implicit asFa: this.type <:< Facade[That[A] bit)

The purpose of <:< is to exposing Scala language features to user space type level programming. I admit it is a valid usage of implicit parameters. And I don't know how to avoid those kind of usage. On the contrary I created a lot of implicit parameter style type class in order to access Scala language features.

Among these language feature type classes, the Factory is most useful tool for creating this factory pattern style mix-ins.

The usage of these implicit parameters do bother me because they do not have a Java alternative.

Do you have any idea? @SystemFw

Atry commented Sep 6, 2017

  • To have multiple parameters with constraints (the implicit asFa: this.type <:< Facade[That[A] bit)

The purpose of <:< is to exposing Scala language features to user space type level programming. I admit it is a valid usage of implicit parameters. And I don't know how to avoid those kind of usage. On the contrary I created a lot of implicit parameter style type class in order to access Scala language features.

Among these language feature type classes, the Factory is most useful tool for creating this factory pattern style mix-ins.

The usage of these implicit parameters do bother me because they do not have a Java alternative.

Do you have any idea? @SystemFw

@SystemFw

This comment has been minimized.

Show comment
Hide comment
@SystemFw

SystemFw Sep 6, 2017

If you mean type class auto derivation for serialisation

Serialisation is just the first example I could think of, but type-directed program synthesis, or type class induction, or whatever you want to call it has a lot of different use cases, from typelevel DSLs, to shapeless, to ordinary FP code (e.g. instance (Monoid b) => Monoid (a -> b) and so on.
Anyway, I think it's quite important, so I was just trying to understand if your approach had it covered or not :)

SystemFw commented Sep 6, 2017

If you mean type class auto derivation for serialisation

Serialisation is just the first example I could think of, but type-directed program synthesis, or type class induction, or whatever you want to call it has a lot of different use cases, from typelevel DSLs, to shapeless, to ordinary FP code (e.g. instance (Monoid b) => Monoid (a -> b) and so on.
Anyway, I think it's quite important, so I was just trying to understand if your approach had it covered or not :)

@Atry

This comment has been minimized.

Show comment
Hide comment
@Atry

Atry Sep 6, 2017

Of course this proposal does not cover type induction, because the point of this proposal is avoiding type inductions.

However it does cover some important usages of type induction by replacing them to ordinary method calls.

Atry commented Sep 6, 2017

Of course this proposal does not cover type induction, because the point of this proposal is avoiding type inductions.

However it does cover some important usages of type induction by replacing them to ordinary method calls.

@TomasMikula

This comment has been minimized.

Show comment
Hide comment
@TomasMikula

TomasMikula Sep 6, 2017

There is an implicit method to create a Facade for that purpose:

The situation with current encoding is, I can have N lists and a single Monad[List] instance to perform monadic operations on any of those N lists. That is an overhead of 1 object (the monad instance).

With your approach, it seems, you would have to wrap each of the N lists in a facade that implements Monad. This is an overhead of N objects. Do you measure the impact of this? Or can those allocations be optimized away?

TomasMikula commented Sep 6, 2017

There is an implicit method to create a Facade for that purpose:

The situation with current encoding is, I can have N lists and a single Monad[List] instance to perform monadic operations on any of those N lists. That is an overhead of 1 object (the monad instance).

With your approach, it seems, you would have to wrap each of the N lists in a facade that implements Monad. This is an overhead of N objects. Do you measure the impact of this? Or can those allocations be optimized away?

@Atry

This comment has been minimized.

Show comment
Hide comment
@Atry

Atry Sep 6, 2017

@TomasMikula According to the AsyncIO benchmark, these Facades run very fast, more than twice performance of scalaz.

Facades might be AnyVal, though it may be not possible if you let your Facade extend a proxy Seq. Fortunately, I delicately added a BoxFactory so that a type class implementation is able to unbox those Facades manually, no matter Facades are AnyVals or AnyRefs, preventing Facades being referenced by closures.

The programming style of this proposal is very similar with Scala standard library and Scala compiler. I wonder why @odersky did not include a object-oriented style categorical library in Scala standard library.

Atry commented Sep 6, 2017

@TomasMikula According to the AsyncIO benchmark, these Facades run very fast, more than twice performance of scalaz.

Facades might be AnyVal, though it may be not possible if you let your Facade extend a proxy Seq. Fortunately, I delicately added a BoxFactory so that a type class implementation is able to unbox those Facades manually, no matter Facades are AnyVals or AnyRefs, preventing Facades being referenced by closures.

The programming style of this proposal is very similar with Scala standard library and Scala compiler. I wonder why @odersky did not include a object-oriented style categorical library in Scala standard library.

@Atry

This comment has been minimized.

Show comment
Hide comment
@Atry

Atry Sep 6, 2017

Hi @kailuowang , note that the BoxFactory trick can be also applied to scalaz or cats, as long as you add an UnboxableK[_[_]] implicit parameter on each monad transformer, where UnboxableK is an isomorphic natural transformation.

Atry commented Sep 6, 2017

Hi @kailuowang , note that the BoxFactory trick can be also applied to scalaz or cats, as long as you add an UnboxableK[_[_]] implicit parameter on each monad transformer, where UnboxableK is an isomorphic natural transformation.

@Atry

This comment has been minimized.

Show comment
Hide comment
@Atry

Atry Sep 13, 2017

At first, I will create a minimal library of categories in next month, especially monad-related type classes in the new encoding.

Atry commented Sep 13, 2017

At first, I will create a minimal library of categories in next month, especially monad-related type classes in the new encoding.

@DmytroMitin

This comment has been minimized.

Show comment
Hide comment
@DmytroMitin

DmytroMitin Oct 7, 2017

@Atry
Did I understand correctly that you proposed to write

  trait FunctorFactory {
    type Facade[+A] <: Functor[A]

    trait Functor[+A] {
      def map[B](mapper: A => B): Facade[B]
    }
  }

  object listFunctor extends FunctorFactory {
    case class Facade[+A](list: List[A]) extends Functor[A] {
      override def map[B](mapper: A => B): Facade[B] = Facade[B](list.map(mapper))
    }
  }

  listFunctor.Facade(List(1, 2, 3)).map(_ + 1)

instead of

  trait Functor[F[_]] {
    def map[A, B](mapper: A => B)(fa: F[A]): F[B]
  }

  object Functor {
    implicit object listFunctor extends Functor[List] {
      override def map[A, B](mapper: A => B)(fa: List[A]): List[B] = fa.map(mapper)
    }
  }

  implicitly[Functor[List]].map((_ : Int) + 1)(List(1, 2, 3))

?

Sounds like emulating higher-order generics.

DmytroMitin commented Oct 7, 2017

@Atry
Did I understand correctly that you proposed to write

  trait FunctorFactory {
    type Facade[+A] <: Functor[A]

    trait Functor[+A] {
      def map[B](mapper: A => B): Facade[B]
    }
  }

  object listFunctor extends FunctorFactory {
    case class Facade[+A](list: List[A]) extends Functor[A] {
      override def map[B](mapper: A => B): Facade[B] = Facade[B](list.map(mapper))
    }
  }

  listFunctor.Facade(List(1, 2, 3)).map(_ + 1)

instead of

  trait Functor[F[_]] {
    def map[A, B](mapper: A => B)(fa: F[A]): F[B]
  }

  object Functor {
    implicit object listFunctor extends Functor[List] {
      override def map[A, B](mapper: A => B)(fa: List[A]): List[B] = fa.map(mapper)
    }
  }

  implicitly[Functor[List]].map((_ : Int) + 1)(List(1, 2, 3))

?

Sounds like emulating higher-order generics.

@Atry

This comment has been minimized.

Show comment
Hide comment
@Atry

Atry Oct 7, 2017

@DmytroMitin Not really. I use higher kinded abstract types here like Facade. This is a more powerful feature than higher kinded type parameter. No need to emulate.

Atry commented Oct 7, 2017

@DmytroMitin Not really. I use higher kinded abstract types here like Facade. This is a more powerful feature than higher kinded type parameter. No need to emulate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment