Skip to content

Instantly share code, notes, and snippets.

@arosien
Created January 17, 2025 21:11
Show Gist options
  • Save arosien/eb2dc075aa5c5661b04432b6d8d4f4bc to your computer and use it in GitHub Desktop.
Save arosien/eb2dc075aa5c5661b04432b6d8d4f4bc to your computer and use it in GitHub Desktop.
//> using toolkit typelevel:latest
//> using option -Ykind-projector
//> using dep org.typelevel::monoids:0.2.0
import cats._
import cats.syntax.all._
import org.typelevel.monoids._
import cats.data.Store
import cats.data.StoreT
// https://chshersh.com/posts/2019-03-25-comonadic-builders
// https://gist.github.com/ChShersh/5a4c8e1c0557627859fe93ad0b05dd56
// Any is Boolean where |+| = ||
case class Settings(hasLibrary: Any, github: Any, travis: Any)
object Settings {
val empty = Settings(Monoid[Any].empty, Monoid[Any].empty, Monoid[Any].empty)
given Monoid[Settings] with
def empty: Settings =
Settings.empty
def combine(x: Settings, y: Settings): Settings =
Settings(
x.hasLibrary |+| y.hasLibrary,
x.github |+| y.github,
x.travis |+| y.travis
)
}
case class Project(name: String, hasLibrary: Any, github: Any, travis: Any)
type ProjectBuilder = Settings => Project
// buildProject :: Text -> ProjectBuilder
def buildProject(name: String): ProjectBuilder =
settings =>
Project(name, settings.hasLibrary, settings.github, settings.travis)
/*
instance Monoid m => Comonad ((->) m) where
extract :: (m -> a) -> a
extract f = f mempty
duplicate :: (m -> a) -> (m -> m -> a)
duplicate f = \m1 m2 -> f (m1 <> m2)
extend :: (w a -> b) -> w a -> w b
extend f = fmap f . duplicate
*/
given function1Comonad[M](using M: Monoid[M]): Comonad[M => *] with
def extract[A](f: M => A): A =
f(M.empty)
def map[A, B](fa: M => A)(f: A => B): M => B =
f compose fa
override def coflatten[A](fa: M => A): M => M => A =
m1 => m2 => fa(m1 |+| m2)
def coflatMap[A, B](fa: M => A)(f: (M => A) => B): M => B =
map(coflatten(fa))(f)
extension (builder: ProjectBuilder)
/*
append :: ProjectBuilder -> (ProjectBuilder -> Project) -> ProjectBuilder
append = (=>>)
(S => P).coflatMap((S => P) => P)
map(coflatten(S => P))((S => P) => P)
map(S => (S => P))((S => P) => P)
((S => P) => P) compose (S => (S => P))
(S => P)
*/
def append(f: ProjectBuilder => Project): ProjectBuilder =
builder.coflatMap(f)
/*
hasLibraryB :: ProjectBuilder -> Project
hasLibraryB builder = builder $ mempty { settingsHasLibrary = Any True }
gitHubB :: ProjectBuilder -> Project
gitHubB builder = builder $ mempty { settingsGitHub = Any True }
alwaysTravisB :: ProjectBuilder -> Project
alwaysTravisB builder = builder $ mempty { settingsTravis = Any True }
*/
def withLibrary: ProjectBuilder =
builder coflatMap (b => b(Settings.empty.copy(hasLibrary = Any(true))))
def withGithub: ProjectBuilder =
builder coflatMap (b => b(Settings.empty.copy(github = Any(true))))
def withTravisAlways: ProjectBuilder =
builder coflatMap (b => b(Settings.empty.copy(travis = Any(true))))
/*
We don’t want to set projectTravis to True if GitHub flag is set to False.
NOTE: here projectTravis is set to the value of projectGitHub because it is the same as if projectGitHub then True else False.
travisB :: ProjectBuilder -> Project
travisB builder =
let project = extract builder
in project { projectTravis = projectGitHub project }
*/
def withTravis: ProjectBuilder =
builder.coflatMap { b =>
val project = b.extract
project.copy(travis = project.github)
}
@main
def run =
/*
pPrint $ extract $ buildProject "minimal-project"
pPrint $ extract $ buildProject "only-library" =>> hasLibraryB
pPrint $ extract $ buildProject "library-github" =>> hasLibraryB =>> gitHubB
*/
println(buildProject("minimal-project").extract)
println(
buildProject("only-library").withLibrary.extract
)
println(
buildProject("library-github").withLibrary.withGithub.extract
)
/*
-- dependent: 1 level
pPrint $ extract $ buildProject "travis" =>> travisB
pPrint $ extract $ buildProject "always-travis" =>> alwaysTravisB
pPrint $ extract $ buildProject "github-travis" =>> gitHubB =>> travisB
pPrint $ extract $ buildProject "travis-github" =>> travisB =>> gitHubB
*/
println(
buildProject("travis").withTravis.extract
)
println(
buildProject("always-travis").withTravisAlways.extract
)
println(
buildProject("travis-github").withTravis.withGithub.extract
)
println(
buildProject("github-travis").withGithub.withTravis.extract
)
println(Store(buildProject("store-empty"), Settings.empty).extract)
println(
Store(buildProject("store-library"), Settings.empty)
.seeks(_.copy(hasLibrary = Any(true)))
.extract
)
println(
Store(buildProject("store-library-github"), Settings.empty)
.seeks(_.copy(hasLibrary = Any(true)))
.seeks(_.copy(github = Any(true)))
.extract
)
// import cats.effect._
// type StoreIO[S, A] = StoreT[IO, S, A]
// def StoreIO[S: Monoid, A](a: A): StoreIO[S, A] = StoreT.pure(a)
// println(StoreIO[Settings, ProjectBuilder](buildProject("storeIO-empty")).)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment