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

Custom Functor Typeclass #1643

Merged
merged 10 commits into from
Oct 6, 2016
Merged

Custom Functor Typeclass #1643

merged 10 commits into from
Oct 6, 2016

Conversation

fosskers
Copy link
Contributor

@fosskers fosskers commented Sep 29, 2016

TODO

Motivation

#1575 describes how TileLayerRDD.toSpatial doesn't reflect the change in key type in its metadata. After much deliberation, we recognized our attempts to address this as attempts to write a Functor instance for TileLayerMetadata[K] which maps over its KeyBounds. Unfortunately, Scalaz's Functor trait is a bit too restrictive, in that types that use it can't impose additional constraints on their inner type. Compare Scalaz:

trait Functor[F[_]] extends InvariantFunctor[F] { self =>

  /** Lift `f` into `F` and apply to `F[A]`. */
  def map[A, B](fa: F[A])(f: A => B): F[B]
...
}

to our implementation:

 trait Functor[F[_], A] extends MethodExtensions[F[A]]{
   /** Lift `f` into `F` and apply to `F[A]`. */
   def map[B](f: A => B): F[B]
 }

where A is lifted into Functor's type signature. This allows us to write code like this:

def foo[K: A: B: C](implicit f: Functor[TileLayerMetadata, K]): ??? = {
...
  tlm.map(k => ... ) // `k` has all the power of its constraints A, B, and C.
...
}

@fosskers
Copy link
Contributor Author

This may or may not be an actual Functor, depending on how well we can convince ourselves that the second Functor law holds:

fmap (g . h) = (fmap g) . (fmap h)

If you only had one implicit Functor[F, A] in scope, then:

val m: F[A] = ???
val f: A => B = ???
val g: B => C = ???

val n: F[C] = m.map(f).map(g)

wouldn't compile. But if you did have both Functor[F, A] and Functor[F, B] (which may both use the same underlying implicit class, see class TlmFunctor[A]), then the above would compile. Furthermore, so would:

val n: F[C] = m.map(k => g(f(k)))

and so the Functor law holds, and it is indeed a Functor.

@fosskers fosskers changed the title [WIP] Custom Functor Typeclass Custom Functor Typeclass Sep 29, 2016
@@ -16,9 +16,8 @@ trait Implicits {
extends TileLayerRDDFilterMethods[K, V, M]

implicit class withSpaceTimeToSpatialMethods[
K: SpatialComponent: TemporalComponent,
K: SpatialComponent: TemporalComponent: λ[α => M[α] => Functor[M, α]]: λ[α => Component[M[α], Bounds[α]]],
Copy link
Member

Choose a reason for hiding this comment

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

This should be type aliased w/ a broken down explained in a comment, pretty tough to parse and also understand why they just can't be context bounds on M

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's definitely space voodoo the first time you see it, yeah. For technical reasons they can't be bounds on M, so I'll explain that.

@@ -62,6 +61,14 @@ package object spark
new ContextRDD(rdd, metadata)
}

implicit class TileLayerMetadataFunctor[A](val self: TileLayerMetadata[A]) extends Functor[TileLayerMetadata, A] {
Copy link
Member

Choose a reason for hiding this comment

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

Comments to explain why this is needed.

@lossyrob lossyrob merged commit 3b132fd into locationtech:master Oct 6, 2016
@lossyrob lossyrob added this to the 1.0 milestone Oct 18, 2016
@fosskers fosskers mentioned this pull request Dec 28, 2016
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.

3 participants