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

Unboxed Forall #1417

Merged
merged 2 commits into from Sep 4, 2017

Conversation

Projects
None yet
2 participants
@TomasMikula
Member

TomasMikula commented Jun 28, 2017

Define Forall[F[_]] as an abstract type, privately implemented as

type Forall[F[_]] = F[Any]

A type alias is provided

type ∀[F[_]] = Forall[F]

Creating instances

Since Forall[F] is represented as F[Any], care needs to be taken so that only truly parametric instances can be created. Two ways to create instances are provided:

  1. From a "prototype", which is a Scalaz7-style universally quantified value:

    def from[F[_]](p: Prototype[F]): Forall[F]
    
    trait Prototype[F[_]] {
      def apply[A]: F[A]
    }

    Prospectively, there could be convenient syntax for creating Prototypes via non/kind-projector#54.

    Note that creation via a prototype requires an allocation of a short-lived object (the prototype), but the resulting Forall[F] value is unboxed. (Perhaps that one allocation could be eliminated by the optimizer?)

  2. Via the same trick (due to @alexknvl) as in #1416:

    val l1: Forall[List] = Forall.of[List](Nil)
    
    // or
    
    val l2: Forall[List] = Forall.mk[Forall[List]].from(Nil)

    The seemingly longer mk[...].from(...) syntax becomes more succinct in some cases:

    def listSemigroup[A]: Semigroup[List[A]] = ???
    
    type Plus[F[_]] = Forall[λ[A => Semigroup[F[A]]]]
    
    val listPlus: Plus[List] = Forall.of[λ[A => Semigroup[F[A]]]](listSemigroup)
    
    // vs
    
    val listPlus: Plus[List] = Forall.mk[Plus[List]].from(listSemigroup)

Using instances

From user's perspective Forall[F] is completely abstract, but values are enriched with apply[A] method:

class ForallOps[F[_]](val a: Forall[F]) extends AnyVal {
  def apply[A]: F[A] = ???
}

One then uses this method to get an instance specialized for a specific type:

listPlus[Int].append(List(1, 2), List(3, 4)) // List(1, 2, 3, 4)

See also ForallUsage.scala on how Forall can be used to represent NaturalTransformation.

@TomasMikula TomasMikula added the scalaz8 label Jun 28, 2017

@aloiscochard

This comment has been minimized.

Contributor

aloiscochard commented Jun 29, 2017

This is beautiful work, I ❤️ it.

What about adding the $ syntax directly in base? seems pretty useful to me.

Should we add this in default Prelude? I think yes.

@TomasMikula

This comment has been minimized.

Member

TomasMikula commented Jun 30, 2017

In this PR, ~> is used just as an example. We can consider $ syntax when adding natural transformation. Though there might be confusion about preference, since a $ b $ c in Scala is (a $ b) $ c, whereas in Haskell it is a $ (b $ c). I used $ because Forall already has an apply method.

Yeah, I will add it to Prelude.

@TomasMikula

This comment has been minimized.

Member

TomasMikula commented Jun 30, 2017

Added to Prelude. Maybe there's a better organization of the code?

@aloiscochard

This comment has been minimized.

Contributor

aloiscochard commented Jul 10, 2017

Looks great!

@TomasMikula TomasMikula merged commit 7f3e5d3 into scalaz:series/8.0.x Sep 4, 2017

1 check passed

continuous-integration/travis-ci/pr The Travis CI build passed
Details

@TomasMikula TomasMikula deleted the TomasMikula:unboxed-forall branch Sep 4, 2017

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