{"id":16451727,"url":"https://github.com/tpolecat/tiny-world","last_synced_at":"2025-03-21T05:30:37.932Z","repository":{"id":6890379,"uuid":"8139956","full_name":"tpolecat/tiny-world","owner":"tpolecat","description":"Monadic effect worlds for interacting safely with mutable data.","archived":false,"fork":false,"pushed_at":"2015-08-19T04:53:24.000Z","size":330,"stargazers_count":38,"open_issues_count":0,"forks_count":1,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-03-20T15:58:13.907Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tpolecat.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-02-11T14:59:39.000Z","updated_at":"2023-04-28T08:58:10.000Z","dependencies_parsed_at":"2022-08-26T07:11:27.041Z","dependency_job_id":null,"html_url":"https://github.com/tpolecat/tiny-world","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tpolecat%2Ftiny-world","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tpolecat%2Ftiny-world/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tpolecat%2Ftiny-world/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tpolecat%2Ftiny-world/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tpolecat","download_url":"https://codeload.github.com/tpolecat/tiny-world/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244745641,"owners_count":20503043,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-11T10:10:09.724Z","updated_at":"2025-03-21T05:30:37.488Z","avatar_url":"https://github.com/tpolecat.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"tiny-world\n==========\n\nYou know? That thing? Where you wrap an unsafe Java doodad and make a pure monadic API? Tiny-world abstracts this pattern, and does it in such a way that users of your API have no knowledge of the passed state (not even its type) unless you say so.\n\nThere are three implementations, but probably the one you want to use is `FreeWorld`, which is also the simplest. Honestly just copy and paste it; this is more of a snippet than a library. Anyway you create a module with your primitive operations that extends a world, and users just import splat from that module. Here's an example.\n\n```scala\nimport scalaz.effect.IO\nimport scala.util.Random\nimport org.tpolecat.tiny.world.FreeWorld\n\n/**\n * An example `World` with a small set of `Action`s for random number generation. Although interpreting `Action`s in \n * this `World` involves manipulation of an impure `State`, this impurity is not visible to users; the API is pure.\n */\nobject RngWorldMinimal extends FreeWorld {\n\n  // Our world's state is an instance of `Random`, but clients have no way to know this, and have no way to get a \n  // reference to the `State` as it is passed through each `Action`.\n  protected type State = Random\n\n  // An `Action[A]` is a pure value that (when interpreted) performs a potentially effectful computation on our `State`\n  // and returns a value of type `A`. The `Action` type is path-dependent and unique to this `World`. These `Actions`s \n  // are values and their constructors are pure.\n  def nextInt = effect(_.nextInt)\n  def nextInt(n: Int) = effect(_.nextInt(n))\n\n  // Expose our unit constructor\n  def unit[A](a: A) = super.unit(a)\n\n  // In order to make our `Action`s runnable, we must provide a public way to invoke `runWorld`. Because the choice of\n  // initial `State` and return value are specific to each `World`, this is left to the user. Here we provide two ways\n  // of running an `Action`. The first consumes a seed value and is referentially transparent. The second returns an IO\n  // action that uses the system clock for the random seed.\n  implicit class RunnableAction[A](a: Action[A]) {\n    def exec(seed: Long): A = runWorld(a, new Random(seed))._2\n    def liftIO: IO[A] = IO(exec(System.currentTimeMillis))\n  }\n\n}\n\nobject RngWorldMinimalTest extends App {\n\n  // Import our world's actions\n  import RngWorldMinimal.{ Action =\u003e Rng, _ }\n\n  // An `Action` that returns a pair of integers, a \u003c 100, b \u003c a\n  val pair: Rng[(Int, Int)] =\n    for {\n      a \u003c- nextInt(100)\n      b \u003c- nextInt(a)\n    } yield (a, b)\n\n  // Run that baby\n  println(pair.exec(0L))   // pure! always returns (60, 28)\n  println(pair.exec(0L))   // exactly the same of course\n  println(pair.exec(123L)) // (82, 52)\n  println(pair.liftIO.unsafePerformIO) // DANGER: impure, who knows what will happen?\n\n  // Show that our world is trampolined.\n  def genMany[A](n: Int, a: Rng[A], acc: List[A] = Nil): Rng[List[A]] =\n    a.flatMap(x =\u003e if (n == 0) unit(x :: acc) else genMany(n - 1, a, x :: acc))\n\n  // Many iterations via flatMap\n  println(genMany(1000000, nextInt).map(_.sum).exec(0L)) // -340966447\n\n}\n```\n\nFor more info...\n----------------\n\nTons of examples down in `src/test`.\n\nYou can find me on `#scala` and on the Tweeter.\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftpolecat%2Ftiny-world","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftpolecat%2Ftiny-world","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftpolecat%2Ftiny-world/lists"}