1
Fork 0

purescript(switcheroo): POC

Signed-off-by: prescientmoon <git@moonythm.dev>
This commit is contained in:
Matei Adriel 2023-02-08 19:32:08 +01:00 committed by prescientmoon
parent ad12ca0bff
commit a1f2c01651
Signed by: prescientmoon
SSH key fingerprint: SHA256:WFp/cO76nbarETAoQcQXuV+0h7XJsEsOCI0UsyPIy6U
5 changed files with 949 additions and 1 deletions

View file

@ -8,3 +8,4 @@
/.purs* /.purs*
/.psa* /.psa*
/.spago /.spago
main.js

View file

@ -0,0 +1,640 @@
{
"nodes": {
"deadnix": {
"inputs": {
"fenix": "fenix",
"naersk": "naersk",
"nixpkgs": "nixpkgs_4",
"utils": "utils"
},
"locked": {
"lastModified": 1655647809,
"narHash": "sha256-npyHYIJW7HyGIFpCZZK+t5JM/v2LsyFhAGJxX1DXO7E=",
"owner": "astro",
"repo": "deadnix",
"rev": "83c42cc64d190ecb72f5929eab0f64fe88e25dc4",
"type": "github"
},
"original": {
"owner": "astro",
"repo": "deadnix",
"type": "github"
}
},
"deadnix_2": {
"inputs": {
"fenix": "fenix_2",
"naersk": "naersk_2",
"nixpkgs": "nixpkgs_8",
"utils": "utils_2"
},
"locked": {
"lastModified": 1656370114,
"narHash": "sha256-XBbSWeBuF6Ck0jc634yAp2hjPXWM2JyRDPCdK0dh3w4=",
"owner": "astro",
"repo": "deadnix",
"rev": "9f450f7250ad7680cb5f12ce5985cc18496c2d5f",
"type": "github"
},
"original": {
"owner": "astro",
"repo": "deadnix",
"type": "github"
}
},
"docs-search": {
"flake": false,
"locked": {
"lastModified": 1661787609,
"narHash": "sha256-jgOl8PKisRmcaHOya3HzArI3eKjVErx+XIBGminh9Zk=",
"owner": "purs-nix",
"repo": "purescript-docs-search",
"rev": "4620575e21886fcbf516d0b43910ba4ead2a60d0",
"type": "github"
},
"original": {
"owner": "purs-nix",
"repo": "purescript-docs-search",
"type": "github"
}
},
"fenix": {
"inputs": {
"nixpkgs": "nixpkgs_2",
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1655533500,
"narHash": "sha256-qJJmLVoMYfDLywI9MNL7sb0W/GsKQF9HDatdHm1tSl0=",
"owner": "nix-community",
"repo": "fenix",
"rev": "b6630603af13df17d0dd4df8629e9a24e6ba0fbd",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "fenix",
"type": "github"
}
},
"fenix_2": {
"inputs": {
"nixpkgs": "nixpkgs_6",
"rust-analyzer-src": "rust-analyzer-src_2"
},
"locked": {
"lastModified": 1655533500,
"narHash": "sha256-qJJmLVoMYfDLywI9MNL7sb0W/GsKQF9HDatdHm1tSl0=",
"owner": "nix-community",
"repo": "fenix",
"rev": "b6630603af13df17d0dd4df8629e9a24e6ba0fbd",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "fenix",
"type": "github"
}
},
"fenix_3": {
"inputs": {
"nixpkgs": [
"purs-nix",
"statix",
"nixpkgs"
],
"rust-analyzer-src": "rust-analyzer-src_3"
},
"locked": {
"lastModified": 1645251813,
"narHash": "sha256-cQ66tGjnZclBCS3nD26mZ5fUH+3/HnysGffBiWXUSHk=",
"owner": "nix-community",
"repo": "fenix",
"rev": "9892337b588c38ec59466a1c89befce464aae7f8",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "fenix",
"type": "github"
}
},
"flake-utils": {
"locked": {
"lastModified": 1618217525,
"narHash": "sha256-WGrhVczjXTiswQaoxQ+0PTfbLNeOQM6M36zvLn78AYg=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c6169a2772643c4a93a0b5ac1c61e296cba68544",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"locked": {
"lastModified": 1618217525,
"narHash": "sha256-WGrhVczjXTiswQaoxQ+0PTfbLNeOQM6M36zvLn78AYg=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c6169a2772643c4a93a0b5ac1c61e296cba68544",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"get-flake": {
"locked": {
"lastModified": 1644686428,
"narHash": "sha256-zkhYsURWFrvEZLkIoBeqFBzSu+cA2u5mo6M8vq9LN7M=",
"owner": "ursi",
"repo": "get-flake",
"rev": "703f15558daa56dfae19d1858bb3046afe68831a",
"type": "github"
},
"original": {
"owner": "ursi",
"repo": "get-flake",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"purs-nix",
"statix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1635165013,
"narHash": "sha256-o/BdVjNwcB6jOmzZjOH703BesSkkS5O7ej3xhyO8hAY=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "5b9e0ff9d3b551234b4f3eb3983744fa354b17f1",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"make-shell": {
"locked": {
"lastModified": 1634940815,
"narHash": "sha256-P69OmveboXzS+es1vQGS4bt+ckwbeIExqxfGLjGuJqA=",
"owner": "ursi",
"repo": "nix-make-shell",
"rev": "8add91681170924e4d0591b22f294aee3f5516f9",
"type": "github"
},
"original": {
"owner": "ursi",
"ref": "1",
"repo": "nix-make-shell",
"type": "github"
}
},
"make-shell_2": {
"locked": {
"lastModified": 1634940815,
"narHash": "sha256-P69OmveboXzS+es1vQGS4bt+ckwbeIExqxfGLjGuJqA=",
"owner": "ursi",
"repo": "nix-make-shell",
"rev": "8add91681170924e4d0591b22f294aee3f5516f9",
"type": "github"
},
"original": {
"owner": "ursi",
"ref": "1",
"repo": "nix-make-shell",
"type": "github"
}
},
"naersk": {
"inputs": {
"nixpkgs": "nixpkgs_3"
},
"locked": {
"lastModified": 1655042882,
"narHash": "sha256-9BX8Fuez5YJlN7cdPO63InoyBy7dm3VlJkkmTt6fS1A=",
"owner": "nix-community",
"repo": "naersk",
"rev": "cddffb5aa211f50c4b8750adbec0bbbdfb26bb9f",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "naersk",
"type": "github"
}
},
"naersk_2": {
"inputs": {
"nixpkgs": "nixpkgs_7"
},
"locked": {
"lastModified": 1655042882,
"narHash": "sha256-9BX8Fuez5YJlN7cdPO63InoyBy7dm3VlJkkmTt6fS1A=",
"owner": "nix-community",
"repo": "naersk",
"rev": "cddffb5aa211f50c4b8750adbec0bbbdfb26bb9f",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "naersk",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1675698036,
"narHash": "sha256-BgsQkQewdlQi8gapJN4phpxkI/FCE/2sORBaFcYbp/A=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1046c7b92e908a1202c0f1ba3fc21d19e1cf1b62",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_10": {
"locked": {
"lastModified": 1645013224,
"narHash": "sha256-b7OEC8vwzJv3rsz9pwnTX2LQDkeOWz2DbKypkVvNHXc=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "b66b39216b1fef2d8c33cc7a5c72d8da80b79970",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1655400192,
"narHash": "sha256-49OBVVRgb9H/PSmNT9W61+NRdDbuSJVuDDflwXlaUKU=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "3d7435c638baffaa826b85459df0fff47f12317d",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1655481042,
"narHash": "sha256-XHbcywq2vIQ5CeH1OK3TN793jkiNAAZsSctS1PFgseo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "103a4c0ae46afa9cf008c30744175315ca38e9f9",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"nixpkgs_4": {
"locked": {
"lastModified": 1655481042,
"narHash": "sha256-XHbcywq2vIQ5CeH1OK3TN793jkiNAAZsSctS1PFgseo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "103a4c0ae46afa9cf008c30744175315ca38e9f9",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"nixpkgs_5": {
"locked": {
"lastModified": 1646506091,
"narHash": "sha256-sWNAJE2m+HOh1jtXlHcnhxsj6/sXrHgbqVNcVRlveK4=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "3e644bd62489b516292c816f70bf0052c693b3c7",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_6": {
"locked": {
"lastModified": 1655400192,
"narHash": "sha256-49OBVVRgb9H/PSmNT9W61+NRdDbuSJVuDDflwXlaUKU=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "3d7435c638baffaa826b85459df0fff47f12317d",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_7": {
"locked": {
"lastModified": 1655481042,
"narHash": "sha256-XHbcywq2vIQ5CeH1OK3TN793jkiNAAZsSctS1PFgseo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "103a4c0ae46afa9cf008c30744175315ca38e9f9",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"nixpkgs_8": {
"locked": {
"lastModified": 1655481042,
"narHash": "sha256-XHbcywq2vIQ5CeH1OK3TN793jkiNAAZsSctS1PFgseo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "103a4c0ae46afa9cf008c30744175315ca38e9f9",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"nixpkgs_9": {
"locked": {
"lastModified": 1656549732,
"narHash": "sha256-eILutFZGjfk2bEzfim8S/qyYc//0S1KsCeO+OWbtoR0=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d3248619647234b5dc74a6921bcdf6dd8323eb22",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"parsec": {
"locked": {
"lastModified": 1635533376,
"narHash": "sha256-/HrG0UPGnI5VdkhrNrpDiM2+nhdL6lD/bqyGtYv0QDE=",
"owner": "nprindle",
"repo": "nix-parsec",
"rev": "1bf25dd9c5de1257a1c67de3c81c96d05e8beb5e",
"type": "github"
},
"original": {
"owner": "nprindle",
"repo": "nix-parsec",
"type": "github"
}
},
"ps-tools": {
"inputs": {
"deadnix": "deadnix_2",
"make-shell": "make-shell_2",
"nixpkgs": "nixpkgs_9",
"utils": "utils_3"
},
"locked": {
"lastModified": 1658374818,
"narHash": "sha256-WxbQ/BR4Ep8tBbaOikXechspyZlvwfL5XNmRNEnaOFo=",
"owner": "purs-nix",
"repo": "purescript-tools",
"rev": "c0f887f60ea2331dfdc5b0e8be2e732976887345",
"type": "github"
},
"original": {
"owner": "purs-nix",
"repo": "purescript-tools",
"type": "github"
}
},
"purs-nix": {
"inputs": {
"deadnix": "deadnix",
"docs-search": "docs-search",
"get-flake": "get-flake",
"make-shell": "make-shell",
"nixpkgs": "nixpkgs_5",
"parsec": "parsec",
"ps-tools": "ps-tools",
"statix": "statix",
"utils": "utils_4"
},
"locked": {
"lastModified": 1674243319,
"narHash": "sha256-o39rBVSNqchahHrMYNixdlasDro8omlf/n7yQZsdNI8=",
"owner": "purs-nix",
"repo": "purs-nix",
"rev": "2b7761ffaded363d0d00afe320350cc5c9ee9012",
"type": "github"
},
"original": {
"owner": "purs-nix",
"ref": "ps-0.15",
"repo": "purs-nix",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"ps-tools": [
"purs-nix",
"ps-tools"
],
"purs-nix": "purs-nix",
"utils": "utils_5"
}
},
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1655507737,
"narHash": "sha256-o+AqNsjL6o2RHh4InZHQVpkmqg570YFJL4Db8mKq+fs=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "12dd81092e37df28b7a3591cae9675e668927198",
"type": "github"
},
"original": {
"owner": "rust-lang",
"ref": "nightly",
"repo": "rust-analyzer",
"type": "github"
}
},
"rust-analyzer-src_2": {
"flake": false,
"locked": {
"lastModified": 1655507737,
"narHash": "sha256-o+AqNsjL6o2RHh4InZHQVpkmqg570YFJL4Db8mKq+fs=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "12dd81092e37df28b7a3591cae9675e668927198",
"type": "github"
},
"original": {
"owner": "rust-lang",
"ref": "nightly",
"repo": "rust-analyzer",
"type": "github"
}
},
"rust-analyzer-src_3": {
"flake": false,
"locked": {
"lastModified": 1645205556,
"narHash": "sha256-e4lZW3qRyOEJ+vLKFQP7m2Dxh5P44NrnekZYLxlucww=",
"owner": "rust-analyzer",
"repo": "rust-analyzer",
"rev": "acf5874b39f3dc5262317a6074d9fc7285081161",
"type": "github"
},
"original": {
"owner": "rust-analyzer",
"ref": "nightly",
"repo": "rust-analyzer",
"type": "github"
}
},
"statix": {
"inputs": {
"fenix": "fenix_3",
"gitignore": "gitignore",
"nixpkgs": "nixpkgs_10"
},
"locked": {
"lastModified": 1657460333,
"narHash": "sha256-5o6zMBASEsGKtjKDb3SizJnN9A7qpOcbzWBXsacfMyc=",
"owner": "nerdypepper",
"repo": "statix",
"rev": "6422c959d365dee2fda5eda8858fefad31f17b25",
"type": "github"
},
"original": {
"owner": "nerdypepper",
"repo": "statix",
"type": "github"
}
},
"utils": {
"locked": {
"lastModified": 1653893745,
"narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"utils_2": {
"locked": {
"lastModified": 1653893745,
"narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"utils_3": {
"inputs": {
"flake-utils": "flake-utils"
},
"locked": {
"lastModified": 1656044990,
"narHash": "sha256-f01BB7CaOyntOab9XnpH9HD63rGcnu2iyL4M2ubs5F8=",
"owner": "ursi",
"repo": "flake-utils",
"rev": "f53b674a2c90f6202a2f4cd491aba121775490b5",
"type": "github"
},
"original": {
"owner": "ursi",
"ref": "8",
"repo": "flake-utils",
"type": "github"
}
},
"utils_4": {
"inputs": {
"flake-utils": "flake-utils_2"
},
"locked": {
"lastModified": 1656044990,
"narHash": "sha256-f01BB7CaOyntOab9XnpH9HD63rGcnu2iyL4M2ubs5F8=",
"owner": "ursi",
"repo": "flake-utils",
"rev": "f53b674a2c90f6202a2f4cd491aba121775490b5",
"type": "github"
},
"original": {
"owner": "ursi",
"ref": "8",
"repo": "flake-utils",
"type": "github"
}
},
"utils_5": {
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View file

@ -20,6 +20,10 @@
[ console [ console
effect effect
prelude prelude
exists
tuples
indexed-monad
aff
]; ];
dir = ./.; dir = ./.;

View file

@ -2,8 +2,119 @@ module Main where
import Prelude import Prelude
import Control.Monad.Indexed.Qualified as Ix
import Data.Array as Array
import Data.Identity (Identity(..))
import Effect (Effect) import Effect (Effect)
import Effect.Console (log) import Effect.Console (log)
import Safe.Coerce (coerce)
import Swictheroo.Stream (Finished, Producer, constantProducer, runConsumeM_)
import Swictheroo.Stream as Stream
type Producers m =
{ download :: Producer m Int
, reportDownload :: Producer m String
, ping :: Producer m Boolean
}
program :: forall m. Monad m => Producers m -> Finished m String Unit
program producers = Ix.do
Stream.replace producers.download
a <- Stream.pull
Stream.replace producers.reportDownload
b <- Stream.pull
Stream.replace producers.ping
c <- Stream.pull
Stream.terminate $ Array.fold
[ "Download: "
, show a
, ", Report: "
, show b
, ", Ping: "
, show c
]
{-
Pseudo-code for testing:
data SyntheticEvent = Download Int | Report String | Ping Int
producers =
{ download: case _ of
Download s -> Just s
_ -> Nothing
, report: ...
, ping: ...
}
story =
[ Emit (Download 7)
, Expect Cancel
, Emit (Report 7)
, Expect Cancel
, Emit (Ping 7)
, Expect Cancel
]
-}
{- How do we cancel our program?
The program would require an extra producer called "cancel".
Every time we call "Stream.replace", we would merge "cancel" with
the respective new stream. This way, when we call "Stream.pull" we can
either receive a "Left" (which means we must cancel) or a "Right"
(which means we have gotten our value and can keep going).
-}
{- All async effects should be avoided outside the internals of our monad!
Eg: do
a <- lift $ Aff.wait ...
This is *bad*, because it means we cannot receive a cancellation event.
A better idea would be:
do
Stream.mapSource (\old -> merge old (fromAff (Aff.wait ...)))
result <- Stream.pull
case result of
-- Our effect finished running!
Left effectResult -> do
-- Return to old producer
Stream.mapSource Either.hush
-- Do stuff with the result of the effect
...
-- Try again later!
Right otherResult -> do
-- Handle the other result
...
This does not block the other events at any point!
Although this might look complicated, it's possible to create helpers for
common patterns like this one!
Note that in an actual production codebase we would receive
(fromAff (Aff.wait ...)) from the exterior in order to
allow mocking and to not tie ourselves to a specific monad.
-}
{- One could thing about this approach is
that simple state can be kept without the need for StateT!
(because we can simply pass around values)
-}
main :: Effect Unit main :: Effect Unit
main = log "❄" main = log (coerce result)
where
result :: Identity _
result = runConsumeM_ $ program
{ download: constantProducer 3
, reportDownload: constantProducer "foo"
, ping: constantProducer true
}

View file

@ -0,0 +1,192 @@
module Swictheroo.Stream where
import Prelude
import Control.Alt (class Alt)
import Control.Alternative (class Plus, (<|>))
import Control.Applicative.Indexed (class IxApplicative, class IxApply, class IxFunctor)
import Control.Bind.Indexed (class IxBind)
import Control.Monad.Indexed (class IxMonad, iap)
import Control.Parallel (parOneOf, parSequence_)
import Data.Either (Either(..))
import Data.Maybe (Maybe(..))
import Data.Tuple.Nested (type (/\), (/\))
import Effect.Aff (Aff, never)
-- | A producer can:
-- | - Produce values
-- | - Destroy itself
-- |
-- | A producer does this by reacting to input events
-- | in order to satisfy continuations.
newtype Producer m a =
Producer (forall c. Monad m => ProducerEvent m c a -> m c)
data ProducerEvent m c a
-- | Producers advance to a new version once they produce a value.
-- | This allows pure producers to exist. For example, a pure producer
-- | could hold an array of events, return the first one, and then create
-- | a new producer from the tail of the array
= Produce (a -> Producer m a -> c)
| Destroy c
produce :: forall m i. Monad m => Producer m i -> m (i /\ Producer m i)
produce (Producer producer) = producer (Produce (/\))
destroyProducer :: forall m i. Monad m => Producer m i -> m Unit
destroyProducer (Producer producer) = producer (Destroy unit)
constantProducer :: forall m a. Monad m => a -> Producer m a
constantProducer value = Producer case _ of
Destroy c -> pure c
Produce next -> pure (next value (constantProducer value))
unitProducer :: forall m. Monad m => Producer m Unit
unitProducer = constantProducer unit
filterMapProducer :: forall a b m. Monad m => (a -> Maybe b) -> Producer m a -> Producer m b
filterMapProducer f producer = Producer case _ of
Destroy c -> destroyProducer producer $> c
Produce next -> loop producer
where
-- Keeps producing values until one matches the given predicate!
loop producer = do
value /\ producer' <- produce producer
case f value of
Nothing -> loop producer'
Just updated -> pure (next updated (filterMapProducer f producer'))
-- | Type parameter explanation:
-- | - m = underlying monad
-- | - t = type we want to end on
-- | - i = what the producer present when
-- | the computation *starts* needs to produce
-- | - o = what the producer present when
-- | the computation *ends* needs to produce
-- | - a = result of the computation
-- |
-- | This monad encapsulates the followin 3 operations:
-- | - Basic continuations (we have a type we want to terminate on)
-- | - Consuming values from a producer
-- | - Cancelling + replacing the current producer with a different one.
newtype ConsumeM m t i o a = ConsumeM
( forall c
. { producer :: Producer m i
, continue :: Producer m o -> a -> m c
, terminate :: t -> m c
}
-> m c
)
-- | A ConsumeM computation that has finshed running
type Finished m t i = ConsumeM m t i Void Void
pull :: forall m t i. Monad m => ConsumeM m t i i i
pull = ConsumeM \inputs -> do
i /\ producer' <- produce inputs.producer
inputs.continue producer' i
replace :: forall m t i o. Monad m => Producer m o -> ConsumeM m t i o Unit
replace producer' = ConsumeM \inputs -> do
destroyProducer inputs.producer
inputs.continue producer' unit
terminate :: forall m t i. Monad m => t -> Finished m t i
terminate result = ConsumeM \inputs -> do
destroyProducer inputs.producer
inputs.terminate result
lift :: forall m t i a. Monad m => m a -> ConsumeM m t i i a
lift computation = ConsumeM \inputs -> do
result <- computation
inputs.continue inputs.producer result
producer :: forall m t i. Monad m => ConsumeM m t i i (Producer m i)
producer = ConsumeM \inputs -> inputs.continue inputs.producer inputs.producer
mapSource :: forall m t i o. Monad m => (Producer m i -> Producer m o) -> ConsumeM m t i o Unit
mapSource f = ConsumeM \inputs -> do
let new = f inputs.producer
inputs.continue new unit
runConsumeM :: forall m i t. Monad m => Producer m i -> Finished m t i -> m t
runConsumeM producer (ConsumeM run) = run
{ producer
, terminate: pure
, continue: \producer value -> absurd value -- not eta-reduced for clarity
}
runConsumeM_ :: forall m t. Monad m => Finished m t Unit -> m t
runConsumeM_ = runConsumeM unitProducer
---------- Typeclass instances
instance Monad m => Functor (Producer m) where
map f (Producer producer) = Producer case _ of
Destroy c -> producer (Destroy c)
Produce next -> producer $ Produce \a p -> next (f a) (map f p)
instance Monad m => IxFunctor (ConsumeM m t) where
imap f (ConsumeM consumer) = ConsumeM
\inputs -> consumer
{ producer: inputs.producer
, terminate: inputs.terminate
, continue: \producer a -> inputs.continue producer (f a)
}
instance Monad m => IxApply (ConsumeM m t) where
iapply = iap
instance Monad m => IxApplicative (ConsumeM m t) where
ipure a = ConsumeM \inputs -> inputs.continue inputs.producer a
instance Monad m => IxBind (ConsumeM m t) where
ibind (ConsumeM consumer) f = ConsumeM
\inputs -> do
consumerResult <- consumer
{ producer: inputs.producer
, terminate: \result -> pure (Left result)
, continue: \a b -> pure (Right (a /\ b))
}
case consumerResult of
-- Terminated
Left final -> inputs.terminate final
-- Kept going
Right (producer' /\ result) -> do
let (ConsumeM consumer') = f result
consumer'
{ producer: producer'
, continue: inputs.continue
, terminate: inputs.terminate
}
instance Monad m => IxMonad (ConsumeM m t)
---------- Merge producers
type AffProducer = Producer Aff
instance Alt AffProducer where
alt first second = Producer case _ of
Produce continue -> ado
result <- parOneOf
[ produce first <#> Left
, produce second <#> Right
]
in
case result of
Left (result /\ first') -> continue result (first' <|> second)
Right (result /\ second') -> continue result (first <|> second')
Destroy continue -> ado
parSequence_
[ destroyProducer first
, destroyProducer second
]
in continue
instance Plus AffProducer where
empty = Producer \_ -> never