From b80d69a13101a22353fcaf79b135c616875cdd2c Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Fri, 13 Apr 2018 12:40:59 +0100 Subject: [PATCH 01/23] Add `Monoid` class and newtypes --- src/Data/Monoid.purs | 67 +++++++++++++++++++++++++++++ src/Data/Monoid/Additive.purs | 44 +++++++++++++++++++ src/Data/Monoid/Conj.purs | 51 ++++++++++++++++++++++ src/Data/Monoid/Disj.purs | 51 ++++++++++++++++++++++ src/Data/Monoid/Dual.purs | 44 +++++++++++++++++++ src/Data/Monoid/Endo.purs | 29 +++++++++++++ src/Data/Monoid/Multiplicative.purs | 44 +++++++++++++++++++ src/Prelude.purs | 2 + 8 files changed, 332 insertions(+) create mode 100644 src/Data/Monoid.purs create mode 100644 src/Data/Monoid/Additive.purs create mode 100644 src/Data/Monoid/Conj.purs create mode 100644 src/Data/Monoid/Disj.purs create mode 100644 src/Data/Monoid/Dual.purs create mode 100644 src/Data/Monoid/Endo.purs create mode 100644 src/Data/Monoid/Multiplicative.purs diff --git a/src/Data/Monoid.purs b/src/Data/Monoid.purs new file mode 100644 index 00000000..19bc1e2f --- /dev/null +++ b/src/Data/Monoid.purs @@ -0,0 +1,67 @@ +module Data.Monoid + ( class Monoid, mempty + , power + , guard + , module Data.Semigroup + ) where + +import Data.Boolean (otherwise) +import Data.Eq ((==)) +import Data.EuclideanRing (mod, (/)) +import Data.Ord ((<=)) +import Data.Ordering (Ordering(..)) +import Data.Semigroup (class Semigroup, (<>)) +import Data.Unit (Unit, unit) + +-- | A `Monoid` is a `Semigroup` with a value `mempty`, which is both a +-- | left and right unit for the associative operation `<>`: +-- | +-- | ``` +-- | forall x. mempty <> x = x <> mempty = x +-- | ``` +-- | +-- | `Monoid`s are commonly used as the result of fold operations, where +-- | `<>` is used to combine individual results, and `mempty` gives the result +-- | of folding an empty collection of elements. +class Semigroup m <= Monoid m where + mempty :: m + +instance monoidUnit :: Monoid Unit where + mempty = unit + +instance monoidOrdering :: Monoid Ordering where + mempty = EQ + +instance monoidFn :: Monoid b => Monoid (a -> b) where + mempty _ = mempty + +instance monoidString :: Monoid String where + mempty = "" + +instance monoidArray :: Monoid (Array a) where + mempty = [] + +-- | Append a value to itself a certain number of times. For the +-- | `Multiplicative` type, and for a non-negative power, this is the same as +-- | normal number exponentiation. +-- | +-- | If the second argument is negative this function will return `mempty` +-- | (*unlike* normal number exponentiation). The `Monoid` constraint alone +-- | is not enough to write a `power` function with the property that `power x +-- | n` cancels with `power x (-n)`, i.e. `power x n <> power x (-n) = mempty`. +-- | For that, we would additionally need the ability to invert elements, i.e. +-- | a Group. +power :: forall m. Monoid m => m -> Int -> m +power x = go + where + go :: Int -> m + go p + | p <= 0 = mempty + | p == 1 = x + | p `mod` 2 == 0 = let x' = go (p / 2) in x' <> x' + | otherwise = let x' = go (p / 2) in x' <> x' <> x + +-- | Allow or "truncate" a Monoid to its `mempty` value based on a condition. +guard :: forall m. Monoid m => Boolean -> m -> m +guard true a = a +guard false _ = mempty diff --git a/src/Data/Monoid/Additive.purs b/src/Data/Monoid/Additive.purs new file mode 100644 index 00000000..6c365b14 --- /dev/null +++ b/src/Data/Monoid/Additive.purs @@ -0,0 +1,44 @@ +module Data.Monoid.Additive where + +import Prelude + +import Data.Eq (class Eq1) +import Data.Ord (class Ord1) + +-- | Monoid and semigroup for semirings under addition. +-- | +-- | ``` purescript +-- | Additive x <> Additive y == Additive (x + y) +-- | mempty :: Additive _ == Additive zero +-- | ``` +newtype Additive a = Additive a + +derive newtype instance eqAdditive :: Eq a => Eq (Additive a) +derive instance eq1Additive :: Eq1 Additive + +derive newtype instance ordAdditive :: Ord a => Ord (Additive a) +derive instance ord1Additive :: Ord1 Additive + +derive newtype instance boundedAdditive :: Bounded a => Bounded (Additive a) + +instance showAdditive :: Show a => Show (Additive a) where + show (Additive a) = "(Additive " <> show a <> ")" + +derive instance functorAdditive :: Functor Additive + +instance applyAdditive :: Apply Additive where + apply (Additive f) (Additive x) = Additive (f x) + +instance applicativeAdditive :: Applicative Additive where + pure = Additive + +instance bindAdditive :: Bind Additive where + bind (Additive x) f = f x + +instance monadAdditive :: Monad Additive + +instance semigroupAdditive :: Semiring a => Semigroup (Additive a) where + append (Additive a) (Additive b) = Additive (a + b) + +instance monoidAdditive :: Semiring a => Monoid (Additive a) where + mempty = Additive zero diff --git a/src/Data/Monoid/Conj.purs b/src/Data/Monoid/Conj.purs new file mode 100644 index 00000000..0b090c31 --- /dev/null +++ b/src/Data/Monoid/Conj.purs @@ -0,0 +1,51 @@ +module Data.Monoid.Conj where + +import Prelude + +import Data.Eq (class Eq1) +import Data.HeytingAlgebra (ff, tt) +import Data.Ord (class Ord1) + +-- | Monoid and semigroup for conjuntion. +-- | +-- | ``` purescript +-- | Conj x <> Conj y == Conj (x && y) +-- | mempty :: Conj _ == Conj top +-- | ``` +newtype Conj a = Conj a + +derive newtype instance eqConj :: Eq a => Eq (Conj a) +derive instance eq1Conj :: Eq1 Conj + +derive newtype instance ordConj :: Ord a => Ord (Conj a) +derive instance ord1Conj :: Ord1 Conj + +derive newtype instance boundedConj :: Bounded a => Bounded (Conj a) + +instance showConj :: (Show a) => Show (Conj a) where + show (Conj a) = "(Conj " <> show a <> ")" + +derive instance functorConj :: Functor Conj + +instance applyConj :: Apply Conj where + apply (Conj f) (Conj x) = Conj (f x) + +instance applicativeConj :: Applicative Conj where + pure = Conj + +instance bindConj :: Bind Conj where + bind (Conj x) f = f x + +instance monadConj :: Monad Conj + +instance semigroupConj :: HeytingAlgebra a => Semigroup (Conj a) where + append (Conj a) (Conj b) = Conj (conj a b) + +instance monoidConj :: HeytingAlgebra a => Monoid (Conj a) where + mempty = Conj tt + +instance semiringConj :: HeytingAlgebra a => Semiring (Conj a) where + zero = Conj tt + one = Conj ff + add (Conj a) (Conj b) = Conj (conj a b) + mul (Conj a) (Conj b) = Conj (disj a b) diff --git a/src/Data/Monoid/Disj.purs b/src/Data/Monoid/Disj.purs new file mode 100644 index 00000000..ea734b90 --- /dev/null +++ b/src/Data/Monoid/Disj.purs @@ -0,0 +1,51 @@ +module Data.Monoid.Disj where + +import Prelude + +import Data.Eq (class Eq1) +import Data.HeytingAlgebra (ff, tt) +import Data.Ord (class Ord1) + +-- | Monoid and semigroup for disjuntion. +-- | +-- | ``` purescript +-- | Disj x <> Disj y == Disj (x || y) +-- | mempty :: Disj _ == Disj bottom +-- | ``` +newtype Disj a = Disj a + +derive newtype instance eqDisj :: Eq a => Eq (Disj a) +derive instance eq1Disj :: Eq1 Disj + +derive newtype instance ordDisj :: Ord a => Ord (Disj a) +derive instance ord1Disj :: Ord1 Disj + +derive newtype instance boundedDisj :: Bounded a => Bounded (Disj a) + +instance showDisj :: Show a => Show (Disj a) where + show (Disj a) = "(Disj " <> show a <> ")" + +derive instance functorDisj :: Functor Disj + +instance applyDisj :: Apply Disj where + apply (Disj f) (Disj x) = Disj (f x) + +instance applicativeDisj :: Applicative Disj where + pure = Disj + +instance bindDisj :: Bind Disj where + bind (Disj x) f = f x + +instance monadDisj :: Monad Disj + +instance semigroupDisj :: HeytingAlgebra a => Semigroup (Disj a) where + append (Disj a) (Disj b) = Disj (disj a b) + +instance monoidDisj :: HeytingAlgebra a => Monoid (Disj a) where + mempty = Disj ff + +instance semiringDisj :: HeytingAlgebra a => Semiring (Disj a) where + zero = Disj ff + one = Disj tt + add (Disj a) (Disj b) = Disj (disj a b) + mul (Disj a) (Disj b) = Disj (conj a b) diff --git a/src/Data/Monoid/Dual.purs b/src/Data/Monoid/Dual.purs new file mode 100644 index 00000000..197a833c --- /dev/null +++ b/src/Data/Monoid/Dual.purs @@ -0,0 +1,44 @@ +module Data.Monoid.Dual where + +import Prelude + +import Data.Eq (class Eq1) +import Data.Ord (class Ord1) + +-- | The dual of a monoid. +-- | +-- | ``` purescript +-- | Dual x <> Dual y == Dual (y <> x) +-- | mempty :: Dual _ == Dual mempty +-- | ``` +newtype Dual a = Dual a + +derive newtype instance eqDual :: Eq a => Eq (Dual a) +derive instance eq1Dual :: Eq1 Dual + +derive newtype instance ordDual :: Ord a => Ord (Dual a) +derive instance ord1Dual :: Ord1 Dual + +derive newtype instance boundedDual :: Bounded a => Bounded (Dual a) + +instance showDual :: Show a => Show (Dual a) where + show (Dual a) = "(Dual " <> show a <> ")" + +derive instance functorDual :: Functor Dual + +instance applyDual :: Apply Dual where + apply (Dual f) (Dual x) = Dual (f x) + +instance applicativeDual :: Applicative Dual where + pure = Dual + +instance bindDual :: Bind Dual where + bind (Dual x) f = f x + +instance monadDual :: Monad Dual + +instance semigroupDual :: Semigroup a => Semigroup (Dual a) where + append (Dual x) (Dual y) = Dual (y <> x) + +instance monoidDual :: Monoid a => Monoid (Dual a) where + mempty = Dual mempty diff --git a/src/Data/Monoid/Endo.purs b/src/Data/Monoid/Endo.purs new file mode 100644 index 00000000..19678c3e --- /dev/null +++ b/src/Data/Monoid/Endo.purs @@ -0,0 +1,29 @@ +module Data.Monoid.Endo where + +import Prelude + +-- | Monoid and semigroup for category endomorphisms. +-- | +-- | When `c` is instantiated with `->` this composes functions of type +-- | `a -> a`: +-- | +-- | ``` purescript +-- | Endo f <> Endo g == Endo (f <<< g) +-- | mempty :: Endo _ == Endo id +-- | ``` +newtype Endo c a = Endo (c a a) + +derive newtype instance eqEndo :: Eq (c a a) => Eq (Endo c a) + +derive newtype instance ordEndo :: Ord (c a a) => Ord (Endo c a) + +derive newtype instance boundedEndo :: Bounded (c a a) => Bounded (Endo c a) + +instance showEndo :: Show (c a a) => Show (Endo c a) where + show (Endo x) = "(Endo " <> show x <> ")" + +instance semigroupEndo :: Semigroupoid c => Semigroup (Endo c a) where + append (Endo a) (Endo b) = Endo (a <<< b) + +instance monoidEndo :: Category c => Monoid (Endo c a) where + mempty = Endo id diff --git a/src/Data/Monoid/Multiplicative.purs b/src/Data/Monoid/Multiplicative.purs new file mode 100644 index 00000000..3b929ab8 --- /dev/null +++ b/src/Data/Monoid/Multiplicative.purs @@ -0,0 +1,44 @@ +module Data.Monoid.Multiplicative where + +import Prelude + +import Data.Eq (class Eq1) +import Data.Ord (class Ord1) + +-- | Monoid and semigroup for semirings under multiplication. +-- | +-- | ``` purescript +-- | Multiplicative x <> Multiplicative y == Multiplicative (x * y) +-- | mempty :: Multiplicative _ == Multiplicative one +-- | ``` +newtype Multiplicative a = Multiplicative a + +derive newtype instance eqMultiplicative :: Eq a => Eq (Multiplicative a) +derive instance eq1Multiplicative :: Eq1 Multiplicative + +derive newtype instance ordMultiplicative :: Ord a => Ord (Multiplicative a) +derive instance ord1Multiplicative :: Ord1 Multiplicative + +derive newtype instance boundedMultiplicative :: Bounded a => Bounded (Multiplicative a) + +instance showMultiplicative :: Show a => Show (Multiplicative a) where + show (Multiplicative a) = "(Multiplicative " <> show a <> ")" + +derive instance functorMultiplicative :: Functor Multiplicative + +instance applyMultiplicative :: Apply Multiplicative where + apply (Multiplicative f) (Multiplicative x) = Multiplicative (f x) + +instance applicativeMultiplicative :: Applicative Multiplicative where + pure = Multiplicative + +instance bindMultiplicative :: Bind Multiplicative where + bind (Multiplicative x) f = f x + +instance monadMultiplicative :: Monad Multiplicative + +instance semigroupMultiplicative :: Semiring a => Semigroup (Multiplicative a) where + append (Multiplicative a) (Multiplicative b) = Multiplicative (a * b) + +instance monoidMultiplicative :: Semiring a => Monoid (Multiplicative a) where + mempty = Multiplicative one diff --git a/src/Prelude.purs b/src/Prelude.purs index af9dd21e..f4e9f2a7 100644 --- a/src/Prelude.purs +++ b/src/Prelude.purs @@ -16,6 +16,7 @@ module Prelude , module Data.Function , module Data.Functor , module Data.HeytingAlgebra + , module Data.Monoid , module Data.NaturalTransformation , module Data.Ord , module Data.Ordering @@ -45,6 +46,7 @@ import Data.Field (class Field) import Data.Function (const, flip, ($), (#)) import Data.Functor (class Functor, flap, map, void, ($>), (<#>), (<$), (<$>), (<@>)) import Data.HeytingAlgebra (class HeytingAlgebra, conj, disj, not, (&&), (||)) +import Data.Monoid (class Monoid, mempty) import Data.NaturalTransformation (type (~>)) import Data.Ord (class Ord, compare, (<), (<=), (>), (>=), comparing, min, max, clamp, between) import Data.Ordering (Ordering(..)) From 6b142a55f74b24e9a2dec25c573722134275371f Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Fri, 13 Apr 2018 12:41:17 +0100 Subject: [PATCH 02/23] Add `Semigroup`s From @matthewleon --- src/Data/Semigroup/First.purs | 40 +++++++++++++++++++++++++++++++++++ src/Data/Semigroup/Last.purs | 40 +++++++++++++++++++++++++++++++++++ src/Data/Semigroup/Max.purs | 40 +++++++++++++++++++++++++++++++++++ src/Data/Semigroup/Min.purs | 40 +++++++++++++++++++++++++++++++++++ 4 files changed, 160 insertions(+) create mode 100644 src/Data/Semigroup/First.purs create mode 100644 src/Data/Semigroup/Last.purs create mode 100644 src/Data/Semigroup/Max.purs create mode 100644 src/Data/Semigroup/Min.purs diff --git a/src/Data/Semigroup/First.purs b/src/Data/Semigroup/First.purs new file mode 100644 index 00000000..18681bb0 --- /dev/null +++ b/src/Data/Semigroup/First.purs @@ -0,0 +1,40 @@ +module Data.Semigroup.First where + +import Prelude + +import Data.Eq (class Eq1) +import Data.Ord (class Ord1) + +-- | Semigroup where `append` always takes the first option. +-- | +-- | ``` purescript +-- | First x <> First y == First x +-- | ``` +newtype First a = First a + +derive newtype instance eqFirst :: Eq a => Eq (First a) +derive instance eq1First :: Eq1 First + +derive newtype instance ordFirst :: Ord a => Ord (First a) +derive instance ord1First :: Ord1 First + +derive newtype instance boundedFirst :: Bounded a => Bounded (First a) + +instance showFirst :: Show a => Show (First a) where + show (First a) = "(First " <> show a <> ")" + +derive instance functorFirst :: Functor First + +instance applyFirst :: Apply First where + apply (First f) (First x) = First (f x) + +instance applicativeFirst :: Applicative First where + pure = First + +instance bindFirst :: Bind First where + bind (First x) f = f x + +instance monadFirst :: Monad First + +instance semigroupFirst :: Semigroup (First a) where + append x _ = x diff --git a/src/Data/Semigroup/Last.purs b/src/Data/Semigroup/Last.purs new file mode 100644 index 00000000..1dbd3244 --- /dev/null +++ b/src/Data/Semigroup/Last.purs @@ -0,0 +1,40 @@ +module Data.Semigroup.Last where + +import Prelude + +import Data.Eq (class Eq1) +import Data.Ord (class Ord1) + +-- | Semigroup where `append` always takes the second option. +-- | +-- | ``` purescript +-- | Last x <> Last y == Last x +-- | ``` +newtype Last a = Last a + +derive newtype instance eqLast :: Eq a => Eq (Last a) +derive instance eq1Last :: Eq1 Last + +derive newtype instance ordLast :: Ord a => Ord (Last a) +derive instance ord1Last :: Ord1 Last + +derive newtype instance boundedLast :: Bounded a => Bounded (Last a) + +instance showLast :: Show a => Show (Last a) where + show (Last a) = "(Last " <> show a <> ")" + +derive instance functorLast :: Functor Last + +instance applyLast :: Apply Last where + apply (Last f) (Last x) = Last (f x) + +instance applicativeLast :: Applicative Last where + pure = Last + +instance bindLast :: Bind Last where + bind (Last x) f = f x + +instance monadLast :: Monad Last + +instance semigroupLast :: Semigroup (Last a) where + append _ x = x diff --git a/src/Data/Semigroup/Max.purs b/src/Data/Semigroup/Max.purs new file mode 100644 index 00000000..75665861 --- /dev/null +++ b/src/Data/Semigroup/Max.purs @@ -0,0 +1,40 @@ +module Data.Semigroup.Max where + +import Prelude + +import Data.Eq (class Eq1) +import Data.Ord (class Ord1) + +-- | Semigroup where `append` always takes the greater value. +-- | +-- | ``` purescript +-- | Max x <> Max y == Max (if x >= y then x else y) +-- | ``` +newtype Max a = Max a + +derive newtype instance eqMax :: Eq a => Eq (Max a) +derive instance eq1Max :: Eq1 Max + +derive newtype instance ordMax :: Ord a => Ord (Max a) +derive instance ord1Max :: Ord1 Max + +derive newtype instance boundedMax :: Bounded a => Bounded (Max a) + +instance showMax :: Show a => Show (Max a) where + show (Max a) = "(Max " <> show a <> ")" + +derive instance functorMax :: Functor Max + +instance applyMax :: Apply Max where + apply (Max f) (Max x) = Max (f x) + +instance applicativeMax :: Applicative Max where + pure = Max + +instance bindMax :: Bind Max where + bind (Max x) f = f x + +instance monadMax :: Monad Max + +instance semigroupMax :: Ord a => Semigroup (Max a) where + append (Max x) (Max y) = Max (max x y) diff --git a/src/Data/Semigroup/Min.purs b/src/Data/Semigroup/Min.purs new file mode 100644 index 00000000..127f0b03 --- /dev/null +++ b/src/Data/Semigroup/Min.purs @@ -0,0 +1,40 @@ +module Data.Semigroup.Min where + +import Prelude + +import Data.Eq (class Eq1) +import Data.Ord (class Ord1) + +-- | Semigroup where `append` always takes the lesser value. +-- | +-- | ``` purescript +-- | Min x <> Min y == Min (if x <= y then x else y) +-- | ``` +newtype Min a = Min a + +derive newtype instance eqMin :: Eq a => Eq (Min a) +derive instance eq1Min :: Eq1 Min + +derive newtype instance ordMin :: Ord a => Ord (Min a) +derive instance ord1Min :: Ord1 Min + +derive newtype instance boundedMin :: Bounded a => Bounded (Min a) + +instance showMin :: Show a => Show (Min a) where + show (Min a) = "(Min " <> show a <> ")" + +derive instance functorMin :: Functor Min + +instance applyMin :: Apply Min where + apply (Min f) (Min x) = Min (f x) + +instance applicativeMin :: Applicative Min where + pure = Min + +instance bindMin :: Bind Min where + bind (Min x) f = f x + +instance monadMin :: Monad Min + +instance semigroupMin :: Ord a => Semigroup (Min a) where + append (Min x) (Min y) = Min (min x y) From 2f11d6fe49ff85fd78f351ac99610f6f1693e33a Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Fri, 13 Apr 2018 22:20:30 +0100 Subject: [PATCH 03/23] Rename `id` to `identity` --- src/Control/Applicative.purs | 2 +- src/Control/Apply.purs | 4 ++-- src/Control/Bind.purs | 4 ++-- src/Control/Category.purs | 8 ++++---- src/Control/Semigroupoid.purs | 2 +- src/Data/Function.purs | 2 +- src/Data/Functor.purs | 2 +- src/Data/Monoid/Endo.purs | 4 ++-- src/Prelude.purs | 2 +- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Control/Applicative.purs b/src/Control/Applicative.purs index d9d24fc7..d4c8489c 100644 --- a/src/Control/Applicative.purs +++ b/src/Control/Applicative.purs @@ -25,7 +25,7 @@ import Data.Unit (Unit, unit) -- | Instances must satisfy the following laws in addition to the `Apply` -- | laws: -- | --- | - Identity: `(pure id) <*> v = v` +-- | - Identity: `(pure identity) <*> v = v` -- | - Composition: `pure (<<<) <*> f <*> g <*> h = f <*> (g <*> h)` -- | - Homomorphism: `(pure f) <*> (pure x) = pure (f x)` -- | - Interchange: `u <*> (pure y) = (pure (_ $ y)) <*> u` diff --git a/src/Control/Apply.purs b/src/Control/Apply.purs index e6e7e1d8..2bbe40f0 100644 --- a/src/Control/Apply.purs +++ b/src/Control/Apply.purs @@ -8,7 +8,7 @@ module Control.Apply import Data.Functor (class Functor, map, void, ($>), (<#>), (<$), (<$>)) import Data.Function (const) -import Control.Category (id) +import Control.Category (identity) -- | The `Apply` class provides the `(<*>)` which is used to apply a function -- | to an argument under a type constructor. @@ -53,7 +53,7 @@ infixl 4 applyFirst as <* -- | Combine two effectful actions, keeping only the result of the second. applySecond :: forall a b f. Apply f => f a -> f b -> f b -applySecond a b = const id <$> a <*> b +applySecond a b = const identity <$> a <*> b infixl 4 applySecond as *> diff --git a/src/Control/Bind.purs b/src/Control/Bind.purs index 43c45c28..4257665f 100644 --- a/src/Control/Bind.purs +++ b/src/Control/Bind.purs @@ -13,7 +13,7 @@ module Control.Bind import Control.Applicative (class Applicative, liftA1, pure, unless, when) import Control.Apply (class Apply, apply, (*>), (<*), (<*>)) -import Control.Category (id) +import Control.Category (identity) import Data.Function (flip) import Data.Functor (class Functor, map, void, ($>), (<#>), (<$), (<$>)) @@ -81,7 +81,7 @@ instance discardUnit :: Discard Unit where -- | Collapse two applications of a monadic type constructor into one. join :: forall a m. Bind m => m (m a) -> m a -join m = m >>= id +join m = m >>= identity -- | Forwards Kleisli composition. -- | diff --git a/src/Control/Category.purs b/src/Control/Category.purs index 9fa17b23..77cb7bbc 100644 --- a/src/Control/Category.purs +++ b/src/Control/Category.purs @@ -1,5 +1,5 @@ module Control.Category - ( class Category, id + ( class Category, identity , module Control.Semigroupoid ) where @@ -12,9 +12,9 @@ import Control.Semigroupoid (class Semigroupoid, compose, (<<<), (>>>)) -- | Instances must satisfy the following law in addition to the -- | `Semigroupoid` law: -- | --- | - Identity: `id <<< p = p <<< id = p` +-- | - Identity: `identity <<< p = p <<< identity = p` class Semigroupoid a <= Category a where - id :: forall t. a t t + identity :: forall t. a t t instance categoryFn :: Category (->) where - id x = x + identity x = x diff --git a/src/Control/Semigroupoid.purs b/src/Control/Semigroupoid.purs index 729e1bbe..9c20b919 100644 --- a/src/Control/Semigroupoid.purs +++ b/src/Control/Semigroupoid.purs @@ -1,7 +1,7 @@ module Control.Semigroupoid where -- | A `Semigroupoid` is similar to a [`Category`](#category) but does not --- | require an identity element `id`, just composable morphisms. +-- | require an identity element `identity`, just composable morphisms. -- | -- | `Semigroupoid`s must satisfy the following law: -- | diff --git a/src/Data/Function.purs b/src/Data/Function.purs index e4875d97..e6acce20 100644 --- a/src/Data/Function.purs +++ b/src/Data/Function.purs @@ -8,7 +8,7 @@ module Data.Function , module Control.Category ) where -import Control.Category (id, compose, (<<<), (>>>)) +import Control.Category (identity, compose, (<<<), (>>>)) import Data.Boolean (otherwise) import Data.Ord ((<=)) import Data.Ring ((-)) diff --git a/src/Data/Functor.purs b/src/Data/Functor.purs index 07b99497..e2754832 100644 --- a/src/Data/Functor.purs +++ b/src/Data/Functor.purs @@ -19,7 +19,7 @@ import Data.Unit (Unit, unit) -- | -- | Instances must satisfy the following laws: -- | --- | - Identity: `map id = id` +-- | - Identity: `map identity = identity` -- | - Composition: `map (f <<< g) = map f <<< map g` class Functor f where map :: forall a b. (a -> b) -> f a -> f b diff --git a/src/Data/Monoid/Endo.purs b/src/Data/Monoid/Endo.purs index 19678c3e..04df3fb7 100644 --- a/src/Data/Monoid/Endo.purs +++ b/src/Data/Monoid/Endo.purs @@ -9,7 +9,7 @@ import Prelude -- | -- | ``` purescript -- | Endo f <> Endo g == Endo (f <<< g) --- | mempty :: Endo _ == Endo id +-- | mempty :: Endo _ == Endo identity -- | ``` newtype Endo c a = Endo (c a a) @@ -26,4 +26,4 @@ instance semigroupEndo :: Semigroupoid c => Semigroup (Endo c a) where append (Endo a) (Endo b) = Endo (a <<< b) instance monoidEndo :: Category c => Monoid (Endo c a) where - mempty = Endo id + mempty = Endo identity diff --git a/src/Prelude.purs b/src/Prelude.purs index f4e9f2a7..3a1cd439 100644 --- a/src/Prelude.purs +++ b/src/Prelude.purs @@ -31,7 +31,7 @@ module Prelude import Control.Applicative (class Applicative, pure, liftA1, unless, when) import Control.Apply (class Apply, apply, (*>), (<*), (<*>)) import Control.Bind (class Bind, bind, class Discard, discard, ifM, join, (<=<), (=<<), (>=>), (>>=)) -import Control.Category (class Category, id) +import Control.Category (class Category, identity) import Control.Monad (class Monad, ap, liftM1, unlessM, whenM) import Control.Semigroupoid (class Semigroupoid, compose, (<<<), (>>>)) From 51530cb41c84d40f57ce7ddd717e6c2efdd94db1 Mon Sep 17 00:00:00 2001 From: Harry Garrood Date: Sun, 15 Apr 2018 17:48:52 +0100 Subject: [PATCH 04/23] Switch to Euclidean division for Int, resolves #161 Also provide `quot` and `rem`, like Haskell does, for users who do want truncating division - the one which matches what JS does. I've temporarily exported `intDiv` and `intMod` so that I can use those in the tests and the compiler won't 'inline' different definitions of them; we'll want to modify the compiler to change this before merging. --- src/Data/EuclideanRing.js | 15 ++++++++++ src/Data/EuclideanRing.purs | 58 +++++++++++++++++++++++++++++++++++++ test/Test/Main.purs | 54 ++++++++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+) diff --git a/src/Data/EuclideanRing.js b/src/Data/EuclideanRing.js index 362b119e..90224b5b 100644 --- a/src/Data/EuclideanRing.js +++ b/src/Data/EuclideanRing.js @@ -4,7 +4,15 @@ exports.intDegree = function (x) { return Math.min(Math.abs(x), 2147483647); }; +// See the Euclidean definition in +// https://en.m.wikipedia.org/wiki/Modulo_operation. exports.intDiv = function (x) { + return function (y) { + return Math.sign(y) * Math.floor(x / Math.abs(y)); + }; +}; + +exports.quot = function (x) { return function (y) { /* jshint bitwise: false */ return x / y | 0; @@ -12,6 +20,13 @@ exports.intDiv = function (x) { }; exports.intMod = function (x) { + return function (y) { + var yy = Math.abs(y) + return ((x % yy) + yy) % yy; + }; +}; + +exports.rem = function (x) { return function (y) { return x % y; }; diff --git a/src/Data/EuclideanRing.purs b/src/Data/EuclideanRing.purs index 6d02ec1f..683c420f 100644 --- a/src/Data/EuclideanRing.purs +++ b/src/Data/EuclideanRing.purs @@ -2,9 +2,13 @@ module Data.EuclideanRing ( class EuclideanRing, degree, div, mod, (/) , gcd , lcm + , quot + , rem , module Data.CommutativeRing , module Data.Ring , module Data.Semiring + , intDiv + , intMod ) where import Data.BooleanAlgebra ((||)) @@ -41,6 +45,25 @@ import Data.Semiring (class Semiring, add, mul, one, zero, (*), (+)) -- | for `degree` is simply `const 1`. In fact, unless there's a specific -- | reason not to, `Field` types should normally use this definition of -- | `degree`. +-- | +-- | The `EuclideanRing Int` instance is one of the most commonly used +-- | `EuclideanRing` instances and deserves a little more discussion. In +-- | particular, there are a few different sensible law-abiding implementations +-- | to choose from, with slightly different behaviour in the presence of +-- | negative dividends or divisors. The most common definitions are "truncating" +-- | division, where the result of `a / b` is rounded towards 0, and "Knuthian" +-- | or "flooring" division, where the result of `a / b` is rounded towards +-- | negative infinity. A slightly less common, but arguably more useful, option +-- | is "Euclidean" division, which is defined so as to ensure that ``a `mod` b`` +-- | is always nonnegative. With Euclidean division, `a / b` rounds towards +-- | negative infinity if the divisor is positive, and towards positive infinity +-- | if the divisor is negative. Note that all three definitions are identical if +-- | we restrict our attention to nonnegative dividends and divisors. + +-- | In versions 1.x, 2.x, and 3.x of the Prelude, the `EuclideanRing Int` +-- | instance used truncating division. As of 4.x, the `EuclideanRing Int` +-- | instance uses Euclidean division. Additional functions `quot` and `rem` are +-- | supplied if truncating division is desired. class CommutativeRing a <= EuclideanRing a where degree :: a -> Int div :: a -> a -> a @@ -77,3 +100,38 @@ lcm a b = if a == zero || b == zero then zero else a * b / gcd a b + +-- | The `quot` function provides _truncating_ integer division (see the +-- | documentation for the `EuclideanRing` class). It is identical to `div` in +-- | the `EuclideanRing Int` instance if the dividend is positive, but will be +-- | slightly different if the dividend is negative. For example: +-- | +-- | ```purescript +-- | div 2 3 == 0 +-- | quot 2 3 == 0 +-- | +-- | div (-2) 3 == (-1) +-- | quot (-2) 3 == 0 +-- | +-- | div 2 (-3) == 0 +-- | quot 2 (-3) == 0 +-- | ``` +foreign import quot :: Int -> Int -> Int + +-- | The `rem` function provides the remainder after _truncating_ integer +-- | division (see the documentation for the `EuclideanRing` class). It is +-- | identical to `mod` in the `EuclideanRing Int` instance if the dividend is +-- | positive, but will be slightly different if the dividend is negative. For +-- | example: +-- | +-- | ```purescript +-- | mod 2 3 == 2 +-- | rem 2 3 == 2 +-- | +-- | mod (-2) 3 == 1 +-- | rem (-2) 3 == (-2) +-- | +-- | mod 2 (-3) == 2 +-- | rem 2 (-3) == 2 +-- | ``` +foreign import rem :: Int -> Int -> Int diff --git a/test/Test/Main.purs b/test/Test/Main.purs index 983f5b47..177d85f3 100644 --- a/test/Test/Main.purs +++ b/test/Test/Main.purs @@ -1,6 +1,8 @@ module Test.Main where import Prelude +import Data.EuclideanRing (intDiv, intMod) +import Data.Ord (abs) type AlmostEff = Unit -> Unit @@ -9,6 +11,8 @@ main = do testNumberShow show testOrderings testOrdUtils + testIntDivMod + testIntQuotRem testIntDegree foreign import testNumberShow :: (Number -> String) -> AlmostEff @@ -82,6 +86,56 @@ testOrdUtils = do assert "5 should be between 0 and 10" $ between 0 10 5 == true assert "15 should not be between 0 10" $ between 0 10 15 == false +testIntDivMod :: AlmostEff +testIntDivMod = do + -- Check when dividend goes into divisor exactly + go 8 2 + go (-8) 2 + go 8 (-2) + go (-8) (-2) + + -- Check when dividend does not go into divisor exactly + go 2 3 + go (-2) 3 + go 2 (-3) + go (-2) (-3) + + where + go a b = + let + q = intDiv a b + r = intMod a b + msg = show a <> " / " <> show b <> ": " + in do + assert (msg <> "Quotient/remainder law") $ + q * b + r == a + assert (msg <> "Remainder should be between 0 and `abs b`, got: " <> show r) $ + 0 <= r && r < abs b + +testIntQuotRem :: AlmostEff +testIntQuotRem = do + -- Check when dividend goes into divisor exactly + go 8 2 + go (-8) 2 + go 8 (-2) + go (-8) (-2) + + -- Check when dividend does not go into divisor exactly + go 2 3 + go (-2) 3 + go 2 (-3) + go (-2) (-3) + + where + go a b = + let + q = quot a b + r = rem a b + msg = show a <> " / " <> show b <> ": " + in do + assert (msg <> "Quotient/remainder law") $ + q * b + r == a + testIntDegree :: AlmostEff testIntDegree = do let bot = bottom :: Int From d138345180e0452facf1e95e0893e6526fda6e71 Mon Sep 17 00:00:00 2001 From: Harry Garrood Date: Sun, 15 Apr 2018 17:54:48 +0100 Subject: [PATCH 05/23] Fix missing semicolon --- src/Data/EuclideanRing.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/EuclideanRing.js b/src/Data/EuclideanRing.js index 90224b5b..16d3cfa2 100644 --- a/src/Data/EuclideanRing.js +++ b/src/Data/EuclideanRing.js @@ -21,7 +21,7 @@ exports.quot = function (x) { exports.intMod = function (x) { return function (y) { - var yy = Math.abs(y) + var yy = Math.abs(y); return ((x % yy) + yy) % yy; }; }; From a27f06fa5a8be386a74cf2c4c7baaa900765ba8b Mon Sep 17 00:00:00 2001 From: Harry Garrood Date: Sun, 15 Apr 2018 17:57:37 +0100 Subject: [PATCH 06/23] Export quot and rem --- src/Prelude.purs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Prelude.purs b/src/Prelude.purs index af9dd21e..bb5dadbc 100644 --- a/src/Prelude.purs +++ b/src/Prelude.purs @@ -40,7 +40,7 @@ import Data.Bounded (class Bounded, bottom, top) import Data.CommutativeRing (class CommutativeRing) import Data.DivisionRing (class DivisionRing, recip) import Data.Eq (class Eq, eq, notEq, (/=), (==)) -import Data.EuclideanRing (class EuclideanRing, degree, div, mod, (/), gcd, lcm) +import Data.EuclideanRing (class EuclideanRing, degree, div, mod, quot, rem, (/), gcd, lcm) import Data.Field (class Field) import Data.Function (const, flip, ($), (#)) import Data.Functor (class Functor, flap, map, void, ($>), (<#>), (<$), (<$>), (<@>)) From 7e720c188f63728b6298359efde34d645334867d Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Sun, 15 Apr 2018 20:42:23 +0100 Subject: [PATCH 07/23] Remove Max/Min that already exist in -orders --- src/Data/Semigroup/Max.purs | 40 ------------------------------------- src/Data/Semigroup/Min.purs | 40 ------------------------------------- 2 files changed, 80 deletions(-) delete mode 100644 src/Data/Semigroup/Max.purs delete mode 100644 src/Data/Semigroup/Min.purs diff --git a/src/Data/Semigroup/Max.purs b/src/Data/Semigroup/Max.purs deleted file mode 100644 index 75665861..00000000 --- a/src/Data/Semigroup/Max.purs +++ /dev/null @@ -1,40 +0,0 @@ -module Data.Semigroup.Max where - -import Prelude - -import Data.Eq (class Eq1) -import Data.Ord (class Ord1) - --- | Semigroup where `append` always takes the greater value. --- | --- | ``` purescript --- | Max x <> Max y == Max (if x >= y then x else y) --- | ``` -newtype Max a = Max a - -derive newtype instance eqMax :: Eq a => Eq (Max a) -derive instance eq1Max :: Eq1 Max - -derive newtype instance ordMax :: Ord a => Ord (Max a) -derive instance ord1Max :: Ord1 Max - -derive newtype instance boundedMax :: Bounded a => Bounded (Max a) - -instance showMax :: Show a => Show (Max a) where - show (Max a) = "(Max " <> show a <> ")" - -derive instance functorMax :: Functor Max - -instance applyMax :: Apply Max where - apply (Max f) (Max x) = Max (f x) - -instance applicativeMax :: Applicative Max where - pure = Max - -instance bindMax :: Bind Max where - bind (Max x) f = f x - -instance monadMax :: Monad Max - -instance semigroupMax :: Ord a => Semigroup (Max a) where - append (Max x) (Max y) = Max (max x y) diff --git a/src/Data/Semigroup/Min.purs b/src/Data/Semigroup/Min.purs deleted file mode 100644 index 127f0b03..00000000 --- a/src/Data/Semigroup/Min.purs +++ /dev/null @@ -1,40 +0,0 @@ -module Data.Semigroup.Min where - -import Prelude - -import Data.Eq (class Eq1) -import Data.Ord (class Ord1) - --- | Semigroup where `append` always takes the lesser value. --- | --- | ``` purescript --- | Min x <> Min y == Min (if x <= y then x else y) --- | ``` -newtype Min a = Min a - -derive newtype instance eqMin :: Eq a => Eq (Min a) -derive instance eq1Min :: Eq1 Min - -derive newtype instance ordMin :: Ord a => Ord (Min a) -derive instance ord1Min :: Ord1 Min - -derive newtype instance boundedMin :: Bounded a => Bounded (Min a) - -instance showMin :: Show a => Show (Min a) where - show (Min a) = "(Min " <> show a <> ")" - -derive instance functorMin :: Functor Min - -instance applyMin :: Apply Min where - apply (Min f) (Min x) = Min (f x) - -instance applicativeMin :: Applicative Min where - pure = Min - -instance bindMin :: Bind Min where - bind (Min x) f = f x - -instance monadMin :: Monad Min - -instance semigroupMin :: Ord a => Semigroup (Min a) where - append (Min x) (Min y) = Min (min x y) From 8980d5e8f5174ab484685a961bfc9d01c29ab8f9 Mon Sep 17 00:00:00 2001 From: Harry Garrood Date: Sun, 15 Apr 2018 21:22:27 +0100 Subject: [PATCH 08/23] Avoid ES2015 Math.sign --- src/Data/EuclideanRing.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/EuclideanRing.js b/src/Data/EuclideanRing.js index 16d3cfa2..e7b8ad94 100644 --- a/src/Data/EuclideanRing.js +++ b/src/Data/EuclideanRing.js @@ -8,7 +8,7 @@ exports.intDegree = function (x) { // https://en.m.wikipedia.org/wiki/Modulo_operation. exports.intDiv = function (x) { return function (y) { - return Math.sign(y) * Math.floor(x / Math.abs(y)); + return y > 0 ? Math.floor(x / y) : -Math.floor(x / -y); }; }; From 05a85406fc124ca716b270780d46b7e217856848 Mon Sep 17 00:00:00 2001 From: Harry Garrood Date: Sun, 15 Apr 2018 21:23:18 +0100 Subject: [PATCH 09/23] Fix doc-comments --- src/Data/EuclideanRing.purs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/EuclideanRing.purs b/src/Data/EuclideanRing.purs index 683c420f..6a0edf2f 100644 --- a/src/Data/EuclideanRing.purs +++ b/src/Data/EuclideanRing.purs @@ -59,7 +59,7 @@ import Data.Semiring (class Semiring, add, mul, one, zero, (*), (+)) -- | negative infinity if the divisor is positive, and towards positive infinity -- | if the divisor is negative. Note that all three definitions are identical if -- | we restrict our attention to nonnegative dividends and divisors. - +-- | -- | In versions 1.x, 2.x, and 3.x of the Prelude, the `EuclideanRing Int` -- | instance used truncating division. As of 4.x, the `EuclideanRing Int` -- | instance uses Euclidean division. Additional functions `quot` and `rem` are From 94ce7efbd2d2436033c125b3b4a6b7d415a6b278 Mon Sep 17 00:00:00 2001 From: Liam Goodacre Date: Wed, 25 Apr 2018 20:54:23 +0100 Subject: [PATCH 10/23] Move purescript-symbols in --- src/Data/Symbol.js | 8 ++++++++ src/Data/Symbol.purs | 24 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 src/Data/Symbol.js create mode 100644 src/Data/Symbol.purs diff --git a/src/Data/Symbol.js b/src/Data/Symbol.js new file mode 100644 index 00000000..b4b6e28f --- /dev/null +++ b/src/Data/Symbol.js @@ -0,0 +1,8 @@ +"use strict"; + +// module Data.Symbol + +exports.unsafeCoerce = function (arg) { + return arg; +}; + diff --git a/src/Data/Symbol.purs b/src/Data/Symbol.purs new file mode 100644 index 00000000..bda2cd3e --- /dev/null +++ b/src/Data/Symbol.purs @@ -0,0 +1,24 @@ +module Data.Symbol + ( class IsSymbol + , reflectSymbol + , reifySymbol + , SProxy(..) + ) where + +-- | A value-level proxy for a type-level symbol. +data SProxy (sym :: Symbol) = SProxy + +-- | A class for known symbols +class IsSymbol (sym :: Symbol) where + reflectSymbol :: SProxy sym -> String + +-- local definition for use in `reifySymbol` +foreign import unsafeCoerce :: forall a b. a -> b + +reifySymbol :: forall r. String -> (forall sym. IsSymbol sym => SProxy sym -> r) -> r +reifySymbol s f = coerce f { reflectSymbol: \_ -> s } SProxy where + coerce + :: (forall sym1. IsSymbol sym1 => SProxy sym1 -> r) + -> { reflectSymbol :: SProxy "" -> String } -> SProxy "" -> r + coerce = unsafeCoerce + From 347b9a62762ef93310b767236e65cf8132cdf9ba Mon Sep 17 00:00:00 2001 From: Tom Harding Date: Sun, 29 Apr 2018 14:47:04 +0100 Subject: [PATCH 11/23] Add `Eq`, `Ord` and `Semigroup` constraints for `Record` This commit is to be followed by several others, slowly building up the set of record instances in the prelude by way of RowToList. --- src/Data/Eq.purs | 35 ++++++++++++++++++++++++++++++++++ src/Data/Internal/Record.js | 14 ++++++++++++++ src/Data/Internal/Record.purs | 14 ++++++++++++++ src/Data/Ord.purs | 36 ++++++++++++++++++++++++++++++++++- src/Data/RowList.purs | 7 +++++++ src/Data/Semigroup.purs | 32 +++++++++++++++++++++++++++++++ 6 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 src/Data/Internal/Record.js create mode 100644 src/Data/Internal/Record.purs create mode 100644 src/Data/RowList.purs diff --git a/src/Data/Eq.purs b/src/Data/Eq.purs index 0d589df8..30673e55 100644 --- a/src/Data/Eq.purs +++ b/src/Data/Eq.purs @@ -1,10 +1,19 @@ module Data.Eq ( class Eq, eq, (==), notEq, (/=) , class Eq1, eq1, notEq1 + + , class EqRecord + , eqRecordImpl ) where +import Data.HeytingAlgebra ((&&)) +import Data.Internal.Record (unsafeGet) +import Data.RowList (RLProxy(..)) +import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol) import Data.Unit (Unit) import Data.Void (Void) +import Prim.Row as Row +import Prim.RowList as RL -- | The `Eq` type class represents types which support decidable equality. -- | @@ -66,3 +75,29 @@ instance eq1Array :: Eq1 Array where notEq1 :: forall f a. Eq1 f => Eq a => f a -> f a -> Boolean notEq1 x y = (x `eq1` y) == false + +class EqRecord rowlist row focus | rowlist -> focus where + eqRecordImpl :: RLProxy rowlist -> Record row -> Record row -> Boolean + +instance eqRecordNil :: EqRecord RL.Nil row focus where + eqRecordImpl _ _ _ = true + +instance eqRecordCons + :: ( EqRecord rowlistTail row subfocus + , Row.Cons key focus rowTail row + , IsSymbol key + , Eq focus + ) + => EqRecord (RL.Cons key a rowlistTail) row a where + eqRecordImpl _ ra rb + = unsafeGet' key ra == unsafeGet key rb + && eqRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb + where key = reflectSymbol (SProxy :: SProxy key) + unsafeGet' = unsafeGet :: String -> Record row -> focus + +instance eqRecord + :: ( RL.RowToList row list + , EqRecord list row focus + ) + => Eq (Record row) where + eq = eqRecordImpl (RLProxy :: RLProxy list) diff --git a/src/Data/Internal/Record.js b/src/Data/Internal/Record.js new file mode 100644 index 00000000..c4a827d4 --- /dev/null +++ b/src/Data/Internal/Record.js @@ -0,0 +1,14 @@ +exports.unsafeGet = function (key) { + return function (xs) { + return xs[key]; + }; +}; + +exports.unsafeInsert = function (key) { + return function (value) { + return function (xs) { + xs[key] = value; + return xs; + }; + }; +}; diff --git a/src/Data/Internal/Record.purs b/src/Data/Internal/Record.purs new file mode 100644 index 00000000..09790590 --- /dev/null +++ b/src/Data/Internal/Record.purs @@ -0,0 +1,14 @@ +module Data.Internal.Record where + +-- | *Really* unsafely get a value from a record. You really shouldn't be using +-- | this function unless you know what you're doing. +foreign import unsafeGet :: forall a rs. String -> Record rs -> a + +-- | *Really* unsafely insert a value into a record. Again, you really +-- | shouldn't use this function. +foreign import unsafeInsert + :: forall a ra rb + . String + -> a + -> Record ra + -> Record rb diff --git a/src/Data/Ord.purs b/src/Data/Ord.purs index a3bec35f..10c53eeb 100644 --- a/src/Data/Ord.purs +++ b/src/Data/Ord.purs @@ -14,12 +14,17 @@ module Data.Ord , module Data.Ordering ) where -import Data.Eq (class Eq, class Eq1) +import Data.Eq (class Eq, class Eq1, class EqRecord, (/=)) +import Data.Internal.Record (unsafeGet) import Data.Ord.Unsafe (unsafeCompare) import Data.Ordering (Ordering(..)) import Data.Ring (class Ring, zero, one, negate) +import Data.RowList (RLProxy(..)) +import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol) import Data.Unit (Unit) import Data.Void (Void) +import Prim.Row as Row +import Prim.RowList as RL -- | The `Ord` type class represents types which support comparisons with a -- | _total order_. @@ -168,3 +173,32 @@ class Eq1 f <= Ord1 f where instance ord1Array :: Ord1 Array where compare1 = compare + +class EqRecord rowlist row focus <= OrdRecord rowlist row focus | rowlist -> focus where + compareImpl :: RLProxy rowlist -> Record row -> Record row -> Ordering + +instance ordRecordNil :: OrdRecord RL.Nil row focus where + compareImpl _ _ _ = EQ + +instance ordRecordCons + :: ( OrdRecord rowlistTail row subfocus + , Row.Cons key focus rowTail row + , IsSymbol key + , Ord focus + ) + => OrdRecord (RL.Cons key focus rowlistTail) row focus where + compareImpl _ ra rb + = if left /= EQ + then left + else compareImpl (RLProxy :: RLProxy rowlistTail) ra rb + where + key = reflectSymbol (SProxy :: SProxy key) + unsafeGet' = unsafeGet :: String -> Record row -> focus + left = unsafeGet' key ra `compare` unsafeGet' key rb + +instance ordRecord + :: ( RL.RowToList row list + , OrdRecord list row focus + ) + => Ord (Record row) where + compare = compareImpl (RLProxy :: RLProxy list) diff --git a/src/Data/RowList.purs b/src/Data/RowList.purs new file mode 100644 index 00000000..fc27768f --- /dev/null +++ b/src/Data/RowList.purs @@ -0,0 +1,7 @@ +module Data.RowList where + +import Prim.RowList (kind RowList) + +-- | A proxy to carry information about a rowlist. +data RLProxy (rowlist :: RowList) + = RLProxy diff --git a/src/Data/Semigroup.purs b/src/Data/Semigroup.purs index ec7ac207..8a9858a1 100644 --- a/src/Data/Semigroup.purs +++ b/src/Data/Semigroup.purs @@ -1,7 +1,12 @@ module Data.Semigroup (class Semigroup, append, (<>)) where +import Data.Internal.Record (unsafeGet, unsafeInsert) +import Data.RowList (RLProxy(..)) +import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol) import Data.Unit (Unit, unit) import Data.Void (Void, absurd) +import Prim.Row as Row +import Prim.RowList as RL -- | The `Semigroup` type class identifies an associative operation on a type. -- | @@ -33,3 +38,30 @@ instance semigroupArray :: Semigroup (Array a) where foreign import concatString :: String -> String -> String foreign import concatArray :: forall a. Array a -> Array a -> Array a + +class SemigroupRecord rowlist row subrow focus | rowlist -> subrow focus where + semigroupRecordImpl :: RLProxy rowlist -> Record row -> Record row -> Record subrow + +instance semigroupRecordNil :: SemigroupRecord RL.Nil row () focus where + semigroupRecordImpl _ _ _ = {} + +instance semigroupRecordCons + :: ( IsSymbol key + , Row.Cons key focus subrowTail subrow + , SemigroupRecord rowlistTail row subrowTail subfocus + , Semigroup focus + ) + => SemigroupRecord (RL.Cons key focus rowlistTail) row subrow focus where + semigroupRecordImpl _ ra rb + = unsafeInsert key + (unsafeGet' key ra <> unsafeGet' key rb) + (semigroupRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb) + where key = reflectSymbol (SProxy :: SProxy key) + unsafeGet' = unsafeGet :: String -> Record row -> focus + +instance semigroupRecord + :: ( RL.RowToList row list + , SemigroupRecord list row row focus + ) + => Semigroup (Record row) where + append = semigroupRecordImpl (RLProxy :: RLProxy list) From 4e192c84894825c75ca48bb93ad63770c5e8e32c Mon Sep 17 00:00:00 2001 From: Tom Harding Date: Sun, 29 Apr 2018 22:26:41 +0100 Subject: [PATCH 12/23] Add `Monoid`, `Semiring` and `Ring` for `Record` Adding more and more type classes for records. We're getting somewhere! --- src/Data/Eq.purs | 2 +- src/Data/Monoid.purs | 34 +++++++++++++++- src/Data/Ord.purs | 68 ++++++++++++++++---------------- src/Data/Ring.purs | 35 ++++++++++++++++- src/Data/Semigroup.purs | 9 ++++- src/Data/Semiring.purs | 86 ++++++++++++++++++++++++++++++++++++++++- src/Data/Show.js | 12 ++++++ src/Data/Show.purs | 40 +++++++++++++++++++ src/Type/Data/Row.purs | 4 ++ 9 files changed, 251 insertions(+), 39 deletions(-) create mode 100644 src/Type/Data/Row.purs diff --git a/src/Data/Eq.purs b/src/Data/Eq.purs index 30673e55..6600ff09 100644 --- a/src/Data/Eq.purs +++ b/src/Data/Eq.purs @@ -88,7 +88,7 @@ instance eqRecordCons , IsSymbol key , Eq focus ) - => EqRecord (RL.Cons key a rowlistTail) row a where + => EqRecord (RL.Cons key focus rowlistTail) row focus where eqRecordImpl _ ra rb = unsafeGet' key ra == unsafeGet key rb && eqRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb diff --git a/src/Data/Monoid.purs b/src/Data/Monoid.purs index 19bc1e2f..f2a3955f 100644 --- a/src/Data/Monoid.purs +++ b/src/Data/Monoid.purs @@ -8,10 +8,14 @@ module Data.Monoid import Data.Boolean (otherwise) import Data.Eq ((==)) import Data.EuclideanRing (mod, (/)) +import Data.Internal.Record (unsafeInsert) import Data.Ord ((<=)) import Data.Ordering (Ordering(..)) -import Data.Semigroup (class Semigroup, (<>)) +import Data.RowList (RLProxy(..)) +import Data.Semigroup (class Semigroup, class SemigroupRecord, (<>)) +import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol) import Data.Unit (Unit, unit) +import Prim.RowList as RL -- | A `Monoid` is a `Semigroup` with a value `mempty`, which is both a -- | left and right unit for the associative operation `<>`: @@ -41,6 +45,34 @@ instance monoidString :: Monoid String where instance monoidArray :: Monoid (Array a) where mempty = [] +class MonoidRecord rowlist row focus | rowlist -> row focus where + monoidRecordImpl :: RLProxy rowlist -> Record row + +instance monoidRecordNil :: MonoidRecord RL.Nil () focus where + monoidRecordImpl _ = {} + +instance monoidRecordCons + :: ( IsSymbol key + , Monoid focus + , MonoidRecord rowlistTail row subfocus + ) + => MonoidRecord (RL.Cons key focus rowlistTail) row focus where + monoidRecordImpl _ + = unsafeInsert key + (mempty :: focus) + (monoidRecordImpl (RLProxy :: RLProxy rowlistTail)) + where + key = reflectSymbol (SProxy :: SProxy key) + +instance monoidRecord + :: ( RL.RowToList row list + , SemigroupRecord list row row focus + , MonoidRecord list row focus + ) + => Monoid (Record row) where + mempty = monoidRecordImpl (RLProxy :: RLProxy list) + + -- | Append a value to itself a certain number of times. For the -- | `Multiplicative` type, and for a non-negative power, this is the same as -- | normal number exponentiation. diff --git a/src/Data/Ord.purs b/src/Data/Ord.purs index 10c53eeb..66609d4b 100644 --- a/src/Data/Ord.purs +++ b/src/Data/Ord.purs @@ -14,17 +14,12 @@ module Data.Ord , module Data.Ordering ) where -import Data.Eq (class Eq, class Eq1, class EqRecord, (/=)) -import Data.Internal.Record (unsafeGet) +import Data.Eq (class Eq, class Eq1) import Data.Ord.Unsafe (unsafeCompare) import Data.Ordering (Ordering(..)) import Data.Ring (class Ring, zero, one, negate) -import Data.RowList (RLProxy(..)) -import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol) import Data.Unit (Unit) import Data.Void (Void) -import Prim.Row as Row -import Prim.RowList as RL -- | The `Ord` type class represents types which support comparisons with a -- | _total order_. @@ -174,31 +169,36 @@ class Eq1 f <= Ord1 f where instance ord1Array :: Ord1 Array where compare1 = compare -class EqRecord rowlist row focus <= OrdRecord rowlist row focus | rowlist -> focus where - compareImpl :: RLProxy rowlist -> Record row -> Record row -> Ordering - -instance ordRecordNil :: OrdRecord RL.Nil row focus where - compareImpl _ _ _ = EQ - -instance ordRecordCons - :: ( OrdRecord rowlistTail row subfocus - , Row.Cons key focus rowTail row - , IsSymbol key - , Ord focus - ) - => OrdRecord (RL.Cons key focus rowlistTail) row focus where - compareImpl _ ra rb - = if left /= EQ - then left - else compareImpl (RLProxy :: RLProxy rowlistTail) ra rb - where - key = reflectSymbol (SProxy :: SProxy key) - unsafeGet' = unsafeGet :: String -> Record row -> focus - left = unsafeGet' key ra `compare` unsafeGet' key rb - -instance ordRecord - :: ( RL.RowToList row list - , OrdRecord list row focus - ) - => Ord (Record row) where - compare = compareImpl (RLProxy :: RLProxy list) +-- Ordering for records is currently unimplemented as there are outstanding +-- questions around whether this implementation be useful. This is because it +-- prioritises the keys alphabetically, and this behaviour isn't overridable. +-- For now, we leave this unavailable, but the implementation is as follows: + +-- class EqRecord rowlist row focus <= OrdRecord rowlist row focus | rowlist -> focus where +-- compareImpl :: RLProxy rowlist -> Record row -> Record row -> Ordering +-- +-- instance ordRecordNil :: OrdRecord RL.Nil row focus where +-- compareImpl _ _ _ = EQ +-- +-- instance ordRecordCons +-- :: ( OrdRecord rowlistTail row subfocus +-- , Row.Cons key focus rowTail row +-- , IsSymbol key +-- , Ord focus +-- ) +-- => OrdRecord (RL.Cons key focus rowlistTail) row focus where +-- compareImpl _ ra rb +-- = if left /= EQ +-- then left +-- else compareImpl (RLProxy :: RLProxy rowlistTail) ra rb +-- where +-- key = reflectSymbol (SProxy :: SProxy key) +-- unsafeGet' = unsafeGet :: String -> Record row -> focus +-- left = unsafeGet' key ra `compare` unsafeGet' key rb +-- +-- instance ordRecord +-- :: ( RL.RowToList row list +-- , OrdRecord list row focus +-- ) +-- => Ord (Record row) where +-- compare = compareImpl (RLProxy :: RLProxy list) diff --git a/src/Data/Ring.purs b/src/Data/Ring.purs index 1fca86b5..943bf574 100644 --- a/src/Data/Ring.purs +++ b/src/Data/Ring.purs @@ -3,8 +3,13 @@ module Data.Ring , module Data.Semiring ) where -import Data.Semiring (class Semiring, add, mul, one, zero, (*), (+)) +import Data.Internal.Record (unsafeGet, unsafeInsert) +import Data.RowList (RLProxy(..)) +import Data.Semiring (class Semiring, class SemiringRecord, add, mul, one, zero, (*), (+)) +import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol) import Data.Unit (Unit, unit) +import Prim.Row as Row +import Prim.RowList as RL -- | The `Ring` class is for types that support addition, multiplication, -- | and subtraction operations. @@ -36,3 +41,31 @@ negate a = zero - a foreign import intSub :: Int -> Int -> Int foreign import numSub :: Number -> Number -> Number + +class RingRecord rowlist row subrow focus | rowlist -> subrow focus where + subRecordImpl :: RLProxy rowlist -> Record row -> Record row -> Record subrow + +instance ringRecordNil :: RingRecord RL.Nil row () focus where + subRecordImpl _ _ _ = {} + +instance ringRecordCons + :: ( IsSymbol key + , Row.Cons key focus subrowTail subrow + , RingRecord rowlistTail row subrowTail subfocus + , Ring focus + ) + => RingRecord (RL.Cons key focus rowlistTail) row subrow focus where + subRecordImpl _ ra rb + = unsafeInsert key + (unsafeGet' key ra - unsafeGet' key rb) + (subRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb) + where key = reflectSymbol (SProxy :: SProxy key) + unsafeGet' = unsafeGet :: String -> Record row -> focus + +instance ringRecord + :: ( RL.RowToList row list + , SemiringRecord list row row focus + , RingRecord list row row focus + ) + => Ring (Record row) where + sub = subRecordImpl (RLProxy :: RLProxy list) diff --git a/src/Data/Semigroup.purs b/src/Data/Semigroup.purs index 8a9858a1..159acf7b 100644 --- a/src/Data/Semigroup.purs +++ b/src/Data/Semigroup.purs @@ -1,4 +1,11 @@ -module Data.Semigroup (class Semigroup, append, (<>)) where +module Data.Semigroup + ( class Semigroup + , append + , (<>) + + , class SemigroupRecord + , semigroupRecordImpl + ) where import Data.Internal.Record (unsafeGet, unsafeInsert) import Data.RowList (RLProxy(..)) diff --git a/src/Data/Semiring.purs b/src/Data/Semiring.purs index 75f1e591..408226e9 100644 --- a/src/Data/Semiring.purs +++ b/src/Data/Semiring.purs @@ -1,6 +1,26 @@ -module Data.Semiring (class Semiring, add, (+), zero, mul, (*), one) where +module Data.Semiring + ( class Semiring + , add + , (+) + , zero + , mul + , (*) + , one + , class SemiringRecord + , addRecordImpl + , mulRecordImpl + , oneRecordImpl + , zeroRecordImpl + ) where + +import Data.Internal.Record (unsafeGet, unsafeInsert) +import Data.RowList (RLProxy(..)) +import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol) +import Type.Data.Row (RProxy(..)) import Data.Unit (Unit, unit) +import Prim.Row as Row +import Prim.RowList as RL -- | The `Semiring` class is for types that support an addition and -- | multiplication operation. @@ -60,3 +80,67 @@ foreign import intAdd :: Int -> Int -> Int foreign import intMul :: Int -> Int -> Int foreign import numAdd :: Number -> Number -> Number foreign import numMul :: Number -> Number -> Number + +class SemiringRecord rowlist row subrow focus | rowlist -> subrow focus where + addRecordImpl :: RLProxy rowlist -> Record row -> Record row -> Record subrow + mulRecordImpl :: RLProxy rowlist -> Record row -> Record row -> Record subrow + oneRecordImpl :: RLProxy rowlist -> RProxy row -> Record subrow + zeroRecordImpl :: RLProxy rowlist -> RProxy row -> Record subrow + +instance semiringRecordNil :: SemiringRecord RL.Nil row () focus where + addRecordImpl _ _ _ = {} + mulRecordImpl _ _ _ = {} + oneRecordImpl _ _ = {} + zeroRecordImpl _ _ = {} + +instance semiringRecordCons + :: ( IsSymbol key + , Row.Cons key focus subrowTail subrow + , SemiringRecord rowlistTail row subrowTail subfocus + , Semiring focus + ) + => SemiringRecord (RL.Cons key focus rowlistTail) row subrow focus where + addRecordImpl _ ra rb + = unsafeInsert key + (unsafeGet' key ra + unsafeGet' key rb) + (addRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb) + where key = reflectSymbol (SProxy :: SProxy key) + unsafeGet' = unsafeGet :: String -> Record row -> focus + + mulRecordImpl _ ra rb + = unsafeInsert key + (unsafeGet' key ra * unsafeGet' key rb) + (mulRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb) + where + key = reflectSymbol (SProxy :: SProxy key) + unsafeGet' = unsafeGet :: String -> Record row -> focus + + oneRecordImpl _ _ + = unsafeInsert key (one :: focus) + ( oneRecordImpl + (RLProxy :: RLProxy rowlistTail) + (RProxy :: RProxy row) + ) + where + key = reflectSymbol (SProxy :: SProxy key) + unsafeGet' = unsafeGet :: String -> Record row -> focus + + zeroRecordImpl _ _ + = unsafeInsert key (one :: focus) + ( zeroRecordImpl + (RLProxy :: RLProxy rowlistTail) + (RProxy :: RProxy row) + ) + where + key = reflectSymbol (SProxy :: SProxy key) + unsafeGet' = unsafeGet :: String -> Record row -> focus + +instance semiringRecord + :: ( RL.RowToList row list + , SemiringRecord list row row focus + ) + => Semiring (Record row) where + add = addRecordImpl (RLProxy :: RLProxy list) + mul = mulRecordImpl (RLProxy :: RLProxy list) + one = oneRecordImpl (RLProxy :: RLProxy list) (RProxy :: RProxy row) + zero = zeroRecordImpl (RLProxy :: RLProxy list) (RProxy :: RProxy row) diff --git a/src/Data/Show.js b/src/Data/Show.js index 1bef3390..4a85cd66 100644 --- a/src/Data/Show.js +++ b/src/Data/Show.js @@ -59,3 +59,15 @@ exports.showArrayImpl = function (f) { return "[" + ss.join(",") + "]"; }; }; + +exports.cons = function (head) { + return function (tail) { + return [head].concat(tail); + }; +}; + +exports.join = function (separator) { + return function (xs) { + return xs.join(separator); + }; +}; diff --git a/src/Data/Show.purs b/src/Data/Show.purs index 18ec08cf..7b28f7b4 100644 --- a/src/Data/Show.purs +++ b/src/Data/Show.purs @@ -1,5 +1,10 @@ module Data.Show (class Show, show) where +import Data.Internal.Record (unsafeGet) +import Data.RowList (RLProxy(..)) +import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol) +import Prim.RowList as RL + -- | The `Show` type class represents those types which can be converted into -- | a human-readable `String` representation. -- | @@ -28,8 +33,43 @@ instance showString :: Show String where instance showArray :: Show a => Show (Array a) where show = showArrayImpl show +class ShowRecordFields + (rowlist :: RL.RowList) + (row :: # Type) + focus + | rowlist -> focus where + showRecordFieldsImpl :: (RLProxy rowlist) -> Record row -> Array String + +instance showRecordFieldsNil + :: ShowRecordFields RL.Nil row focus where + showRecordFieldsImpl _ _ = [] + +instance showRecordFieldsCons + :: ( IsSymbol key + , ShowRecordFields rowlistTail row subfocus + , Show focus + ) + => ShowRecordFields (RL.Cons key focus rowlistTail) row focus where + showRecordFieldsImpl _ record = cons + (join ": " [ key, show (unsafeGet' key record) ]) + (showRecordFieldsImpl (RLProxy :: RLProxy rowlistTail) record) + where + key = reflectSymbol (SProxy :: SProxy key) + unsafeGet' = unsafeGet :: String -> Record row -> focus + +instance showRecord + :: ( RL.RowToList rs ls + , ShowRecordFields ls rs focus + ) + => Show (Record rs) where + show record = case showRecordFieldsImpl (RLProxy :: RLProxy ls) record of + [] -> "{}" + fields -> join " " [ "{", join ", " fields, "}" ] + foreign import showIntImpl :: Int -> String foreign import showNumberImpl :: Number -> String foreign import showCharImpl :: Char -> String foreign import showStringImpl :: String -> String foreign import showArrayImpl :: forall a. (a -> String) -> Array a -> String +foreign import cons :: forall a. a -> Array a -> Array a +foreign import join :: String -> Array String -> String diff --git a/src/Type/Data/Row.purs b/src/Type/Data/Row.purs new file mode 100644 index 00000000..0dd2113b --- /dev/null +++ b/src/Type/Data/Row.purs @@ -0,0 +1,4 @@ +module Type.Data.Row where + +data RProxy (row :: # Type) + = RProxy From 1c6a562f6f74d6b59fd0988e70d8131e461c1ed2 Mon Sep 17 00:00:00 2001 From: Tom Harding Date: Sun, 29 Apr 2018 23:25:48 +0100 Subject: [PATCH 13/23] BooleanAlgebra, Bounded, CommutativeRing, HeytingAlgebra, Ring SO MANY INSTANCES --- src/Data/BooleanAlgebra.purs | 9 ++- src/Data/Bounded.purs | 37 ++++++++++++ src/Data/CommutativeRing.purs | 12 +++- src/Data/HeytingAlgebra.purs | 110 +++++++++++++++++++++++++++++++++- src/Data/Ring.purs | 3 + 5 files changed, 166 insertions(+), 5 deletions(-) diff --git a/src/Data/BooleanAlgebra.purs b/src/Data/BooleanAlgebra.purs index 3babbf48..6bf48292 100644 --- a/src/Data/BooleanAlgebra.purs +++ b/src/Data/BooleanAlgebra.purs @@ -3,8 +3,9 @@ module Data.BooleanAlgebra , module Data.HeytingAlgebra ) where -import Data.HeytingAlgebra (class HeytingAlgebra, ff, tt, implies, conj, disj, not, (&&), (||)) +import Data.HeytingAlgebra (class HeytingAlgebra, class HeytingAlgebraRecord, ff, tt, implies, conj, disj, not, (&&), (||)) import Data.Unit (Unit) +import Prim.RowList as RL -- | The `BooleanAlgebra` type class represents types that behave like boolean -- | values. @@ -19,3 +20,9 @@ class HeytingAlgebra a <= BooleanAlgebra a instance booleanAlgebraBoolean :: BooleanAlgebra Boolean instance booleanAlgebraUnit :: BooleanAlgebra Unit instance booleanAlgebraFn :: BooleanAlgebra b => BooleanAlgebra (a -> b) + +instance booleanAlgebraRecord + :: ( RL.RowToList row list + , HeytingAlgebraRecord list row row focus + ) + => BooleanAlgebra (Record row) diff --git a/src/Data/Bounded.purs b/src/Data/Bounded.purs index 079c0d72..2da260f7 100644 --- a/src/Data/Bounded.purs +++ b/src/Data/Bounded.purs @@ -51,3 +51,40 @@ foreign import bottomNumber :: Number instance boundedNumber :: Bounded Number where top = topNumber bottom = bottomNumber + +-- Similarly to the `OrdRecord` constraint, this implementation is potentially +-- unstable. However, it is left here as a reference: + +--class BoundedRecord rowlist row subrow focus | rowlist -> subrow focus where +-- bottomRecordImpl :: RLProxy rowlist -> RProxy row -> Record subrow +-- topRecordImpl :: RLProxy rowlist -> RProxy row -> Record subrow +-- +--instance boundedRecordNil :: BoundedRecord RL.Nil row () focus where +-- bottomRecordImpl _ _ = {} +-- topRecordImpl _ _ = {} +-- +--instance boundedRecordCons +-- :: ( BoundedRecord rowlistTail row subrowTail subfocus +-- , Row.Cons key focus subrowTail subrow +-- , IsSymbol key +-- , Bounded focus +-- ) +-- => BoundedRecord (Row.Cons key focus rowlistTail) row subrow focus where +-- bottomRecordImpl _ row +-- = unsafeInsert key (bottom :: focus) +-- (bottomRecordImpl (RLProxy :: RLProxy rowlistTail) row) +-- where key = reflectSymbol (SProxy :: SProxy key) +-- +-- topRecordImpl _ row +-- = unsafeInsert key (bottom :: focus) +-- (topRecordImpl (RLProxy :: RLProxy rowlistTail) row) +-- where key = reflectSymbol (SProxy :: SProxy key) +-- +--instance boundedRecord +-- :: ( RL.RowToList row list +-- , BoundedRecord list row row focus +-- , OrdRecord list row row focus +-- ) +-- => Bounded (Record row) where +-- bottom = bottomRecordImpl (RLProxy :: RLProxy list) (RProxy :: RProxy row) +-- top = topRecordImpl (RLProxy :: RLProxy list) (RProxy :: RProxy row) diff --git a/src/Data/CommutativeRing.purs b/src/Data/CommutativeRing.purs index 1cd21fc4..5139fbc8 100644 --- a/src/Data/CommutativeRing.purs +++ b/src/Data/CommutativeRing.purs @@ -4,9 +4,10 @@ module Data.CommutativeRing , module Data.Semiring ) where -import Data.Ring (class Ring) -import Data.Semiring (class Semiring, add, mul, one, zero, (*), (+)) +import Data.Ring (class Ring, class RingRecord) +import Data.Semiring (class Semiring, class SemiringRecord, add, mul, one, zero, (*), (+)) import Data.Unit (Unit) +import Prim.RowList as RL -- | The `CommutativeRing` class is for rings where multiplication is -- | commutative. @@ -21,3 +22,10 @@ instance commutativeRingInt :: CommutativeRing Int instance commutativeRingNumber :: CommutativeRing Number instance commutativeRingUnit :: CommutativeRing Unit instance commutativeRingFn :: CommutativeRing b => CommutativeRing (a -> b) + +instance commutativeRingRecord + :: ( RL.RowToList row list + , SemiringRecord list row row focus + , RingRecord list row row focus + ) + => CommutativeRing (Record row) diff --git a/src/Data/HeytingAlgebra.purs b/src/Data/HeytingAlgebra.purs index dca83196..87203baf 100644 --- a/src/Data/HeytingAlgebra.purs +++ b/src/Data/HeytingAlgebra.purs @@ -1,9 +1,31 @@ module Data.HeytingAlgebra - ( class HeytingAlgebra, tt, ff, implies, conj, disj, not - , (&&), (||) + ( class HeytingAlgebra + + , tt + , ff + , implies + , conj + , disj + , not + , (&&) + , (||) + + , class HeytingAlgebraRecord + , ffRecordImpl + , ttRecordImpl + , impliesRecordImpl + , conjRecordImpl + , disjRecordImpl + , notRecordImpl ) where +import Data.Internal.Record (unsafeGet, unsafeInsert) +import Data.RowList (RLProxy(..)) +import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol) import Data.Unit (Unit, unit) +import Prim.Row as Row +import Prim.RowList as RL +import Type.Data.Row (RProxy(..)) -- | The `HeytingAlgebra` type class represents types that are bounded lattices with -- | an implication operator such that the following laws hold: @@ -68,3 +90,87 @@ instance heytingAlgebraFunction :: HeytingAlgebra b => HeytingAlgebra (a -> b) w foreign import boolConj :: Boolean -> Boolean -> Boolean foreign import boolDisj :: Boolean -> Boolean -> Boolean foreign import boolNot :: Boolean -> Boolean + +class HeytingAlgebraRecord rowlist row subrow focus | rowlist -> subrow focus where + ffRecordImpl :: RLProxy rowlist -> RProxy row -> Record subrow + ttRecordImpl :: RLProxy rowlist -> RProxy row -> Record subrow + impliesRecordImpl :: RLProxy rowlist -> Record row -> Record row -> Record subrow + disjRecordImpl :: RLProxy rowlist -> Record row -> Record row -> Record subrow + conjRecordImpl :: RLProxy rowlist -> Record row -> Record row -> Record subrow + notRecordImpl :: RLProxy rowlist -> Record row -> Record subrow + +instance heytingAlgebraRecordNil :: HeytingAlgebraRecord RL.Nil row () focus where + conjRecordImpl _ _ _ = {} + disjRecordImpl _ _ _ = {} + ffRecordImpl _ _ = {} + impliesRecordImpl _ _ _ = {} + notRecordImpl _ _ = {} + ttRecordImpl _ _ = {} + +instance heytingAlgebraRecordCons + :: ( IsSymbol key + , Row.Cons key focus subrowTail subrow + , HeytingAlgebraRecord rowlistTail row subrowTail subfocus + , HeytingAlgebra focus + ) + => HeytingAlgebraRecord (RL.Cons key focus rowlistTail) row subrow focus where + conjRecordImpl _ ra rb + = unsafeInsert key + (conj (unsafeGet' key ra) (unsafeGet' key rb)) + (conjRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb) + where key = reflectSymbol (SProxy :: SProxy key) + unsafeGet' = unsafeGet :: String -> Record row -> focus + + disjRecordImpl _ ra rb + = unsafeInsert key + (disj (unsafeGet' key ra) (unsafeGet' key rb)) + (disjRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb) + where key = reflectSymbol (SProxy :: SProxy key) + unsafeGet' = unsafeGet :: String -> Record row -> focus + + impliesRecordImpl _ ra rb + = unsafeInsert key + (implies (unsafeGet' key ra) (unsafeGet' key rb)) + (impliesRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb) + where + key = reflectSymbol (SProxy :: SProxy key) + unsafeGet' = unsafeGet :: String -> Record row -> focus + + ffRecordImpl _ _ + = unsafeInsert key (ff :: focus) + ( ffRecordImpl + (RLProxy :: RLProxy rowlistTail) + (RProxy :: RProxy row) + ) + where + key = reflectSymbol (SProxy :: SProxy key) + unsafeGet' = unsafeGet :: String -> Record row -> focus + + notRecordImpl _ row + = unsafeInsert key (not (unsafeGet' key row)) + (notRecordImpl (RLProxy :: RLProxy rowlistTail) row) + where + key = reflectSymbol (SProxy :: SProxy key) + unsafeGet' = unsafeGet :: String -> Record row -> focus + + ttRecordImpl _ _ + = unsafeInsert key (tt :: focus) + ( ttRecordImpl + (RLProxy :: RLProxy rowlistTail) + (RProxy :: RProxy row) + ) + where + key = reflectSymbol (SProxy :: SProxy key) + unsafeGet' = unsafeGet :: String -> Record row -> focus + +instance heytingAlgebraRecord + :: ( RL.RowToList row list + , HeytingAlgebraRecord list row row focus + ) + => HeytingAlgebra (Record row) where + ff = ffRecordImpl (RLProxy :: RLProxy list) (RProxy :: RProxy row) + tt = ttRecordImpl (RLProxy :: RLProxy list) (RProxy :: RProxy row) + conj = conjRecordImpl (RLProxy :: RLProxy list) + disj = conjRecordImpl (RLProxy :: RLProxy list) + implies = conjRecordImpl (RLProxy :: RLProxy list) + not = notRecordImpl (RLProxy :: RLProxy list) diff --git a/src/Data/Ring.purs b/src/Data/Ring.purs index 943bf574..4917e185 100644 --- a/src/Data/Ring.purs +++ b/src/Data/Ring.purs @@ -1,6 +1,9 @@ module Data.Ring ( class Ring, sub, negate, (-) , module Data.Semiring + + , class RingRecord + , subRecordImpl ) where import Data.Internal.Record (unsafeGet, unsafeInsert) From 6d929d756676615105bd0e0c457fc3c30b3f76fa Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Mon, 30 Apr 2018 12:17:56 +0100 Subject: [PATCH 14/23] Remove `quot`/`rem` & hide `intDiv`/`intMod` `quot` and `rem` exist in `Data.Int` --- src/Data/EuclideanRing.js | 13 ------------- src/Data/EuclideanRing.purs | 39 ------------------------------------- src/Prelude.purs | 2 +- test/Test/Main.purs | 30 ++-------------------------- 4 files changed, 3 insertions(+), 81 deletions(-) diff --git a/src/Data/EuclideanRing.js b/src/Data/EuclideanRing.js index e7b8ad94..d7ac4c8f 100644 --- a/src/Data/EuclideanRing.js +++ b/src/Data/EuclideanRing.js @@ -12,13 +12,6 @@ exports.intDiv = function (x) { }; }; -exports.quot = function (x) { - return function (y) { - /* jshint bitwise: false */ - return x / y | 0; - }; -}; - exports.intMod = function (x) { return function (y) { var yy = Math.abs(y); @@ -26,12 +19,6 @@ exports.intMod = function (x) { }; }; -exports.rem = function (x) { - return function (y) { - return x % y; - }; -}; - exports.numDiv = function (n1) { return function (n2) { return n1 / n2; diff --git a/src/Data/EuclideanRing.purs b/src/Data/EuclideanRing.purs index 6a0edf2f..3f790bfe 100644 --- a/src/Data/EuclideanRing.purs +++ b/src/Data/EuclideanRing.purs @@ -2,13 +2,9 @@ module Data.EuclideanRing ( class EuclideanRing, degree, div, mod, (/) , gcd , lcm - , quot - , rem , module Data.CommutativeRing , module Data.Ring , module Data.Semiring - , intDiv - , intMod ) where import Data.BooleanAlgebra ((||)) @@ -100,38 +96,3 @@ lcm a b = if a == zero || b == zero then zero else a * b / gcd a b - --- | The `quot` function provides _truncating_ integer division (see the --- | documentation for the `EuclideanRing` class). It is identical to `div` in --- | the `EuclideanRing Int` instance if the dividend is positive, but will be --- | slightly different if the dividend is negative. For example: --- | --- | ```purescript --- | div 2 3 == 0 --- | quot 2 3 == 0 --- | --- | div (-2) 3 == (-1) --- | quot (-2) 3 == 0 --- | --- | div 2 (-3) == 0 --- | quot 2 (-3) == 0 --- | ``` -foreign import quot :: Int -> Int -> Int - --- | The `rem` function provides the remainder after _truncating_ integer --- | division (see the documentation for the `EuclideanRing` class). It is --- | identical to `mod` in the `EuclideanRing Int` instance if the dividend is --- | positive, but will be slightly different if the dividend is negative. For --- | example: --- | --- | ```purescript --- | mod 2 3 == 2 --- | rem 2 3 == 2 --- | --- | mod (-2) 3 == 1 --- | rem (-2) 3 == (-2) --- | --- | mod 2 (-3) == 2 --- | rem 2 (-3) == 2 --- | ``` -foreign import rem :: Int -> Int -> Int diff --git a/src/Prelude.purs b/src/Prelude.purs index d6cfe2bb..3a1cd439 100644 --- a/src/Prelude.purs +++ b/src/Prelude.purs @@ -41,7 +41,7 @@ import Data.Bounded (class Bounded, bottom, top) import Data.CommutativeRing (class CommutativeRing) import Data.DivisionRing (class DivisionRing, recip) import Data.Eq (class Eq, eq, notEq, (/=), (==)) -import Data.EuclideanRing (class EuclideanRing, degree, div, mod, quot, rem, (/), gcd, lcm) +import Data.EuclideanRing (class EuclideanRing, degree, div, mod, (/), gcd, lcm) import Data.Field (class Field) import Data.Function (const, flip, ($), (#)) import Data.Functor (class Functor, flap, map, void, ($>), (<#>), (<$), (<$>), (<@>)) diff --git a/test/Test/Main.purs b/test/Test/Main.purs index 177d85f3..5ee656ef 100644 --- a/test/Test/Main.purs +++ b/test/Test/Main.purs @@ -1,7 +1,6 @@ module Test.Main where import Prelude -import Data.EuclideanRing (intDiv, intMod) import Data.Ord (abs) type AlmostEff = Unit -> Unit @@ -12,7 +11,6 @@ main = do testOrderings testOrdUtils testIntDivMod - testIntQuotRem testIntDegree foreign import testNumberShow :: (Number -> String) -> AlmostEff @@ -103,8 +101,8 @@ testIntDivMod = do where go a b = let - q = intDiv a b - r = intMod a b + q = a / b + r = a `mod` b msg = show a <> " / " <> show b <> ": " in do assert (msg <> "Quotient/remainder law") $ @@ -112,30 +110,6 @@ testIntDivMod = do assert (msg <> "Remainder should be between 0 and `abs b`, got: " <> show r) $ 0 <= r && r < abs b -testIntQuotRem :: AlmostEff -testIntQuotRem = do - -- Check when dividend goes into divisor exactly - go 8 2 - go (-8) 2 - go 8 (-2) - go (-8) (-2) - - -- Check when dividend does not go into divisor exactly - go 2 3 - go (-2) 3 - go 2 (-3) - go (-2) (-3) - - where - go a b = - let - q = quot a b - r = rem a b - msg = show a <> " / " <> show b <> ": " - in do - assert (msg <> "Quotient/remainder law") $ - q * b + r == a - testIntDegree :: AlmostEff testIntDegree = do let bot = bottom :: Int From 3c7f84ae881af003d27cf94e1477431fe4328ba0 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Mon, 30 Apr 2018 12:19:01 +0100 Subject: [PATCH 15/23] Prevent NaN / Infinity for mod/div by 0 integers --- src/Data/EuclideanRing.js | 2 ++ test/Test/Main.purs | 7 ++----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Data/EuclideanRing.js b/src/Data/EuclideanRing.js index d7ac4c8f..40b55494 100644 --- a/src/Data/EuclideanRing.js +++ b/src/Data/EuclideanRing.js @@ -8,12 +8,14 @@ exports.intDegree = function (x) { // https://en.m.wikipedia.org/wiki/Modulo_operation. exports.intDiv = function (x) { return function (y) { + if (y == 0) return 0; return y > 0 ? Math.floor(x / y) : -Math.floor(x / -y); }; }; exports.intMod = function (x) { return function (y) { + if (y == 0) return 0; var yy = Math.abs(y); return ((x % yy) + yy) % yy; }; diff --git a/test/Test/Main.purs b/test/Test/Main.purs index 5ee656ef..51d76e1e 100644 --- a/test/Test/Main.purs +++ b/test/Test/Main.purs @@ -29,10 +29,6 @@ testOrd x y ord = nan :: Number nan = 0.0/0.0 --- Unfortunately, NaN inhabits our Int -intNan :: Int -intNan = mod 1 0 - plusInfinity :: Number plusInfinity = 1.0/0.0 @@ -60,7 +56,8 @@ testOrderings = do assert "NaN > 1 should be false" $ (nan > 1.0) == false assert "NaN < 1 should be false" $ (nan < 1.0) == false assert "NaN == 1 should be false" $ nan /= 1.0 - testOrd intNan 2147483647 GT + testOrd (1 / 0) 0 EQ + testOrd (mod 1 0) 0 EQ testOrd 'a' 'b' LT testOrd 'b' 'A' GT testOrd "10" "0" GT From 5a26b1e0a2251554960386825db95178ff4f0cd5 Mon Sep 17 00:00:00 2001 From: Tom Harding Date: Mon, 30 Apr 2018 19:12:15 +0100 Subject: [PATCH 16/23] Finish record instances and add tests As far as I can tell, there are now instances for all applicable typeclasses given for primitive records. I've even added tests, which typecheck (good news), though I don't think any of them actually run. --- src/Data/BooleanAlgebra.purs | 4 +- src/Data/Bounded.purs | 37 -------------- src/Data/CommutativeRing.purs | 8 +-- src/Data/DivisionRing.purs | 2 +- src/Data/Eq.purs | 27 +++++----- src/Data/HeytingAlgebra.purs | 84 +++++++++++++++----------------- src/Data/Monoid.purs | 38 ++++++++------- src/Data/Ring.purs | 32 ++++++------ src/Data/Semigroup.purs | 36 +++++++------- src/Data/Semiring.purs | 70 ++++++++++++-------------- src/Data/Show.purs | 38 ++++++++------- src/{ => Type}/Data/RowList.purs | 2 +- test/Test/Main.purs | 30 ++++++++++++ 13 files changed, 196 insertions(+), 212 deletions(-) rename src/{ => Type}/Data/RowList.purs (81%) diff --git a/src/Data/BooleanAlgebra.purs b/src/Data/BooleanAlgebra.purs index 6bf48292..d5b2ced0 100644 --- a/src/Data/BooleanAlgebra.purs +++ b/src/Data/BooleanAlgebra.purs @@ -3,7 +3,7 @@ module Data.BooleanAlgebra , module Data.HeytingAlgebra ) where -import Data.HeytingAlgebra (class HeytingAlgebra, class HeytingAlgebraRecord, ff, tt, implies, conj, disj, not, (&&), (||)) +import Data.HeytingAlgebra (class HeytingAlgebra, class HeytingAlgebraRow, ff, tt, implies, conj, disj, not, (&&), (||)) import Data.Unit (Unit) import Prim.RowList as RL @@ -23,6 +23,6 @@ instance booleanAlgebraFn :: BooleanAlgebra b => BooleanAlgebra (a -> b) instance booleanAlgebraRecord :: ( RL.RowToList row list - , HeytingAlgebraRecord list row row focus + , HeytingAlgebraRow list row row focus ) => BooleanAlgebra (Record row) diff --git a/src/Data/Bounded.purs b/src/Data/Bounded.purs index 2da260f7..079c0d72 100644 --- a/src/Data/Bounded.purs +++ b/src/Data/Bounded.purs @@ -51,40 +51,3 @@ foreign import bottomNumber :: Number instance boundedNumber :: Bounded Number where top = topNumber bottom = bottomNumber - --- Similarly to the `OrdRecord` constraint, this implementation is potentially --- unstable. However, it is left here as a reference: - ---class BoundedRecord rowlist row subrow focus | rowlist -> subrow focus where --- bottomRecordImpl :: RLProxy rowlist -> RProxy row -> Record subrow --- topRecordImpl :: RLProxy rowlist -> RProxy row -> Record subrow --- ---instance boundedRecordNil :: BoundedRecord RL.Nil row () focus where --- bottomRecordImpl _ _ = {} --- topRecordImpl _ _ = {} --- ---instance boundedRecordCons --- :: ( BoundedRecord rowlistTail row subrowTail subfocus --- , Row.Cons key focus subrowTail subrow --- , IsSymbol key --- , Bounded focus --- ) --- => BoundedRecord (Row.Cons key focus rowlistTail) row subrow focus where --- bottomRecordImpl _ row --- = unsafeInsert key (bottom :: focus) --- (bottomRecordImpl (RLProxy :: RLProxy rowlistTail) row) --- where key = reflectSymbol (SProxy :: SProxy key) --- --- topRecordImpl _ row --- = unsafeInsert key (bottom :: focus) --- (topRecordImpl (RLProxy :: RLProxy rowlistTail) row) --- where key = reflectSymbol (SProxy :: SProxy key) --- ---instance boundedRecord --- :: ( RL.RowToList row list --- , BoundedRecord list row row focus --- , OrdRecord list row row focus --- ) --- => Bounded (Record row) where --- bottom = bottomRecordImpl (RLProxy :: RLProxy list) (RProxy :: RProxy row) --- top = topRecordImpl (RLProxy :: RLProxy list) (RProxy :: RProxy row) diff --git a/src/Data/CommutativeRing.purs b/src/Data/CommutativeRing.purs index 5139fbc8..2f90a61e 100644 --- a/src/Data/CommutativeRing.purs +++ b/src/Data/CommutativeRing.purs @@ -4,8 +4,8 @@ module Data.CommutativeRing , module Data.Semiring ) where -import Data.Ring (class Ring, class RingRecord) -import Data.Semiring (class Semiring, class SemiringRecord, add, mul, one, zero, (*), (+)) +import Data.Ring (class Ring, class RingRow) +import Data.Semiring (class Semiring, class SemiringRow, add, mul, one, zero, (*), (+)) import Data.Unit (Unit) import Prim.RowList as RL @@ -25,7 +25,7 @@ instance commutativeRingFn :: CommutativeRing b => CommutativeRing (a -> b) instance commutativeRingRecord :: ( RL.RowToList row list - , SemiringRecord list row row focus - , RingRecord list row row focus + , SemiringRow list row row focus + , RingRow list row row focus ) => CommutativeRing (Record row) diff --git a/src/Data/DivisionRing.purs b/src/Data/DivisionRing.purs index c5aa86bc..227f7a94 100644 --- a/src/Data/DivisionRing.purs +++ b/src/Data/DivisionRing.purs @@ -7,9 +7,9 @@ module Data.DivisionRing , module Data.Semiring ) where +import Data.EuclideanRing ((/)) import Data.Ring (class Ring, negate, sub) import Data.Semiring (class Semiring, add, mul, one, zero, (*), (+)) -import Data.EuclideanRing ((/)) -- | The `DivisionRing` class is for non-zero rings in which every non-zero -- | element has a multiplicative inverse. Division rings are sometimes also diff --git a/src/Data/Eq.purs b/src/Data/Eq.purs index 6600ff09..08e8cd3f 100644 --- a/src/Data/Eq.purs +++ b/src/Data/Eq.purs @@ -2,13 +2,13 @@ module Data.Eq ( class Eq, eq, (==), notEq, (/=) , class Eq1, eq1, notEq1 - , class EqRecord + , class EqRow , eqRecordImpl ) where import Data.HeytingAlgebra ((&&)) import Data.Internal.Record (unsafeGet) -import Data.RowList (RLProxy(..)) +import Type.Data.RowList (RLProxy(..)) import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol) import Data.Unit (Unit) import Data.Void (Void) @@ -76,28 +76,29 @@ instance eq1Array :: Eq1 Array where notEq1 :: forall f a. Eq1 f => Eq a => f a -> f a -> Boolean notEq1 x y = (x `eq1` y) == false -class EqRecord rowlist row focus | rowlist -> focus where +-- | A typeclass to characterise rows of types that are all Eq.. +class EqRow rowlist row focus | rowlist -> focus where eqRecordImpl :: RLProxy rowlist -> Record row -> Record row -> Boolean -instance eqRecordNil :: EqRecord RL.Nil row focus where +instance eqRowNil :: EqRow RL.Nil row focus where eqRecordImpl _ _ _ = true -instance eqRecordCons - :: ( EqRecord rowlistTail row subfocus +instance eqRowCons + :: ( EqRow rowlistTail row subfocus , Row.Cons key focus rowTail row , IsSymbol key , Eq focus ) - => EqRecord (RL.Cons key focus rowlistTail) row focus where - eqRecordImpl _ ra rb - = unsafeGet' key ra == unsafeGet key rb - && eqRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb - where key = reflectSymbol (SProxy :: SProxy key) - unsafeGet' = unsafeGet :: String -> Record row -> focus + => EqRow (RL.Cons key focus rowlistTail) row focus where + eqRecordImpl _ ra rb = (get ra == get rb) && tail + where + key = reflectSymbol (SProxy :: SProxy key) + get = unsafeGet key :: Record row -> focus + tail = eqRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb instance eqRecord :: ( RL.RowToList row list - , EqRecord list row focus + , EqRow list row focus ) => Eq (Record row) where eq = eqRecordImpl (RLProxy :: RLProxy list) diff --git a/src/Data/HeytingAlgebra.purs b/src/Data/HeytingAlgebra.purs index 87203baf..b8860e06 100644 --- a/src/Data/HeytingAlgebra.purs +++ b/src/Data/HeytingAlgebra.purs @@ -10,7 +10,7 @@ module Data.HeytingAlgebra , (&&) , (||) - , class HeytingAlgebraRecord + , class HeytingAlgebraRow , ffRecordImpl , ttRecordImpl , impliesRecordImpl @@ -20,12 +20,12 @@ module Data.HeytingAlgebra ) where import Data.Internal.Record (unsafeGet, unsafeInsert) -import Data.RowList (RLProxy(..)) import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol) import Data.Unit (Unit, unit) import Prim.Row as Row import Prim.RowList as RL import Type.Data.Row (RProxy(..)) +import Type.Data.RowList (RLProxy(..)) -- | The `HeytingAlgebra` type class represents types that are bounded lattices with -- | an implication operator such that the following laws hold: @@ -91,7 +91,7 @@ foreign import boolConj :: Boolean -> Boolean -> Boolean foreign import boolDisj :: Boolean -> Boolean -> Boolean foreign import boolNot :: Boolean -> Boolean -class HeytingAlgebraRecord rowlist row subrow focus | rowlist -> subrow focus where +class HeytingAlgebraRow rowlist row subrow focus | rowlist -> subrow focus where ffRecordImpl :: RLProxy rowlist -> RProxy row -> Record subrow ttRecordImpl :: RLProxy rowlist -> RProxy row -> Record subrow impliesRecordImpl :: RLProxy rowlist -> Record row -> Record row -> Record subrow @@ -99,7 +99,7 @@ class HeytingAlgebraRecord rowlist row subrow focus | rowlist -> subrow focus wh conjRecordImpl :: RLProxy rowlist -> Record row -> Record row -> Record subrow notRecordImpl :: RLProxy rowlist -> Record row -> Record subrow -instance heytingAlgebraRecordNil :: HeytingAlgebraRecord RL.Nil row () focus where +instance heytingAlgebraRowNil :: HeytingAlgebraRow RL.Nil row () focus where conjRecordImpl _ _ _ = {} disjRecordImpl _ _ _ = {} ffRecordImpl _ _ = {} @@ -107,65 +107,57 @@ instance heytingAlgebraRecordNil :: HeytingAlgebraRecord RL.Nil row () focus whe notRecordImpl _ _ = {} ttRecordImpl _ _ = {} -instance heytingAlgebraRecordCons +instance heytingAlgebraRowCons :: ( IsSymbol key , Row.Cons key focus subrowTail subrow - , HeytingAlgebraRecord rowlistTail row subrowTail subfocus + , HeytingAlgebraRow rowlistTail row subrowTail subfocus , HeytingAlgebra focus ) - => HeytingAlgebraRecord (RL.Cons key focus rowlistTail) row subrow focus where - conjRecordImpl _ ra rb - = unsafeInsert key - (conj (unsafeGet' key ra) (unsafeGet' key rb)) - (conjRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb) - where key = reflectSymbol (SProxy :: SProxy key) - unsafeGet' = unsafeGet :: String -> Record row -> focus - - disjRecordImpl _ ra rb - = unsafeInsert key - (disj (unsafeGet' key ra) (unsafeGet' key rb)) - (disjRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb) - where key = reflectSymbol (SProxy :: SProxy key) - unsafeGet' = unsafeGet :: String -> Record row -> focus - - impliesRecordImpl _ ra rb - = unsafeInsert key - (implies (unsafeGet' key ra) (unsafeGet' key rb)) - (impliesRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb) + => HeytingAlgebraRow (RL.Cons key focus rowlistTail) row subrow focus where + conjRecordImpl _ ra rb = insert (conj (get ra) (get rb)) tail where key = reflectSymbol (SProxy :: SProxy key) - unsafeGet' = unsafeGet :: String -> Record row -> focus - - ffRecordImpl _ _ - = unsafeInsert key (ff :: focus) - ( ffRecordImpl - (RLProxy :: RLProxy rowlistTail) - (RProxy :: RProxy row) - ) + get = unsafeGet key :: Record row -> focus + insert = unsafeInsert key :: focus -> Record subrowTail -> Record subrow + tail = conjRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb + + disjRecordImpl _ ra rb = insert (disj (get ra) (get rb)) tail + where + key = reflectSymbol (SProxy :: SProxy key) + get = unsafeGet key :: Record row -> focus + insert = unsafeInsert key :: focus -> Record subrowTail -> Record subrow + tail = disjRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb + + impliesRecordImpl _ ra rb = insert (implies (get ra) (get rb)) tail where key = reflectSymbol (SProxy :: SProxy key) - unsafeGet' = unsafeGet :: String -> Record row -> focus + get = unsafeGet key :: Record row -> focus + insert = unsafeInsert key :: focus -> Record subrowTail -> Record subrow + tail = impliesRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb + + ffRecordImpl _ row = insert ff tail + where + key = reflectSymbol (SProxy :: SProxy key) + insert = unsafeInsert key :: focus -> Record subrowTail -> Record subrow + tail = ffRecordImpl (RLProxy :: RLProxy rowlistTail) row notRecordImpl _ row - = unsafeInsert key (not (unsafeGet' key row)) - (notRecordImpl (RLProxy :: RLProxy rowlistTail) row) + = insert (not (get row)) tail where key = reflectSymbol (SProxy :: SProxy key) - unsafeGet' = unsafeGet :: String -> Record row -> focus - - ttRecordImpl _ _ - = unsafeInsert key (tt :: focus) - ( ttRecordImpl - (RLProxy :: RLProxy rowlistTail) - (RProxy :: RProxy row) - ) + get = unsafeGet key :: Record row -> focus + insert = unsafeInsert key :: focus -> Record subrowTail -> Record subrow + tail = notRecordImpl (RLProxy :: RLProxy rowlistTail) row + + ttRecordImpl _ row = insert tt tail where key = reflectSymbol (SProxy :: SProxy key) - unsafeGet' = unsafeGet :: String -> Record row -> focus + insert = unsafeInsert key :: focus -> Record subrowTail -> Record subrow + tail = ttRecordImpl (RLProxy :: RLProxy rowlistTail) row instance heytingAlgebraRecord :: ( RL.RowToList row list - , HeytingAlgebraRecord list row row focus + , HeytingAlgebraRow list row row focus ) => HeytingAlgebra (Record row) where ff = ffRecordImpl (RLProxy :: RLProxy list) (RProxy :: RProxy row) diff --git a/src/Data/Monoid.purs b/src/Data/Monoid.purs index f2a3955f..8775468c 100644 --- a/src/Data/Monoid.purs +++ b/src/Data/Monoid.purs @@ -3,6 +3,9 @@ module Data.Monoid , power , guard , module Data.Semigroup + + , class MonoidRow + , memptyRecordImpl ) where import Data.Boolean (otherwise) @@ -11,10 +14,11 @@ import Data.EuclideanRing (mod, (/)) import Data.Internal.Record (unsafeInsert) import Data.Ord ((<=)) import Data.Ordering (Ordering(..)) -import Data.RowList (RLProxy(..)) -import Data.Semigroup (class Semigroup, class SemigroupRecord, (<>)) +import Type.Data.RowList (RLProxy(..)) +import Data.Semigroup (class Semigroup, class SemigroupRow, (<>)) import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol) import Data.Unit (Unit, unit) +import Prim.Row as Row import Prim.RowList as RL -- | A `Monoid` is a `Semigroup` with a value `mempty`, which is both a @@ -45,33 +49,33 @@ instance monoidString :: Monoid String where instance monoidArray :: Monoid (Array a) where mempty = [] -class MonoidRecord rowlist row focus | rowlist -> row focus where - monoidRecordImpl :: RLProxy rowlist -> Record row +class MonoidRow rowlist subrow focus | rowlist -> subrow focus where + memptyRecordImpl :: RLProxy rowlist -> Record subrow -instance monoidRecordNil :: MonoidRecord RL.Nil () focus where - monoidRecordImpl _ = {} +instance monoidRowNil :: MonoidRow RL.Nil () focus where + memptyRecordImpl _ = {} -instance monoidRecordCons +instance monoidRowCons :: ( IsSymbol key , Monoid focus - , MonoidRecord rowlistTail row subfocus + , Row.Cons key focus subrowTail subrow + , MonoidRow rowlistTail subrowTail subfocus ) - => MonoidRecord (RL.Cons key focus rowlistTail) row focus where - monoidRecordImpl _ - = unsafeInsert key - (mempty :: focus) - (monoidRecordImpl (RLProxy :: RLProxy rowlistTail)) + => MonoidRow (RL.Cons key focus rowlistTail) subrow focus where + memptyRecordImpl _ + = insert mempty tail where key = reflectSymbol (SProxy :: SProxy key) + insert = unsafeInsert key :: focus -> Record subrowTail -> Record subrow + tail = memptyRecordImpl (RLProxy :: RLProxy rowlistTail) instance monoidRecord :: ( RL.RowToList row list - , SemigroupRecord list row row focus - , MonoidRecord list row focus + , SemigroupRow list row row focus + , MonoidRow list row focus ) => Monoid (Record row) where - mempty = monoidRecordImpl (RLProxy :: RLProxy list) - + mempty = memptyRecordImpl (RLProxy :: RLProxy list) -- | Append a value to itself a certain number of times. For the -- | `Multiplicative` type, and for a non-negative power, this is the same as diff --git a/src/Data/Ring.purs b/src/Data/Ring.purs index 4917e185..17d3a0f6 100644 --- a/src/Data/Ring.purs +++ b/src/Data/Ring.purs @@ -2,13 +2,13 @@ module Data.Ring ( class Ring, sub, negate, (-) , module Data.Semiring - , class RingRecord + , class RingRow , subRecordImpl ) where import Data.Internal.Record (unsafeGet, unsafeInsert) -import Data.RowList (RLProxy(..)) -import Data.Semiring (class Semiring, class SemiringRecord, add, mul, one, zero, (*), (+)) +import Type.Data.RowList (RLProxy(..)) +import Data.Semiring (class Semiring, class SemiringRow, add, mul, one, zero, (*), (+)) import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol) import Data.Unit (Unit, unit) import Prim.Row as Row @@ -45,30 +45,30 @@ negate a = zero - a foreign import intSub :: Int -> Int -> Int foreign import numSub :: Number -> Number -> Number -class RingRecord rowlist row subrow focus | rowlist -> subrow focus where +class RingRow rowlist row subrow focus | rowlist -> subrow focus where subRecordImpl :: RLProxy rowlist -> Record row -> Record row -> Record subrow -instance ringRecordNil :: RingRecord RL.Nil row () focus where +instance ringRowNil :: RingRow RL.Nil row () focus where subRecordImpl _ _ _ = {} -instance ringRecordCons +instance ringRowCons :: ( IsSymbol key , Row.Cons key focus subrowTail subrow - , RingRecord rowlistTail row subrowTail subfocus + , RingRow rowlistTail row subrowTail subfocus , Ring focus ) - => RingRecord (RL.Cons key focus rowlistTail) row subrow focus where - subRecordImpl _ ra rb - = unsafeInsert key - (unsafeGet' key ra - unsafeGet' key rb) - (subRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb) - where key = reflectSymbol (SProxy :: SProxy key) - unsafeGet' = unsafeGet :: String -> Record row -> focus + => RingRow (RL.Cons key focus rowlistTail) row subrow focus where + subRecordImpl _ ra rb = insert (get ra - get rb) tail + where + insert = unsafeInsert key :: focus -> Record subrowTail -> Record subrow + key = reflectSymbol (SProxy :: SProxy key) + get = unsafeGet key :: Record row -> focus + tail = subRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb instance ringRecord :: ( RL.RowToList row list - , SemiringRecord list row row focus - , RingRecord list row row focus + , SemiringRow list row row focus + , RingRow list row row focus ) => Ring (Record row) where sub = subRecordImpl (RLProxy :: RLProxy list) diff --git a/src/Data/Semigroup.purs b/src/Data/Semigroup.purs index 159acf7b..917da3fa 100644 --- a/src/Data/Semigroup.purs +++ b/src/Data/Semigroup.purs @@ -3,12 +3,12 @@ module Data.Semigroup , append , (<>) - , class SemigroupRecord - , semigroupRecordImpl + , class SemigroupRow + , appendRecordImpl ) where import Data.Internal.Record (unsafeGet, unsafeInsert) -import Data.RowList (RLProxy(..)) +import Type.Data.RowList (RLProxy(..)) import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol) import Data.Unit (Unit, unit) import Data.Void (Void, absurd) @@ -46,29 +46,29 @@ instance semigroupArray :: Semigroup (Array a) where foreign import concatString :: String -> String -> String foreign import concatArray :: forall a. Array a -> Array a -> Array a -class SemigroupRecord rowlist row subrow focus | rowlist -> subrow focus where - semigroupRecordImpl :: RLProxy rowlist -> Record row -> Record row -> Record subrow +class SemigroupRow rowlist row subrow focus | rowlist -> subrow focus where + appendRecordImpl :: RLProxy rowlist -> Record row -> Record row -> Record subrow -instance semigroupRecordNil :: SemigroupRecord RL.Nil row () focus where - semigroupRecordImpl _ _ _ = {} +instance semigroupRowNil :: SemigroupRow RL.Nil row () focus where + appendRecordImpl _ _ _ = {} instance semigroupRecordCons :: ( IsSymbol key , Row.Cons key focus subrowTail subrow - , SemigroupRecord rowlistTail row subrowTail subfocus + , SemigroupRow rowlistTail row subrowTail subfocus , Semigroup focus ) - => SemigroupRecord (RL.Cons key focus rowlistTail) row subrow focus where - semigroupRecordImpl _ ra rb - = unsafeInsert key - (unsafeGet' key ra <> unsafeGet' key rb) - (semigroupRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb) - where key = reflectSymbol (SProxy :: SProxy key) - unsafeGet' = unsafeGet :: String -> Record row -> focus + => SemigroupRow (RL.Cons key focus rowlistTail) row subrow focus where + appendRecordImpl _ ra rb = insert (get ra <> get rb) tail + where + key = reflectSymbol (SProxy :: SProxy key) + get = unsafeGet key :: Record row -> focus + insert = unsafeInsert key :: focus -> Record subrowTail -> Record subrow + tail = appendRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb -instance semigroupRecord +instance semigroupRow :: ( RL.RowToList row list - , SemigroupRecord list row row focus + , SemigroupRow list row row focus ) => Semigroup (Record row) where - append = semigroupRecordImpl (RLProxy :: RLProxy list) + append = appendRecordImpl (RLProxy :: RLProxy list) diff --git a/src/Data/Semiring.purs b/src/Data/Semiring.purs index 408226e9..dbc5ff0d 100644 --- a/src/Data/Semiring.purs +++ b/src/Data/Semiring.purs @@ -7,7 +7,7 @@ module Data.Semiring , (*) , one - , class SemiringRecord + , class SemiringRow , addRecordImpl , mulRecordImpl , oneRecordImpl @@ -15,7 +15,7 @@ module Data.Semiring ) where import Data.Internal.Record (unsafeGet, unsafeInsert) -import Data.RowList (RLProxy(..)) +import Type.Data.RowList (RLProxy(..)) import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol) import Type.Data.Row (RProxy(..)) import Data.Unit (Unit, unit) @@ -81,66 +81,58 @@ foreign import intMul :: Int -> Int -> Int foreign import numAdd :: Number -> Number -> Number foreign import numMul :: Number -> Number -> Number -class SemiringRecord rowlist row subrow focus | rowlist -> subrow focus where +-- | A type class to characterise row types in which all members are Semiring. +class SemiringRow rowlist row subrow focus | rowlist -> subrow focus where addRecordImpl :: RLProxy rowlist -> Record row -> Record row -> Record subrow mulRecordImpl :: RLProxy rowlist -> Record row -> Record row -> Record subrow oneRecordImpl :: RLProxy rowlist -> RProxy row -> Record subrow zeroRecordImpl :: RLProxy rowlist -> RProxy row -> Record subrow -instance semiringRecordNil :: SemiringRecord RL.Nil row () focus where +instance semiringRowNil :: SemiringRow RL.Nil row () focus where addRecordImpl _ _ _ = {} mulRecordImpl _ _ _ = {} oneRecordImpl _ _ = {} zeroRecordImpl _ _ = {} -instance semiringRecordCons +instance semiringRowCons :: ( IsSymbol key , Row.Cons key focus subrowTail subrow - , SemiringRecord rowlistTail row subrowTail subfocus + , SemiringRow rowlistTail row subrowTail subfocus , Semiring focus ) - => SemiringRecord (RL.Cons key focus rowlistTail) row subrow focus where - addRecordImpl _ ra rb - = unsafeInsert key - (unsafeGet' key ra + unsafeGet' key rb) - (addRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb) - where key = reflectSymbol (SProxy :: SProxy key) - unsafeGet' = unsafeGet :: String -> Record row -> focus - - mulRecordImpl _ ra rb - = unsafeInsert key - (unsafeGet' key ra * unsafeGet' key rb) - (mulRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb) + => SemiringRow (RL.Cons key focus rowlistTail) row subrow focus where + addRecordImpl _ ra rb = insert (get ra + get rb) tail where key = reflectSymbol (SProxy :: SProxy key) - unsafeGet' = unsafeGet :: String -> Record row -> focus - - oneRecordImpl _ _ - = unsafeInsert key (one :: focus) - ( oneRecordImpl - (RLProxy :: RLProxy rowlistTail) - (RProxy :: RProxy row) - ) + get = unsafeGet key :: Record row -> focus + tail = addRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb + insert = unsafeInsert key :: focus -> Record subrowTail -> Record subrow + + mulRecordImpl _ ra rb = insert (get ra * get rb) tail where key = reflectSymbol (SProxy :: SProxy key) - unsafeGet' = unsafeGet :: String -> Record row -> focus - - zeroRecordImpl _ _ - = unsafeInsert key (one :: focus) - ( zeroRecordImpl - (RLProxy :: RLProxy rowlistTail) - (RProxy :: RProxy row) - ) + get = unsafeGet key :: Record row -> focus + tail = mulRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb + insert = unsafeInsert key :: focus -> Record subrowTail -> Record subrow + + oneRecordImpl _ _ = insert one tail + where + key = reflectSymbol (SProxy :: SProxy key) + tail = oneRecordImpl (RLProxy :: RLProxy rowlistTail) (RProxy :: RProxy row) + insert = unsafeInsert key :: focus -> Record subrowTail -> Record subrow + + zeroRecordImpl _ _ = insert zero tail where key = reflectSymbol (SProxy :: SProxy key) - unsafeGet' = unsafeGet :: String -> Record row -> focus + tail = zeroRecordImpl (RLProxy :: RLProxy rowlistTail) (RProxy :: RProxy row) + insert = unsafeInsert key :: focus -> Record subrowTail -> Record subrow instance semiringRecord :: ( RL.RowToList row list - , SemiringRecord list row row focus + , SemiringRow list row row focus ) => Semiring (Record row) where - add = addRecordImpl (RLProxy :: RLProxy list) - mul = mulRecordImpl (RLProxy :: RLProxy list) - one = oneRecordImpl (RLProxy :: RLProxy list) (RProxy :: RProxy row) + add = addRecordImpl (RLProxy :: RLProxy list) + mul = mulRecordImpl (RLProxy :: RLProxy list) + one = oneRecordImpl (RLProxy :: RLProxy list) (RProxy :: RProxy row) zero = zeroRecordImpl (RLProxy :: RLProxy list) (RProxy :: RProxy row) diff --git a/src/Data/Show.purs b/src/Data/Show.purs index 7b28f7b4..c4b6e535 100644 --- a/src/Data/Show.purs +++ b/src/Data/Show.purs @@ -1,7 +1,13 @@ -module Data.Show (class Show, show) where +module Data.Show + ( class Show + , show + + , class ShowRowFields + , showRecordFieldsImpl + ) where import Data.Internal.Record (unsafeGet) -import Data.RowList (RLProxy(..)) +import Type.Data.RowList (RLProxy(..)) import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol) import Prim.RowList as RL @@ -33,33 +39,29 @@ instance showString :: Show String where instance showArray :: Show a => Show (Array a) where show = showArrayImpl show -class ShowRecordFields - (rowlist :: RL.RowList) - (row :: # Type) - focus - | rowlist -> focus where - showRecordFieldsImpl :: (RLProxy rowlist) -> Record row -> Array String +class ShowRowFields rowlist row focus | rowlist -> focus where + showRecordFieldsImpl :: RLProxy rowlist -> Record row -> Array String -instance showRecordFieldsNil - :: ShowRecordFields RL.Nil row focus where +instance showRowFieldsNil + :: ShowRowFields RL.Nil row focus where showRecordFieldsImpl _ _ = [] -instance showRecordFieldsCons +instance showRowFieldsCons :: ( IsSymbol key - , ShowRecordFields rowlistTail row subfocus + , ShowRowFields rowlistTail row subfocus , Show focus ) - => ShowRecordFields (RL.Cons key focus rowlistTail) row focus where - showRecordFieldsImpl _ record = cons - (join ": " [ key, show (unsafeGet' key record) ]) - (showRecordFieldsImpl (RLProxy :: RLProxy rowlistTail) record) + => ShowRowFields (RL.Cons key focus rowlistTail) row focus where + showRecordFieldsImpl _ record + = cons (join ": " [ key, show focus ]) tail where key = reflectSymbol (SProxy :: SProxy key) - unsafeGet' = unsafeGet :: String -> Record row -> focus + focus = unsafeGet key record :: focus + tail = showRecordFieldsImpl (RLProxy :: RLProxy rowlistTail) record instance showRecord :: ( RL.RowToList rs ls - , ShowRecordFields ls rs focus + , ShowRowFields ls rs focus ) => Show (Record rs) where show record = case showRecordFieldsImpl (RLProxy :: RLProxy ls) record of diff --git a/src/Data/RowList.purs b/src/Type/Data/RowList.purs similarity index 81% rename from src/Data/RowList.purs rename to src/Type/Data/RowList.purs index fc27768f..68e8aba6 100644 --- a/src/Data/RowList.purs +++ b/src/Type/Data/RowList.purs @@ -1,4 +1,4 @@ -module Data.RowList where +module Type.Data.RowList where import Prim.RowList (kind RowList) diff --git a/test/Test/Main.purs b/test/Test/Main.purs index 177d85f3..023dee74 100644 --- a/test/Test/Main.purs +++ b/test/Test/Main.purs @@ -2,6 +2,7 @@ module Test.Main where import Prelude import Data.EuclideanRing (intDiv, intMod) +import Data.HeytingAlgebra (ff, tt, implies) import Data.Ord (abs) type AlmostEff = Unit -> Unit @@ -14,6 +15,7 @@ main = do testIntDivMod testIntQuotRem testIntDegree + testRecordInstances foreign import testNumberShow :: (Number -> String) -> AlmostEff foreign import throwErr :: String -> AlmostEff @@ -143,3 +145,31 @@ testIntDegree = do assert "degree returns absolute integers" $ degree 4 == 4 assert "degree returns absolute integers" $ degree bot >= 0 assert "degree does not return out-of-bounds integers" $ degree bot <= top + +testRecordInstances :: AlmostEff +testRecordInstances = do + assert "Record equality" $ { a: 1 } == { a: 1 } + assert "Record inequality" $ { a: 2 } == { a: 1 } + assert "Record show" $ show { a: 1 } == "{ a: 1 }" + assert "Record +" $ ({ a: 1, b: 2.0 } + { a: 0, b: (-2.0) }) == { a: 1, b: 0.0 } + assert "Record *" $ ({ a: 1, b: 2.0 } * { a: 0, b: (-2.0) }) == { a: 0, b: -4.0 } + assert "Record one" $ one == { a: 1, b: 1.0 } + assert "Record zero" $ zero == { a: 0, b: 0.0 } + assert "Record sub" $ { a: 2, b: 2.0 } - { a: 1, b: 1.0 } == { a: 1, b: 1.0 } + assert "Record append" $ { a: [], b: "T" } <> { a: [1], b: "OM" } == { a: [1], b: "TOM" } + assert "Record mempty" $ mempty == { a: [] :: Array Int, b: "" } + assert "Record ff" $ ff == { a: false, b: false } + assert "Record tt" $ tt == { a: true, b: true } + assert "Record not" $ not { a: true, b: false } == { a: false, b: true } + assert "Record conj" $ conj + { a: true, b: false, c: true, d: false } + { a: true, b: true, c: false, d: false } + == ff { a: true, b: false, c: false, d: false } + assert "Record disj" $ disj + { a: true, b: false, c: true, d: false } + { a: true, b: true, c: false, d: false } + == ff { a: true, b: true, c: true, d: false } + assert "Record implies" $ implies + { a: true, b: false, c: true, d: false } + { a: true, b: true, c: false, d: false } + == ff { a: true, b: true, c: false, d: false } From 434a877a79960f51796a83a194b4c503552be1cb Mon Sep 17 00:00:00 2001 From: Liam Goodacre Date: Tue, 1 May 2018 10:42:58 +0100 Subject: [PATCH 17/23] Fix record instance tests --- src/Data/HeytingAlgebra.purs | 4 ++-- test/Test/Main.purs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Data/HeytingAlgebra.purs b/src/Data/HeytingAlgebra.purs index b8860e06..f20b5283 100644 --- a/src/Data/HeytingAlgebra.purs +++ b/src/Data/HeytingAlgebra.purs @@ -163,6 +163,6 @@ instance heytingAlgebraRecord ff = ffRecordImpl (RLProxy :: RLProxy list) (RProxy :: RProxy row) tt = ttRecordImpl (RLProxy :: RLProxy list) (RProxy :: RProxy row) conj = conjRecordImpl (RLProxy :: RLProxy list) - disj = conjRecordImpl (RLProxy :: RLProxy list) - implies = conjRecordImpl (RLProxy :: RLProxy list) + disj = disjRecordImpl (RLProxy :: RLProxy list) + implies = impliesRecordImpl (RLProxy :: RLProxy list) not = notRecordImpl (RLProxy :: RLProxy list) diff --git a/test/Test/Main.purs b/test/Test/Main.purs index 023dee74..052ca88b 100644 --- a/test/Test/Main.purs +++ b/test/Test/Main.purs @@ -149,7 +149,7 @@ testIntDegree = do testRecordInstances :: AlmostEff testRecordInstances = do assert "Record equality" $ { a: 1 } == { a: 1 } - assert "Record inequality" $ { a: 2 } == { a: 1 } + assert "Record inequality" $ { a: 2 } /= { a: 1 } assert "Record show" $ show { a: 1 } == "{ a: 1 }" assert "Record +" $ ({ a: 1, b: 2.0 } + { a: 0, b: (-2.0) }) == { a: 1, b: 0.0 } assert "Record *" $ ({ a: 1, b: 2.0 } * { a: 0, b: (-2.0) }) == { a: 0, b: -4.0 } @@ -164,12 +164,12 @@ testRecordInstances = do assert "Record conj" $ conj { a: true, b: false, c: true, d: false } { a: true, b: true, c: false, d: false } - == ff { a: true, b: false, c: false, d: false } + == { a: true, b: false, c: false, d: false } assert "Record disj" $ disj { a: true, b: false, c: true, d: false } { a: true, b: true, c: false, d: false } - == ff { a: true, b: true, c: true, d: false } + == { a: true, b: true, c: true, d: false } assert "Record implies" $ implies { a: true, b: false, c: true, d: false } { a: true, b: true, c: false, d: false } - == ff { a: true, b: true, c: false, d: false } + == { a: true, b: true, c: false, d: true } From c524f9ebfa498e8fcbd28696dc3626af25b1beb2 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Thu, 3 May 2018 14:59:52 +0100 Subject: [PATCH 18/23] Further work on record instances --- src/Data/BooleanAlgebra.purs | 24 +++++-- src/Data/CommutativeRing.purs | 27 +++++--- src/Data/Eq.purs | 37 +++++------ src/Data/HeytingAlgebra.purs | 118 ++++++++++++++-------------------- src/Data/Internal/Record.js | 14 ---- src/Data/Internal/Record.purs | 14 ---- src/Data/Monoid.purs | 61 ++++++++---------- src/Data/Ring.purs | 43 ++++++------- src/Data/Semigroup.purs | 42 ++++++------ src/Data/Semiring.purs | 94 ++++++++++++--------------- src/Data/Show.purs | 46 ++++++------- src/Record/Unsafe.js | 40 ++++++++++++ src/Record/Unsafe.purs | 27 ++++++++ 13 files changed, 295 insertions(+), 292 deletions(-) delete mode 100644 src/Data/Internal/Record.js delete mode 100644 src/Data/Internal/Record.purs create mode 100644 src/Record/Unsafe.js create mode 100644 src/Record/Unsafe.purs diff --git a/src/Data/BooleanAlgebra.purs b/src/Data/BooleanAlgebra.purs index d5b2ced0..44a6c184 100644 --- a/src/Data/BooleanAlgebra.purs +++ b/src/Data/BooleanAlgebra.purs @@ -1,10 +1,13 @@ module Data.BooleanAlgebra ( class BooleanAlgebra , module Data.HeytingAlgebra + , class BooleanAlgebraRecord ) where -import Data.HeytingAlgebra (class HeytingAlgebra, class HeytingAlgebraRow, ff, tt, implies, conj, disj, not, (&&), (||)) +import Data.HeytingAlgebra (class HeytingAlgebra, class HeytingAlgebraRecord, ff, tt, implies, conj, disj, not, (&&), (||)) +import Data.Symbol (class IsSymbol) import Data.Unit (Unit) +import Prim.Row as Row import Prim.RowList as RL -- | The `BooleanAlgebra` type class represents types that behave like boolean @@ -20,9 +23,18 @@ class HeytingAlgebra a <= BooleanAlgebra a instance booleanAlgebraBoolean :: BooleanAlgebra Boolean instance booleanAlgebraUnit :: BooleanAlgebra Unit instance booleanAlgebraFn :: BooleanAlgebra b => BooleanAlgebra (a -> b) +instance booleanAlgebraRecord :: (RL.RowToList row list, BooleanAlgebraRecord list row row) => BooleanAlgebra (Record row) -instance booleanAlgebraRecord - :: ( RL.RowToList row list - , HeytingAlgebraRow list row row focus - ) - => BooleanAlgebra (Record row) +-- | A class for records where all fields have `BooleanAlgebra` instances, used +-- | to implement the `BooleanAlgebra` instance for records. +class HeytingAlgebraRecord rowlist row subrow <= BooleanAlgebraRecord rowlist row subrow | rowlist -> subrow + +instance booleanAlgebraRecordNil :: BooleanAlgebraRecord RL.Nil row () + +instance booleanAlgebraRecordCons + :: ( IsSymbol key + , Row.Cons key focus subrowTail subrow + , BooleanAlgebraRecord rowlistTail row subrowTail + , BooleanAlgebra focus + ) + => BooleanAlgebraRecord (RL.Cons key focus rowlistTail) row subrow diff --git a/src/Data/CommutativeRing.purs b/src/Data/CommutativeRing.purs index 2f90a61e..6fcf498f 100644 --- a/src/Data/CommutativeRing.purs +++ b/src/Data/CommutativeRing.purs @@ -2,11 +2,14 @@ module Data.CommutativeRing ( class CommutativeRing , module Data.Ring , module Data.Semiring + , class CommutativeRingRecord ) where -import Data.Ring (class Ring, class RingRow) -import Data.Semiring (class Semiring, class SemiringRow, add, mul, one, zero, (*), (+)) +import Data.Ring (class Ring, class RingRecord) +import Data.Semiring (class Semiring, add, mul, one, zero, (*), (+)) +import Data.Symbol (class IsSymbol) import Data.Unit (Unit) +import Prim.Row as Row import Prim.RowList as RL -- | The `CommutativeRing` class is for rings where multiplication is @@ -22,10 +25,18 @@ instance commutativeRingInt :: CommutativeRing Int instance commutativeRingNumber :: CommutativeRing Number instance commutativeRingUnit :: CommutativeRing Unit instance commutativeRingFn :: CommutativeRing b => CommutativeRing (a -> b) +instance commutativeRingRecord :: (RL.RowToList row list, CommutativeRingRecord list row row) => CommutativeRing (Record row) -instance commutativeRingRecord - :: ( RL.RowToList row list - , SemiringRow list row row focus - , RingRow list row row focus - ) - => CommutativeRing (Record row) +-- | A class for records where all fields have `CommutativeRing` instances, used +-- | to implement the `CommutativeRing` instance for records. +class RingRecord rowlist row subrow <= CommutativeRingRecord rowlist row subrow | rowlist -> subrow + +instance commutativeRingRecordNil :: CommutativeRingRecord RL.Nil row () + +instance commutativeRingRecordCons + :: ( IsSymbol key + , Row.Cons key focus subrowTail subrow + , CommutativeRingRecord rowlistTail row subrowTail + , CommutativeRing focus + ) + => CommutativeRingRecord (RL.Cons key focus rowlistTail) row subrow diff --git a/src/Data/Eq.purs b/src/Data/Eq.purs index 08e8cd3f..9c193cea 100644 --- a/src/Data/Eq.purs +++ b/src/Data/Eq.purs @@ -1,19 +1,17 @@ module Data.Eq ( class Eq, eq, (==), notEq, (/=) , class Eq1, eq1, notEq1 - - , class EqRow - , eqRecordImpl + , class EqRecord, eqRecord ) where import Data.HeytingAlgebra ((&&)) -import Data.Internal.Record (unsafeGet) -import Type.Data.RowList (RLProxy(..)) import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol) import Data.Unit (Unit) import Data.Void (Void) import Prim.Row as Row import Prim.RowList as RL +import Record.Unsafe (unsafeGet) +import Type.Data.RowList (RLProxy(..)) -- | The `Eq` type class represents types which support decidable equality. -- | @@ -63,6 +61,9 @@ instance eqVoid :: Eq Void where instance eqArray :: Eq a => Eq (Array a) where eq = eqArrayImpl eq +instance eqRec :: (RL.RowToList row list, EqRecord list row) => Eq (Record row) where + eq = eqRecord (RLProxy :: RLProxy list) + foreign import refEq :: forall a. a -> a -> Boolean foreign import eqArrayImpl :: forall a. (a -> a -> Boolean) -> Array a -> Array a -> Boolean @@ -76,29 +77,23 @@ instance eq1Array :: Eq1 Array where notEq1 :: forall f a. Eq1 f => Eq a => f a -> f a -> Boolean notEq1 x y = (x `eq1` y) == false --- | A typeclass to characterise rows of types that are all Eq.. -class EqRow rowlist row focus | rowlist -> focus where - eqRecordImpl :: RLProxy rowlist -> Record row -> Record row -> Boolean +-- | A class for records where all fields have `Eq` instances, used to implement +-- | the `Eq` instance for records. +class EqRecord rowlist row where + eqRecord :: RLProxy rowlist -> Record row -> Record row -> Boolean -instance eqRowNil :: EqRow RL.Nil row focus where - eqRecordImpl _ _ _ = true +instance eqRowNil :: EqRecord RL.Nil row where + eqRecord _ _ _ = true instance eqRowCons - :: ( EqRow rowlistTail row subfocus + :: ( EqRecord rowlistTail row , Row.Cons key focus rowTail row , IsSymbol key , Eq focus ) - => EqRow (RL.Cons key focus rowlistTail) row focus where - eqRecordImpl _ ra rb = (get ra == get rb) && tail + => EqRecord (RL.Cons key focus rowlistTail) row where + eqRecord _ ra rb = (get ra == get rb) && tail where key = reflectSymbol (SProxy :: SProxy key) get = unsafeGet key :: Record row -> focus - tail = eqRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb - -instance eqRecord - :: ( RL.RowToList row list - , EqRow list row focus - ) - => Eq (Record row) where - eq = eqRecordImpl (RLProxy :: RLProxy list) + tail = eqRecord (RLProxy :: RLProxy rowlistTail) ra rb diff --git a/src/Data/HeytingAlgebra.purs b/src/Data/HeytingAlgebra.purs index f20b5283..5b6920f1 100644 --- a/src/Data/HeytingAlgebra.purs +++ b/src/Data/HeytingAlgebra.purs @@ -1,29 +1,13 @@ module Data.HeytingAlgebra - ( class HeytingAlgebra - - , tt - , ff - , implies - , conj - , disj - , not - , (&&) - , (||) - - , class HeytingAlgebraRow - , ffRecordImpl - , ttRecordImpl - , impliesRecordImpl - , conjRecordImpl - , disjRecordImpl - , notRecordImpl + ( class HeytingAlgebra, tt, ff, implies, conj, disj, not, (&&), (||) + , class HeytingAlgebraRecord, ffRecord, ttRecord, impliesRecord, conjRecord, disjRecord, notRecord ) where -import Data.Internal.Record (unsafeGet, unsafeInsert) import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol) import Data.Unit (Unit, unit) import Prim.Row as Row import Prim.RowList as RL +import Record.Unsafe (unsafeGet, unsafeSet) import Type.Data.Row (RProxy(..)) import Type.Data.RowList (RLProxy(..)) @@ -87,82 +71,80 @@ instance heytingAlgebraFunction :: HeytingAlgebra b => HeytingAlgebra (a -> b) w disj f g a = f a || g a not f a = not (f a) +instance heytingAlgebraRecord :: (RL.RowToList row list, HeytingAlgebraRecord list row row) => HeytingAlgebra (Record row) where + ff = ffRecord (RLProxy :: RLProxy list) (RProxy :: RProxy row) + tt = ttRecord (RLProxy :: RLProxy list) (RProxy :: RProxy row) + conj = conjRecord (RLProxy :: RLProxy list) + disj = disjRecord (RLProxy :: RLProxy list) + implies = impliesRecord (RLProxy :: RLProxy list) + not = notRecord (RLProxy :: RLProxy list) + foreign import boolConj :: Boolean -> Boolean -> Boolean foreign import boolDisj :: Boolean -> Boolean -> Boolean foreign import boolNot :: Boolean -> Boolean -class HeytingAlgebraRow rowlist row subrow focus | rowlist -> subrow focus where - ffRecordImpl :: RLProxy rowlist -> RProxy row -> Record subrow - ttRecordImpl :: RLProxy rowlist -> RProxy row -> Record subrow - impliesRecordImpl :: RLProxy rowlist -> Record row -> Record row -> Record subrow - disjRecordImpl :: RLProxy rowlist -> Record row -> Record row -> Record subrow - conjRecordImpl :: RLProxy rowlist -> Record row -> Record row -> Record subrow - notRecordImpl :: RLProxy rowlist -> Record row -> Record subrow - -instance heytingAlgebraRowNil :: HeytingAlgebraRow RL.Nil row () focus where - conjRecordImpl _ _ _ = {} - disjRecordImpl _ _ _ = {} - ffRecordImpl _ _ = {} - impliesRecordImpl _ _ _ = {} - notRecordImpl _ _ = {} - ttRecordImpl _ _ = {} - -instance heytingAlgebraRowCons +-- | A class for records where all fields have `HeytingAlgebra` instances, used +-- | to implement the `HeytingAlgebra` instance for records. +class HeytingAlgebraRecord rowlist row subrow | rowlist -> subrow where + ffRecord :: RLProxy rowlist -> RProxy row -> Record subrow + ttRecord :: RLProxy rowlist -> RProxy row -> Record subrow + impliesRecord :: RLProxy rowlist -> Record row -> Record row -> Record subrow + disjRecord :: RLProxy rowlist -> Record row -> Record row -> Record subrow + conjRecord :: RLProxy rowlist -> Record row -> Record row -> Record subrow + notRecord :: RLProxy rowlist -> Record row -> Record subrow + +instance heytingAlgebraRecordNil :: HeytingAlgebraRecord RL.Nil row () where + conjRecord _ _ _ = {} + disjRecord _ _ _ = {} + ffRecord _ _ = {} + impliesRecord _ _ _ = {} + notRecord _ _ = {} + ttRecord _ _ = {} + +instance heytingAlgebraRecordCons :: ( IsSymbol key , Row.Cons key focus subrowTail subrow - , HeytingAlgebraRow rowlistTail row subrowTail subfocus + , HeytingAlgebraRecord rowlistTail row subrowTail , HeytingAlgebra focus ) - => HeytingAlgebraRow (RL.Cons key focus rowlistTail) row subrow focus where - conjRecordImpl _ ra rb = insert (conj (get ra) (get rb)) tail + => HeytingAlgebraRecord (RL.Cons key focus rowlistTail) row subrow where + conjRecord _ ra rb = insert (conj (get ra) (get rb)) tail where key = reflectSymbol (SProxy :: SProxy key) get = unsafeGet key :: Record row -> focus - insert = unsafeInsert key :: focus -> Record subrowTail -> Record subrow - tail = conjRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb + insert = unsafeSet key :: focus -> Record subrowTail -> Record subrow + tail = conjRecord (RLProxy :: RLProxy rowlistTail) ra rb - disjRecordImpl _ ra rb = insert (disj (get ra) (get rb)) tail + disjRecord _ ra rb = insert (disj (get ra) (get rb)) tail where key = reflectSymbol (SProxy :: SProxy key) get = unsafeGet key :: Record row -> focus - insert = unsafeInsert key :: focus -> Record subrowTail -> Record subrow - tail = disjRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb + insert = unsafeSet key :: focus -> Record subrowTail -> Record subrow + tail = disjRecord (RLProxy :: RLProxy rowlistTail) ra rb - impliesRecordImpl _ ra rb = insert (implies (get ra) (get rb)) tail + impliesRecord _ ra rb = insert (implies (get ra) (get rb)) tail where key = reflectSymbol (SProxy :: SProxy key) get = unsafeGet key :: Record row -> focus - insert = unsafeInsert key :: focus -> Record subrowTail -> Record subrow - tail = impliesRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb + insert = unsafeSet key :: focus -> Record subrowTail -> Record subrow + tail = impliesRecord (RLProxy :: RLProxy rowlistTail) ra rb - ffRecordImpl _ row = insert ff tail + ffRecord _ row = insert ff tail where key = reflectSymbol (SProxy :: SProxy key) - insert = unsafeInsert key :: focus -> Record subrowTail -> Record subrow - tail = ffRecordImpl (RLProxy :: RLProxy rowlistTail) row + insert = unsafeSet key :: focus -> Record subrowTail -> Record subrow + tail = ffRecord (RLProxy :: RLProxy rowlistTail) row - notRecordImpl _ row + notRecord _ row = insert (not (get row)) tail where key = reflectSymbol (SProxy :: SProxy key) get = unsafeGet key :: Record row -> focus - insert = unsafeInsert key :: focus -> Record subrowTail -> Record subrow - tail = notRecordImpl (RLProxy :: RLProxy rowlistTail) row + insert = unsafeSet key :: focus -> Record subrowTail -> Record subrow + tail = notRecord (RLProxy :: RLProxy rowlistTail) row - ttRecordImpl _ row = insert tt tail + ttRecord _ row = insert tt tail where key = reflectSymbol (SProxy :: SProxy key) - insert = unsafeInsert key :: focus -> Record subrowTail -> Record subrow - tail = ttRecordImpl (RLProxy :: RLProxy rowlistTail) row - -instance heytingAlgebraRecord - :: ( RL.RowToList row list - , HeytingAlgebraRow list row row focus - ) - => HeytingAlgebra (Record row) where - ff = ffRecordImpl (RLProxy :: RLProxy list) (RProxy :: RProxy row) - tt = ttRecordImpl (RLProxy :: RLProxy list) (RProxy :: RProxy row) - conj = conjRecordImpl (RLProxy :: RLProxy list) - disj = disjRecordImpl (RLProxy :: RLProxy list) - implies = impliesRecordImpl (RLProxy :: RLProxy list) - not = notRecordImpl (RLProxy :: RLProxy list) + insert = unsafeSet key :: focus -> Record subrowTail -> Record subrow + tail = ttRecord (RLProxy :: RLProxy rowlistTail) row diff --git a/src/Data/Internal/Record.js b/src/Data/Internal/Record.js deleted file mode 100644 index c4a827d4..00000000 --- a/src/Data/Internal/Record.js +++ /dev/null @@ -1,14 +0,0 @@ -exports.unsafeGet = function (key) { - return function (xs) { - return xs[key]; - }; -}; - -exports.unsafeInsert = function (key) { - return function (value) { - return function (xs) { - xs[key] = value; - return xs; - }; - }; -}; diff --git a/src/Data/Internal/Record.purs b/src/Data/Internal/Record.purs deleted file mode 100644 index 09790590..00000000 --- a/src/Data/Internal/Record.purs +++ /dev/null @@ -1,14 +0,0 @@ -module Data.Internal.Record where - --- | *Really* unsafely get a value from a record. You really shouldn't be using --- | this function unless you know what you're doing. -foreign import unsafeGet :: forall a rs. String -> Record rs -> a - --- | *Really* unsafely insert a value into a record. Again, you really --- | shouldn't use this function. -foreign import unsafeInsert - :: forall a ra rb - . String - -> a - -> Record ra - -> Record rb diff --git a/src/Data/Monoid.purs b/src/Data/Monoid.purs index 8775468c..ba496e9e 100644 --- a/src/Data/Monoid.purs +++ b/src/Data/Monoid.purs @@ -3,23 +3,21 @@ module Data.Monoid , power , guard , module Data.Semigroup - - , class MonoidRow - , memptyRecordImpl + , class MonoidRecord, memptyRecord ) where import Data.Boolean (otherwise) import Data.Eq ((==)) import Data.EuclideanRing (mod, (/)) -import Data.Internal.Record (unsafeInsert) import Data.Ord ((<=)) import Data.Ordering (Ordering(..)) -import Type.Data.RowList (RLProxy(..)) -import Data.Semigroup (class Semigroup, class SemigroupRow, (<>)) +import Data.Semigroup (class Semigroup, class SemigroupRecord, (<>)) import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol) import Data.Unit (Unit, unit) import Prim.Row as Row import Prim.RowList as RL +import Record.Unsafe (unsafeSet) +import Type.Data.RowList (RLProxy(..)) -- | A `Monoid` is a `Semigroup` with a value `mempty`, which is both a -- | left and right unit for the associative operation `<>`: @@ -49,33 +47,8 @@ instance monoidString :: Monoid String where instance monoidArray :: Monoid (Array a) where mempty = [] -class MonoidRow rowlist subrow focus | rowlist -> subrow focus where - memptyRecordImpl :: RLProxy rowlist -> Record subrow - -instance monoidRowNil :: MonoidRow RL.Nil () focus where - memptyRecordImpl _ = {} - -instance monoidRowCons - :: ( IsSymbol key - , Monoid focus - , Row.Cons key focus subrowTail subrow - , MonoidRow rowlistTail subrowTail subfocus - ) - => MonoidRow (RL.Cons key focus rowlistTail) subrow focus where - memptyRecordImpl _ - = insert mempty tail - where - key = reflectSymbol (SProxy :: SProxy key) - insert = unsafeInsert key :: focus -> Record subrowTail -> Record subrow - tail = memptyRecordImpl (RLProxy :: RLProxy rowlistTail) - -instance monoidRecord - :: ( RL.RowToList row list - , SemigroupRow list row row focus - , MonoidRow list row focus - ) - => Monoid (Record row) where - mempty = memptyRecordImpl (RLProxy :: RLProxy list) +instance monoidRecord :: (RL.RowToList row list, MonoidRecord list row row) => Monoid (Record row) where + mempty = memptyRecord (RLProxy :: RLProxy list) -- | Append a value to itself a certain number of times. For the -- | `Multiplicative` type, and for a non-negative power, this is the same as @@ -101,3 +74,25 @@ power x = go guard :: forall m. Monoid m => Boolean -> m -> m guard true a = a guard false _ = mempty + +-- | A class for records where all fields have `Monoid` instances, used to +-- | implement the `Monoid` instance for records. +class SemigroupRecord rowlist row subrow <= MonoidRecord rowlist row subrow | rowlist -> row subrow where + memptyRecord :: RLProxy rowlist -> Record subrow + +instance monoidRecordNil :: MonoidRecord RL.Nil row () where + memptyRecord _ = {} + +instance monoidRecordCons + :: ( IsSymbol key + , Monoid focus + , Row.Cons key focus subrowTail subrow + , MonoidRecord rowlistTail row subrowTail + ) + => MonoidRecord (RL.Cons key focus rowlistTail) row subrow where + memptyRecord _ + = insert mempty tail + where + key = reflectSymbol (SProxy :: SProxy key) + insert = unsafeSet key :: focus -> Record subrowTail -> Record subrow + tail = memptyRecord (RLProxy :: RLProxy rowlistTail) diff --git a/src/Data/Ring.purs b/src/Data/Ring.purs index 17d3a0f6..1d5ab234 100644 --- a/src/Data/Ring.purs +++ b/src/Data/Ring.purs @@ -1,18 +1,16 @@ module Data.Ring ( class Ring, sub, negate, (-) , module Data.Semiring - - , class RingRow - , subRecordImpl + , class RingRecord, subRecord ) where -import Data.Internal.Record (unsafeGet, unsafeInsert) -import Type.Data.RowList (RLProxy(..)) -import Data.Semiring (class Semiring, class SemiringRow, add, mul, one, zero, (*), (+)) +import Data.Semiring (class Semiring, class SemiringRecord, add, mul, one, zero, (*), (+)) import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol) import Data.Unit (Unit, unit) import Prim.Row as Row import Prim.RowList as RL +import Record.Unsafe (unsafeGet, unsafeSet) +import Type.Data.RowList (RLProxy(..)) -- | The `Ring` class is for types that support addition, multiplication, -- | and subtraction operations. @@ -38,6 +36,9 @@ instance ringUnit :: Ring Unit where instance ringFn :: Ring b => Ring (a -> b) where sub f g x = f x - g x +instance ringRecord :: (RL.RowToList row list, RingRecord list row row) => Ring (Record row) where + sub = subRecord (RLProxy :: RLProxy list) + -- | `negate x` can be used as a shorthand for `zero - x`. negate :: forall a. Ring a => a -> a negate a = zero - a @@ -45,30 +46,24 @@ negate a = zero - a foreign import intSub :: Int -> Int -> Int foreign import numSub :: Number -> Number -> Number -class RingRow rowlist row subrow focus | rowlist -> subrow focus where - subRecordImpl :: RLProxy rowlist -> Record row -> Record row -> Record subrow +-- | A class for records where all fields have `Ring` instances, used to +-- | implement the `Ring` instance for records. +class SemiringRecord rowlist row subrow <= RingRecord rowlist row subrow | rowlist -> subrow where + subRecord :: RLProxy rowlist -> Record row -> Record row -> Record subrow -instance ringRowNil :: RingRow RL.Nil row () focus where - subRecordImpl _ _ _ = {} +instance ringRecordNil :: RingRecord RL.Nil row () where + subRecord _ _ _ = {} -instance ringRowCons +instance ringRecordCons :: ( IsSymbol key , Row.Cons key focus subrowTail subrow - , RingRow rowlistTail row subrowTail subfocus + , RingRecord rowlistTail row subrowTail , Ring focus ) - => RingRow (RL.Cons key focus rowlistTail) row subrow focus where - subRecordImpl _ ra rb = insert (get ra - get rb) tail + => RingRecord (RL.Cons key focus rowlistTail) row subrow where + subRecord _ ra rb = insert (get ra - get rb) tail where - insert = unsafeInsert key :: focus -> Record subrowTail -> Record subrow + insert = unsafeSet key :: focus -> Record subrowTail -> Record subrow key = reflectSymbol (SProxy :: SProxy key) get = unsafeGet key :: Record row -> focus - tail = subRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb - -instance ringRecord - :: ( RL.RowToList row list - , SemiringRow list row row focus - , RingRow list row row focus - ) - => Ring (Record row) where - sub = subRecordImpl (RLProxy :: RLProxy list) + tail = subRecord (RLProxy :: RLProxy rowlistTail) ra rb diff --git a/src/Data/Semigroup.purs b/src/Data/Semigroup.purs index 917da3fa..db5bed20 100644 --- a/src/Data/Semigroup.purs +++ b/src/Data/Semigroup.purs @@ -1,19 +1,15 @@ module Data.Semigroup - ( class Semigroup - , append - , (<>) - - , class SemigroupRow - , appendRecordImpl + ( class Semigroup, append, (<>) + , class SemigroupRecord, appendRecord ) where -import Data.Internal.Record (unsafeGet, unsafeInsert) -import Type.Data.RowList (RLProxy(..)) import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol) import Data.Unit (Unit, unit) import Data.Void (Void, absurd) import Prim.Row as Row import Prim.RowList as RL +import Record.Unsafe (unsafeGet, unsafeSet) +import Type.Data.RowList (RLProxy(..)) -- | The `Semigroup` type class identifies an associative operation on a type. -- | @@ -43,32 +39,30 @@ instance semigroupFn :: Semigroup s' => Semigroup (s -> s') where instance semigroupArray :: Semigroup (Array a) where append = concatArray +instance semigroupRecord :: (RL.RowToList row list, SemigroupRecord list row row) => Semigroup (Record row) where + append = appendRecord (RLProxy :: RLProxy list) + foreign import concatString :: String -> String -> String foreign import concatArray :: forall a. Array a -> Array a -> Array a -class SemigroupRow rowlist row subrow focus | rowlist -> subrow focus where - appendRecordImpl :: RLProxy rowlist -> Record row -> Record row -> Record subrow +-- | A class for records where all fields have `Semigroup` instances, used to +-- | implement the `Semigroup` instance for records. +class SemigroupRecord rowlist row subrow | rowlist -> subrow where + appendRecord :: RLProxy rowlist -> Record row -> Record row -> Record subrow -instance semigroupRowNil :: SemigroupRow RL.Nil row () focus where - appendRecordImpl _ _ _ = {} +instance semigroupRecordNil :: SemigroupRecord RL.Nil row () where + appendRecord _ _ _ = {} instance semigroupRecordCons :: ( IsSymbol key , Row.Cons key focus subrowTail subrow - , SemigroupRow rowlistTail row subrowTail subfocus + , SemigroupRecord rowlistTail row subrowTail , Semigroup focus ) - => SemigroupRow (RL.Cons key focus rowlistTail) row subrow focus where - appendRecordImpl _ ra rb = insert (get ra <> get rb) tail + => SemigroupRecord (RL.Cons key focus rowlistTail) row subrow where + appendRecord _ ra rb = insert (get ra <> get rb) tail where key = reflectSymbol (SProxy :: SProxy key) get = unsafeGet key :: Record row -> focus - insert = unsafeInsert key :: focus -> Record subrowTail -> Record subrow - tail = appendRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb - -instance semigroupRow - :: ( RL.RowToList row list - , SemigroupRow list row row focus - ) - => Semigroup (Record row) where - append = appendRecordImpl (RLProxy :: RLProxy list) + insert = unsafeSet key :: focus -> Record subrowTail -> Record subrow + tail = appendRecord (RLProxy :: RLProxy rowlistTail) ra rb diff --git a/src/Data/Semiring.purs b/src/Data/Semiring.purs index dbc5ff0d..e221cf3d 100644 --- a/src/Data/Semiring.purs +++ b/src/Data/Semiring.purs @@ -1,26 +1,15 @@ module Data.Semiring - ( class Semiring - , add - , (+) - , zero - , mul - , (*) - , one - - , class SemiringRow - , addRecordImpl - , mulRecordImpl - , oneRecordImpl - , zeroRecordImpl + ( class Semiring, add, (+), zero, mul, (*), one + , class SemiringRecord, addRecord, mulRecord, oneRecord, zeroRecord ) where -import Data.Internal.Record (unsafeGet, unsafeInsert) -import Type.Data.RowList (RLProxy(..)) import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol) -import Type.Data.Row (RProxy(..)) import Data.Unit (Unit, unit) import Prim.Row as Row import Prim.RowList as RL +import Record.Unsafe (unsafeGet, unsafeSet) +import Type.Data.Row (RProxy(..)) +import Type.Data.RowList (RLProxy(..)) -- | The `Semiring` class is for types that support an addition and -- | multiplication operation. @@ -76,63 +65,60 @@ instance semiringUnit :: Semiring Unit where mul _ _ = unit one = unit +instance semiringRecord :: (RL.RowToList row list, SemiringRecord list row row) => Semiring (Record row) where + add = addRecord (RLProxy :: RLProxy list) + mul = mulRecord (RLProxy :: RLProxy list) + one = oneRecord (RLProxy :: RLProxy list) (RProxy :: RProxy row) + zero = zeroRecord (RLProxy :: RLProxy list) (RProxy :: RProxy row) + foreign import intAdd :: Int -> Int -> Int foreign import intMul :: Int -> Int -> Int foreign import numAdd :: Number -> Number -> Number foreign import numMul :: Number -> Number -> Number --- | A type class to characterise row types in which all members are Semiring. -class SemiringRow rowlist row subrow focus | rowlist -> subrow focus where - addRecordImpl :: RLProxy rowlist -> Record row -> Record row -> Record subrow - mulRecordImpl :: RLProxy rowlist -> Record row -> Record row -> Record subrow - oneRecordImpl :: RLProxy rowlist -> RProxy row -> Record subrow - zeroRecordImpl :: RLProxy rowlist -> RProxy row -> Record subrow - -instance semiringRowNil :: SemiringRow RL.Nil row () focus where - addRecordImpl _ _ _ = {} - mulRecordImpl _ _ _ = {} - oneRecordImpl _ _ = {} - zeroRecordImpl _ _ = {} - -instance semiringRowCons +-- | A class for records where all fields have `Semiring` instances, used to +-- | implement the `Semiring` instance for records. +class SemiringRecord rowlist row subrow | rowlist -> subrow where + addRecord :: RLProxy rowlist -> Record row -> Record row -> Record subrow + mulRecord :: RLProxy rowlist -> Record row -> Record row -> Record subrow + oneRecord :: RLProxy rowlist -> RProxy row -> Record subrow + zeroRecord :: RLProxy rowlist -> RProxy row -> Record subrow + +instance semiringRecordNil :: SemiringRecord RL.Nil row () where + addRecord _ _ _ = {} + mulRecord _ _ _ = {} + oneRecord _ _ = {} + zeroRecord _ _ = {} + +instance semiringRecordCons :: ( IsSymbol key , Row.Cons key focus subrowTail subrow - , SemiringRow rowlistTail row subrowTail subfocus + , SemiringRecord rowlistTail row subrowTail , Semiring focus ) - => SemiringRow (RL.Cons key focus rowlistTail) row subrow focus where - addRecordImpl _ ra rb = insert (get ra + get rb) tail + => SemiringRecord (RL.Cons key focus rowlistTail) row subrow where + addRecord _ ra rb = insert (get ra + get rb) tail where key = reflectSymbol (SProxy :: SProxy key) get = unsafeGet key :: Record row -> focus - tail = addRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb - insert = unsafeInsert key :: focus -> Record subrowTail -> Record subrow + tail = addRecord (RLProxy :: RLProxy rowlistTail) ra rb + insert = unsafeSet key :: focus -> Record subrowTail -> Record subrow - mulRecordImpl _ ra rb = insert (get ra * get rb) tail + mulRecord _ ra rb = insert (get ra * get rb) tail where key = reflectSymbol (SProxy :: SProxy key) get = unsafeGet key :: Record row -> focus - tail = mulRecordImpl (RLProxy :: RLProxy rowlistTail) ra rb - insert = unsafeInsert key :: focus -> Record subrowTail -> Record subrow + tail = mulRecord (RLProxy :: RLProxy rowlistTail) ra rb + insert = unsafeSet key :: focus -> Record subrowTail -> Record subrow - oneRecordImpl _ _ = insert one tail + oneRecord _ _ = insert one tail where key = reflectSymbol (SProxy :: SProxy key) - tail = oneRecordImpl (RLProxy :: RLProxy rowlistTail) (RProxy :: RProxy row) - insert = unsafeInsert key :: focus -> Record subrowTail -> Record subrow + tail = oneRecord (RLProxy :: RLProxy rowlistTail) (RProxy :: RProxy row) + insert = unsafeSet key :: focus -> Record subrowTail -> Record subrow - zeroRecordImpl _ _ = insert zero tail + zeroRecord _ _ = insert zero tail where key = reflectSymbol (SProxy :: SProxy key) - tail = zeroRecordImpl (RLProxy :: RLProxy rowlistTail) (RProxy :: RProxy row) - insert = unsafeInsert key :: focus -> Record subrowTail -> Record subrow - -instance semiringRecord - :: ( RL.RowToList row list - , SemiringRow list row row focus - ) - => Semiring (Record row) where - add = addRecordImpl (RLProxy :: RLProxy list) - mul = mulRecordImpl (RLProxy :: RLProxy list) - one = oneRecordImpl (RLProxy :: RLProxy list) (RProxy :: RProxy row) - zero = zeroRecordImpl (RLProxy :: RLProxy list) (RProxy :: RProxy row) + tail = zeroRecord (RLProxy :: RLProxy rowlistTail) (RProxy :: RProxy row) + insert = unsafeSet key :: focus -> Record subrowTail -> Record subrow diff --git a/src/Data/Show.purs b/src/Data/Show.purs index c4b6e535..836a61c9 100644 --- a/src/Data/Show.purs +++ b/src/Data/Show.purs @@ -1,15 +1,12 @@ module Data.Show - ( class Show - , show - - , class ShowRowFields - , showRecordFieldsImpl + ( class Show, show + , class ShowRecordFields, showRecordFields ) where -import Data.Internal.Record (unsafeGet) -import Type.Data.RowList (RLProxy(..)) import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol) import Prim.RowList as RL +import Record.Unsafe (unsafeGet) +import Type.Data.RowList (RLProxy(..)) -- | The `Show` type class represents those types which can be converted into -- | a human-readable `String` representation. @@ -39,34 +36,31 @@ instance showString :: Show String where instance showArray :: Show a => Show (Array a) where show = showArrayImpl show -class ShowRowFields rowlist row focus | rowlist -> focus where - showRecordFieldsImpl :: RLProxy rowlist -> Record row -> Array String +instance showRecord :: (RL.RowToList rs ls, ShowRecordFields ls rs) => Show (Record rs) where + show record = case showRecordFields (RLProxy :: RLProxy ls) record of + [] -> "{}" + fields -> join " " ["{", join ", " fields, "}"] -instance showRowFieldsNil - :: ShowRowFields RL.Nil row focus where - showRecordFieldsImpl _ _ = [] +-- | A class for records where all fields have `Show` instances, used to +-- | implement the `Show` instance for records. +class ShowRecordFields rowlist row where + showRecordFields :: RLProxy rowlist -> Record row -> Array String -instance showRowFieldsCons +instance showRecordFieldsNil :: ShowRecordFields RL.Nil row where + showRecordFields _ _ = [] + +instance showRecordFieldsCons :: ( IsSymbol key - , ShowRowFields rowlistTail row subfocus + , ShowRecordFields rowlistTail row , Show focus ) - => ShowRowFields (RL.Cons key focus rowlistTail) row focus where - showRecordFieldsImpl _ record + => ShowRecordFields (RL.Cons key focus rowlistTail) row where + showRecordFields _ record = cons (join ": " [ key, show focus ]) tail where key = reflectSymbol (SProxy :: SProxy key) focus = unsafeGet key record :: focus - tail = showRecordFieldsImpl (RLProxy :: RLProxy rowlistTail) record - -instance showRecord - :: ( RL.RowToList rs ls - , ShowRowFields ls rs focus - ) - => Show (Record rs) where - show record = case showRecordFieldsImpl (RLProxy :: RLProxy ls) record of - [] -> "{}" - fields -> join " " [ "{", join ", " fields, "}" ] + tail = showRecordFields (RLProxy :: RLProxy rowlistTail) record foreign import showIntImpl :: Int -> String foreign import showNumberImpl :: Number -> String diff --git a/src/Record/Unsafe.js b/src/Record/Unsafe.js new file mode 100644 index 00000000..c47acf8d --- /dev/null +++ b/src/Record/Unsafe.js @@ -0,0 +1,40 @@ +"use strict"; + +exports.unsafeHas = function (label) { + return function (rec) { + return {}.hasOwnProperty.call(rec, label); + }; +}; + +exports.unsafeGet = function (label) { + return function (rec) { + return rec[label]; + }; +}; + +exports.unsafeSet = function (label) { + return function (value) { + return function (rec) { + var copy = {}; + for (var key in rec) { + if ({}.hasOwnProperty.call(rec, key)) { + copy[key] = rec[key]; + } + } + copy[label] = value; + return copy; + }; + }; +}; + +exports.unsafeDelete = function (label) { + return function (rec) { + var copy = {}; + for (var key in rec) { + if (key !== label && {}.hasOwnProperty.call(rec, key)) { + copy[key] = rec[key]; + } + } + return copy; + }; +}; diff --git a/src/Record/Unsafe.purs b/src/Record/Unsafe.purs new file mode 100644 index 00000000..adeaade7 --- /dev/null +++ b/src/Record/Unsafe.purs @@ -0,0 +1,27 @@ +-- | The functions in this module are highly unsafe as they treat records like +-- | stringly-keyed maps and can coerce the row of labels that a record has. +-- | +-- | These function are intended for situations where there is some other way of +-- | proving things about the structure of the record - for example, when using +-- | `RowToList`. **They should never be used for general record manipulation.** +module Record.Unsafe where + +-- | Checks if a record has a key, using a string for the key. +foreign import unsafeHas :: forall r1. String -> Record r1 -> Boolean + +-- | Unsafely gets a value from a record, using a string for the key. +-- | +-- | If the key does not exist this will cause a runtime error elsewhere. +foreign import unsafeGet :: forall r a. String -> Record r -> a + +-- | Unsafely sets a value on a record, using a string for the key. +-- | +-- | The output record's row is unspecified so can be coerced to any row. If the +-- | output type is incorrect it will cause a runtime error elsewhere. +foreign import unsafeSet :: forall r1 r2 a. String -> a -> Record r1 -> Record r2 + +-- | Unsafely removes a value on a record, using a string for the key. +-- | +-- | The output record's row is unspecified so can be coerced to any row. If the +-- | output type is incorrect it will cause a runtime error elsewhere. +foreign import unsafeDelete :: forall r1 r2. String -> Record r1 -> Record r2 From f7353cde960732a15be6890dcbfd9ec9a4fe54c3 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Mon, 30 Apr 2018 12:00:53 +0100 Subject: [PATCH 19/23] Update `Field` --- src/Data/Field.purs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/Data/Field.purs b/src/Data/Field.purs index 9fc9093f..12ec581c 100644 --- a/src/Data/Field.purs +++ b/src/Data/Field.purs @@ -15,14 +15,9 @@ import Data.Semiring (class Semiring, add, mul, one, zero, (*), (+)) -- | The `Field` class is for types that are (commutative) fields. -- | --- | Instances must satisfy the following law in addition to the --- | `EuclideanRing` laws: --- | --- | - Non-zero multiplicative inverse: ``a `mod` b = zero`` for all `a` and `b` --- | --- | If a type has a `Field` instance, it should also have a `DivisionRing` --- | instance. In a future release, `DivisionRing` may become a superclass of --- | `Field`. -class EuclideanRing a <= Field a +-- | `Field`s are exactly `EuclideanRing` + `CommutativeRing` so this class +-- | exists as a convenience, so a single constraint can be used when field-like +-- | behaviour is expected. +class (EuclideanRing a, CommutativeRing a) <= Field a -instance fieldNumber :: Field Number +instance fieldNumber :: (EuclideanRing a, CommutativeRing a) => Field a From 4b6a057cde5abd8b403ea21919dfd46ab735cf9e Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Fri, 13 Apr 2018 22:26:28 +0100 Subject: [PATCH 20/23] Update build dependencies --- package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 7badb81e..c89017ab 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,12 @@ "scripts": { "clean": "rimraf output && rimraf .pulp-cache", "build": "eslint src && pulp build -- --censor-lib --strict", - "test": "pulp test" + "test": "pulp test --no-check-main" }, "devDependencies": { - "eslint": "^3.17.1", - "purescript-psa": "^0.5.0-rc.1", - "pulp": "^10.0.4", - "rimraf": "^2.6.1" + "eslint": "^4.19.1", + "purescript-psa": "^0.6.0", + "pulp": "^12.2.0", + "rimraf": "^2.6.2" } } From 9247e061568b4eca6c4b0e8ef19bc1e802105ccc Mon Sep 17 00:00:00 2001 From: Harry Garrood Date: Sun, 13 May 2018 14:33:15 +0100 Subject: [PATCH 21/23] Update `Field` (refs #132) Fix superclasses and expand documentation. --- src/Data/Field.purs | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/Data/Field.purs b/src/Data/Field.purs index 12ec581c..113b714d 100644 --- a/src/Data/Field.purs +++ b/src/Data/Field.purs @@ -15,9 +15,27 @@ import Data.Semiring (class Semiring, add, mul, one, zero, (*), (+)) -- | The `Field` class is for types that are (commutative) fields. -- | --- | `Field`s are exactly `EuclideanRing` + `CommutativeRing` so this class --- | exists as a convenience, so a single constraint can be used when field-like --- | behaviour is expected. -class (EuclideanRing a, CommutativeRing a) <= Field a +-- | Mathematically, a field is a ring which is commutative and in which every +-- | nonzero element has a multiplicative inverse; these conditions correspond +-- | to the `CommutativeRing` and `DivisionRing` classes in PureScript +-- | respectively. However, the `Field` class has `EuclideanRing` and +-- | `DivisionRing` as superclasses, which seems like a stronger requirement +-- | (since `CommutativeRing` is a superclass of `EuclideanRing`). In fact, it +-- | is not stronger, since any type which has law-abiding `CommutativeRing` +-- | and `DivisionRing` instances permits exactly one law-abiding +-- | `EuclideanRing` instance. We use a `EuclideanRing` superclass here in +-- | order to ensure that a `Field` constraint on a function permits you to use +-- | `div` on that type, since `div` is a member of `EuclideanRing`. +-- | +-- | This class has no laws or members of its own; it exists as a convenience, +-- | so a single constraint can be used when field-like behaviour is expected. +-- | +-- | This module also defines a single `Field` instance for any type which has +-- | both `EuclideanRing` and `DivisionRing` instances. Any other instance +-- | would overlap with this instance, so no other `Field` instances should be +-- | defined in libraries. Instead, simply define `EuclideanRing` and +-- | `DivisionRing` instances, and this will permit your type to be used with a +-- | `Field` constraint. +class (EuclideanRing a, DivisionRing a) <= Field a -instance fieldNumber :: (EuclideanRing a, CommutativeRing a) => Field a +instance field :: (EuclideanRing a, DivisionRing a) => Field a From dbf951585da70e5108ccf614e2a82ec0a5f34255 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Tue, 22 May 2018 12:54:27 +0100 Subject: [PATCH 22/23] Fix eslint warnings --- src/Data/EuclideanRing.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Data/EuclideanRing.js b/src/Data/EuclideanRing.js index 40b55494..a19fe503 100644 --- a/src/Data/EuclideanRing.js +++ b/src/Data/EuclideanRing.js @@ -8,14 +8,14 @@ exports.intDegree = function (x) { // https://en.m.wikipedia.org/wiki/Modulo_operation. exports.intDiv = function (x) { return function (y) { - if (y == 0) return 0; + if (y === 0) return 0; return y > 0 ? Math.floor(x / y) : -Math.floor(x / -y); }; }; exports.intMod = function (x) { return function (y) { - if (y == 0) return 0; + if (y === 0) return 0; var yy = Math.abs(y); return ((x % yy) + yy) % yy; }; From c276ea0f1447912c83e06abced27e3b49df7a991 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Tue, 22 May 2018 21:37:23 +0100 Subject: [PATCH 23/23] Update license --- LICENSE | 38 ++++++++++++++++++++++---------------- bower.json | 2 +- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/LICENSE b/LICENSE index d3249fee..311379c1 100644 --- a/LICENSE +++ b/LICENSE @@ -1,20 +1,26 @@ -The MIT License (MIT) +Copyright 2018 PureScript -Copyright (c) 2015 PureScript +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/bower.json b/bower.json index 996ec6e2..2b7a1049 100644 --- a/bower.json +++ b/bower.json @@ -2,7 +2,7 @@ "name": "purescript-prelude", "homepage": "https://github.com/purescript/purescript-prelude", "description": "The PureScript Prelude", - "license": "MIT", + "license": "BSD-3-Clause", "repository": { "type": "git", "url": "git://github.com/purescript/purescript-prelude.git"