Skip to content

Instantly share code, notes, and snippets.

@EECOLOR
Forked from paulp/como.scala
Last active August 29, 2015 14:15
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save EECOLOR/c9037d1c56c3c4b59c75 to your computer and use it in GitHub Desktop.
Save EECOLOR/c9037d1c56c3c4b59c75 to your computer and use it in GitHub Desktop.
package p {
object Test {
import construction.Monadic
import execution.Bimonad
def main(args: Array[String]): Unit = {
val expression = Monadic[List](10) flatMap (1 to _ toList) coflatMap (_.sum)
val result = expression runWith ListBimonad
println(result + " <- " + expression)
// output: 55 <- CoflatMap(FlatMap(Pure(10),<function1>),<function1>)
}
private object ListBimonad extends Bimonad.Of[List] {
def fmap[A, B] = _ map _
def join[A] = _.flatten
def pure[A] = List(_)
def cojoin[A] = List(_)
def copure[A] = _.head
}
}
package execution {
trait `Some name in the domain` { type F[_] }
trait Functor extends `Some name in the domain` { def fmap[A, B]: (F[A], (A => B)) => F[B] }
trait Copointed extends Functor { def copure[A]: F[A] => A }
trait Comonad extends Copointed { def cojoin[A]: F[A] => F[F[A]] }
trait Pointed extends Functor { def pure[A]: A => F[A] }
trait Monad extends Pointed { def join[A]: F[F[A]] => F[A] }
trait Bimonad extends Monad with Comonad
object Bimonad {
type Of[G[_]] = `It should not be necessary to add this trait`[G]
trait `It should not be necessary to add this trait`[G[_]] extends Bimonad {
type F[x] = G[x]
}
}
}
package construction {
import execution.Bimonad
sealed trait Monadic[F[_], A] extends Monadic.Operations[F, A]
object Monadic {
def apply[F[_], A](fa: F[A]): Monadic[F, A] = Copure(fa)
def apply[F[_]]: Factory[F] = new Factory[F]
class Factory[F[_]] {
def apply[A](a: A): Monadic[F, A] = Pure[F, A](a)
}
private case class Pure[F[_], A](x: A) extends Monadic[F, A]
private case class Copure[F[_], A](x: F[A]) extends Monadic[F, A]
private case class Map[F[_], A, B](prev: Monadic[F, A], f: A => B) extends Monadic[F, B]
private case class FlatMap[F[_], A, B](prev: Monadic[F, A], f: A => F[B]) extends Monadic[F, B]
private case class CoflatMap[F[_], A, B](prev: Monadic[F, A], f: F[A] => B) extends Monadic[F, B]
trait Operations[F[_], A] { _: Monadic[F, A] =>
private type Result[B] = Monadic[F, B]
def map[B](f: A => B): Result[B] = Map(this, f)
def flatMap[B](f: A => F[B]): Result[B] = FlatMap(this, f)
def coflatMap[B](f: F[A] => B): Result[B] = CoflatMap(this, f)
def runWith(implicit F: Bimonad.Of[F]): A = executed |> F.copure
private[Operations] def executed(implicit F: Bimonad.Of[F]): F[A] = {
def fmap[A, B]: (A => B) => F[A] => F[B] = f => fa => F.fmap(fa, f)
this match {
case Pure(x) => x |> F.pure
case Copure(x) => x
case Map(prev, f) => prev.executed |> fmap(f)
case FlatMap(prev, f) => prev.executed |> fmap(f) |> F.join
case CoflatMap(prev, f) => prev.executed |> F.cojoin |> fmap(f)
}
}
private implicit class ForwardPipe[A](x: A) { def |>[B](f: A => B): B = f(x) }
}
}
}
}
@EECOLOR
Copy link
Author

EECOLOR commented Feb 13, 2015

A summary of the changes:

  • Changed the order in the file. The reader now is taken from overview to detail.
  • Introduced white lines to visually separate (and group) chunks of code.
  • Introduced sensible variable names to improve the readability of the example.
  • Favored user implementation of library interfaces over internal usage.
  • Removed some noise introduced by type parameters.
  • Renamed run to runWith and explicitly passed in the argument to increase understanding.
  • Reduced the amount of public types, methods and implementation details.
  • Reduced noise in return types.
  • Moved methods from implicit enhancement class to the correct type to improve discovery.
  • Moved global methods to the correct location.
  • Renamed implicit z to F to reduce mental translation.
  • Renamed reduce to executed, I think another name would be better.
  • Reduced the amount of noise created by braces in the executed method
  • Made the implementation of executed a tiny bit more consistent

@paulp
Copy link

paulp commented Feb 13, 2015

I think most of the changes are great. I have no substantive disagreement with any of them given the premises under which you made them, but I will elaborate on a couple things:

Moved methods from implicit enhancement class to the correct type to improve discovery.

These days I have the luxury of writing code which is striving for "how we ought to be able to write it" and not "how we're stuck writing it." The fact that tooling tends to find the inherited methods and not the implicitly available ones is an artifact of tooling history and the unusability of the scala compiler except as a black box. I think using inheritance only for the minimum set of fundamental methods (and whatever allocated memory there may be) is the best way. It's much more difficult for accidental dependencies or unintentional privileging of internal details to sprout up when the core class is kept to the absolute minimum and the extension methods manipulate it.

So yes in "real life" complicating the inheritance hierarchy to assist discovery is usually necessary, but I'm done with babying scala that way.

Renamed implicit z to F to reduce mental translation.

This is a convention I've widely adopted in my code - it might seem less arbitrary given the wider context. The main thing is that I don't want to give it a name at all, but I am often forced to - so rather than wasting brain cells on the name of every implicit parameter I always call a single one z if I can.

@EECOLOR
Copy link
Author

EECOLOR commented Feb 13, 2015

I am convinced by your arguments and it's quite funny that you mention "I don't want to give it a name at all, but I am often forced to", I totally agree.

Moved methods from implicit enhancement class to the correct type to improve discovery.

Another reason was that by placing them in a trait helps readers of the source code could see that there are methods available. I however tend to lean to your side on this one.

@paulp
Copy link

paulp commented Feb 14, 2015

👏

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