Skip to content

Custom Functor Typeclass - #1643

Merged
lossyrob merged 10 commits into
locationtech:masterfrom
fosskers:feature/cgw/functor
Oct 6, 2016
Merged

lossyrob merged 10 commits into
locationtech:masterfrom
fosskers:feature/cgw/functor

Conversation

@fosskers

@fosskers fosskers commented Sep 29, 2016 •

Copy link
Copy Markdown
Contributor

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
Copy Markdown
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

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

Copy link
Copy Markdown
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
Copy Markdown
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.

new ContextRDD(rdd, metadata)
}

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

Copy link
Copy Markdown
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