diff --git a/README.md b/README.md new file mode 100644 index 0000000..49e8622 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# purescript-var + +An API to provide uniform read/write access to the references in the `Eff` monad. diff --git a/bower.json b/bower.json index a7b92a4..a9c760a 100644 --- a/bower.json +++ b/bower.json @@ -13,5 +13,8 @@ "purescript-eff": "~0.1.0", "purescript-contravariant": "~0.2.1", "purescript-invariant": "~0.3.0" + }, + "devDependencies": { + "purescript-console": "~0.1.1" } } diff --git a/docs/Control/Monad/Eff/Var.md b/docs/Control/Monad/Eff/Var.md new file mode 100644 index 0000000..a9ccc9a --- /dev/null +++ b/docs/Control/Monad/Eff/Var.md @@ -0,0 +1,160 @@ +## Module Control.Monad.Eff.Var + +`Var`s allow to provide a uniform read/write access to the references in +the `Eff` monad. This is mostly useful when making low-level FFI bindings. +For example we might have some global counter with the following API: +```purescript +foreign import data COUNT :: ! +getCounter :: forall eff. Eff (count :: COUNT | eff) Int +setCounter :: forall eff. Int -> Eff (count :: COUNT | eff) Unit +``` + +`getCounter` and `setCounter` can be kept together in a `Var`: +```purescript +counter :: forall eff. Var (count :: COUNT | eff) Int +counter = makeVar getCounter setCounter +``` + +`counter` can be used in this way: +```purescript +main = do + counter $= 0 -- set counter to 0 + get counter >>= print -- => 0 + counter $= 2 -- set counter to 2 + get counter >>= print -- => 2 + counter $~ (* 5) -- multiply counter by 5 + get counter >>= print -- => 10 +``` + +#### `Gettable` + +``` purescript +class Gettable (eff :: # !) (var :: * -> *) (a :: *) where + get :: var a -> Eff eff a +``` + +Typeclass for vars that can be read. + +##### Instances +``` purescript +instance gettableVar :: Gettable eff (Var eff) a +instance gettableGettableVar :: Gettable eff (GettableVar eff) a +``` + +#### `Settable` + +``` purescript +class Settable (eff :: # !) (var :: * -> *) (a :: *) where + set :: var a -> a -> Eff eff Unit +``` + +Typeclass for vars that can be written. + +##### Instances +``` purescript +instance settableVar :: Settable eff (Var eff) a +instance settableSettableVar :: Settable eff (SettableVar eff) a +``` + +#### `($=)` + +``` purescript +($=) :: forall eff var a. (Settable eff var a) => var a -> a -> Eff eff Unit +``` + +_right-associative / precedence 2_ + +#### `Updatable` + +``` purescript +class Updatable (eff :: # !) (var :: * -> *) (a :: *) where + update :: var a -> (a -> a) -> Eff eff Unit +``` + +Typeclass for vars that can be updated. + +##### Instances +``` purescript +instance updatableVar :: Updatable eff (Var eff) a +``` + +#### `($~)` + +``` purescript +($~) :: forall eff var a. (Updatable eff var a) => var a -> (a -> a) -> Eff eff Unit +``` + +_right-associative / precedence 2_ + +#### `Var` + +``` purescript +data Var (eff :: # !) a +``` + +Read/Write var which holds a value of type `a` and produces effects `eff` +when read or written. + +##### Instances +``` purescript +instance settableVar :: Settable eff (Var eff) a +instance gettableVar :: Gettable eff (Var eff) a +instance updatableVar :: Updatable eff (Var eff) a +instance invariantVar :: Invariant (Var eff) +``` + +#### `makeVar` + +``` purescript +makeVar :: forall eff a. Eff eff a -> (a -> Eff eff Unit) -> Var eff a +``` + +Create a `Var` from getter and setter. + +#### `GettableVar` + +``` purescript +newtype GettableVar eff a +``` + +Read-only var which holds a value of type `a` and produces effects `eff` +when read. + +##### Instances +``` purescript +instance gettableGettableVar :: Gettable eff (GettableVar eff) a +instance functorGettableVar :: Functor (GettableVar eff) +``` + +#### `makeGettableVar` + +``` purescript +makeGettableVar :: forall eff a. Eff eff a -> GettableVar eff a +``` + +Create a `GettableVar` from getter. + +#### `SettableVar` + +``` purescript +newtype SettableVar eff a +``` + +Write-only var which holds a value of type `a` and produces effects `eff` +when written. + +##### Instances +``` purescript +instance settableSettableVar :: Settable eff (SettableVar eff) a +instance contravariantSettableVar :: Contravariant (SettableVar eff) +``` + +#### `makeSettableVar` + +``` purescript +makeSettableVar :: forall eff a. (a -> Eff eff Unit) -> SettableVar eff a +``` + +Create a `SettableVar` from setter. + + diff --git a/src/Control/Monad/Eff/Var.purs b/src/Control/Monad/Eff/Var.purs index 82aaa3f..feb9274 100644 --- a/src/Control/Monad/Eff/Var.purs +++ b/src/Control/Monad/Eff/Var.purs @@ -1,3 +1,30 @@ +-- | `Var`s allow to provide a uniform read/write access to the references in +-- | the `Eff` monad. This is mostly useful when making low-level FFI bindings. + +-- | For example we might have some global counter with the following API: +-- | ```purescript +-- | foreign import data COUNT :: ! +-- | getCounter :: forall eff. Eff (count :: COUNT | eff) Int +-- | setCounter :: forall eff. Int -> Eff (count :: COUNT | eff) Unit +-- | ``` +-- | +-- | `getCounter` and `setCounter` can be kept together in a `Var`: +-- | ```purescript +-- | counter :: forall eff. Var (count :: COUNT | eff) Int +-- | counter = makeVar getCounter setCounter +-- | ``` +-- | +-- | `counter` can be used in this way: +-- | ```purescript +-- | main = do +-- | counter $= 0 -- set counter to 0 +-- | get counter >>= print -- => 0 +-- | counter $= 2 -- set counter to 2 +-- | get counter >>= print -- => 2 +-- | counter $~ (* 5) -- multiply counter by 5 +-- | get counter >>= print -- => 10 +-- | ``` + module Control.Monad.Eff.Var ( Gettable , get @@ -18,30 +45,39 @@ module Control.Monad.Eff.Var import Prelude import Control.Monad.Eff +import Control.Monad.Eff.Ref import Data.Functor.Contravariant import Data.Functor.Invariant +-- | Typeclass for vars that can be read. class Gettable (eff :: # !) (var :: * -> *) (a :: *) where get :: var a -> Eff eff a +-- | Typeclass for vars that can be written. class Settable (eff :: # !) (var :: * -> *) (a :: *) where set :: var a -> a -> Eff eff Unit +-- | Alias for `set`. infixr 2 $= ($=) :: forall eff var a. (Settable eff var a) => var a -> a -> Eff eff Unit ($=) = set +-- | Typeclass for vars that can be updated. class Updatable (eff :: # !) (var :: * -> *) (a :: *) where update :: var a -> (a -> a) -> Eff eff Unit +-- | Alias for `get` infixr 2 $~ ($~) :: forall eff var a. (Updatable eff var a) => var a -> (a -> a) -> Eff eff Unit ($~) = update +-- | Read/Write var which holds a value of type `a` and produces effects `eff` +-- | when read or written. data Var (eff :: # !) a = Var (GettableVar eff a) (SettableVar eff a) +-- | Create a `Var` from getter and setter. makeVar :: forall eff a. Eff eff a -> (a -> Eff eff Unit) -> Var eff a makeVar g s = Var (makeGettableVar g) (makeSettableVar s) @@ -57,8 +93,11 @@ instance updatableVar :: Updatable eff (Var eff) a where instance invariantVar :: Invariant (Var eff) where imap ab ba (Var ga sa) = Var (ab <$> ga) (ba >$< sa) +-- | Read-only var which holds a value of type `a` and produces effects `eff` +-- | when read. newtype GettableVar eff a = GettableVar (Eff eff a) +-- | Create a `GettableVar` from getter. makeGettableVar :: forall eff a. Eff eff a -> GettableVar eff a makeGettableVar = GettableVar @@ -68,8 +107,11 @@ instance gettableGettableVar :: Gettable eff (GettableVar eff) a where instance functorGettableVar :: Functor (GettableVar eff) where map f (GettableVar a) = GettableVar (f <$> a) +-- | Write-only var which holds a value of type `a` and produces effects `eff` +-- | when written. newtype SettableVar eff a = SettableVar (a -> Eff eff Unit) +-- | Create a `SettableVar` from setter. makeSettableVar :: forall eff a. (a -> Eff eff Unit) -> SettableVar eff a makeSettableVar = SettableVar