Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add NonEmptySet #7

Merged
merged 2 commits into from
Sep 6, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"purescript-arrays": "^5.0.0",
"purescript-foldable-traversable": "^4.0.0",
"purescript-gen": "^2.0.0",
"purescript-lists": "^5.0.0",
"purescript-lists": "^5.2.0",
"purescript-maybe": "^4.0.0",
"purescript-partial": "^2.0.0",
"purescript-prelude": "^4.0.0",
Expand Down
149 changes: 149 additions & 0 deletions src/Data/Set/NonEmpty.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
module Data.Set.NonEmpty
( NonEmptySet
, singleton
, cons
, fromSet
, fromFoldable
, fromFoldable1
, toSet
, toUnfoldable
, toUnfoldable1
, map
, member
, insert
, delete
, size
, min
, max
, unionSet
, difference
, subset
, properSubset
, intersection
) where

import Prelude hiding (map)

import Data.Eq (class Eq1)
import Data.Foldable (class Foldable)
import Data.List (List, (:))
import Data.List as List
import Data.List.NonEmpty (NonEmptyList)
import Data.Maybe (Maybe(..), fromJust)
import Data.Ord (class Ord1)
import Data.Semigroup.Foldable (class Foldable1, foldMap1)
import Data.Set (Set)
import Data.Set as Set
import Data.Tuple (Tuple(..))
import Data.Unfoldable (class Unfoldable, class Unfoldable1, unfoldr1)
import Partial.Unsafe (unsafePartial)

-- | `NonEmptySet a` represents a non-empty set of values of type `a`
newtype NonEmptySet a = NonEmptySet (Set a)

derive newtype instance eqNonEmptySet :: Eq a => Eq (NonEmptySet a)
derive newtype instance eq1NonEmptySet :: Eq1 NonEmptySet
derive newtype instance ordNonEmptySet :: Ord a => Ord (NonEmptySet a)
derive newtype instance ord1NonEmptySet :: Ord1 NonEmptySet
derive newtype instance semigroupNonEmptySet :: Ord a => Semigroup (NonEmptySet a)
derive newtype instance foldableNonEmptySet :: Foldable NonEmptySet

instance foldable1NonEmptySet :: Foldable1 NonEmptySet where
foldMap1 f = foldMap1 f <<< toUnfoldable1 :: forall a. NonEmptySet a -> NonEmptyList a
fold1 = foldMap1 identity

instance showNonEmptySet :: Show a => Show (NonEmptySet a) where
show s = "(fromFoldable1 " <> show (toUnfoldable1 s :: NonEmptyList a) <> ")"

-- | Create a set with one element.
singleton :: forall a. a -> NonEmptySet a
singleton a = NonEmptySet (Set.singleton a)

-- | Creates a `NonEmptySet` from an item and a `Set`.
cons :: forall a. Ord a => a -> Set a -> NonEmptySet a
cons a = NonEmptySet <<< Set.insert a

-- | Attempts to create a non-empty set from a possibly-empty set.
fromSet :: forall a. Set a -> Maybe (NonEmptySet a)
fromSet s = if Set.isEmpty s then Nothing else Just (NonEmptySet s)

-- | Create a set from a foldable structure.
fromFoldable :: forall f a. Foldable f => Ord a => f a -> Maybe (NonEmptySet a)
fromFoldable = fromSet <<< Set.fromFoldable

-- | Create a set from a non-empty foldable structure.
fromFoldable1 :: forall f a. Foldable1 f => Ord a => f a -> NonEmptySet a
fromFoldable1 = foldMap1 singleton

-- | Forgets the non-empty property of a set, giving a normal possibly-empty
-- | set.
toSet :: forall a. NonEmptySet a -> Set a
toSet (NonEmptySet s) = s

-- | Convert a set to an unfoldable structure.
toUnfoldable :: forall f a. Unfoldable f => NonEmptySet a -> f a
toUnfoldable (NonEmptySet s) = Set.toUnfoldable s

-- | Convert a set to a non-empty unfoldable structure.
toUnfoldable1 :: forall f a. Unfoldable1 f => NonEmptySet a -> f a
toUnfoldable1 (NonEmptySet s) = unfoldr1 go (Set.toUnfoldable s :: List a)
where
go = unsafePartial case _ of
x : List.Nil -> Tuple x Nothing
x : tail -> Tuple x (Just tail)

-- | Maps over the values in a set.
-- |
-- | This operation is not structure-preserving for sets, so is not a valid
-- | `Functor`. An example case: mapping `const x` over a set with `n > 0`
-- | elements will result in a set with one element.
map :: forall a b. Ord b => (a -> b) -> NonEmptySet a -> NonEmptySet b
map f (NonEmptySet s) = NonEmptySet (Set.map f s)

-- | Test if a value is a member of a set.
member :: forall a. Ord a => a -> NonEmptySet a -> Boolean
member a (NonEmptySet m) = Set.member a m

-- | Insert a value into a set.
insert :: forall a. Ord a => a -> NonEmptySet a -> NonEmptySet a
insert a (NonEmptySet s) = NonEmptySet (Set.insert a s)

-- | Delete a value from a non-empty set. If this would empty the set, the
-- | result is `Nothing`.
delete :: forall a. Ord a => a -> NonEmptySet a -> Maybe (NonEmptySet a)
delete a (NonEmptySet s) = fromSet (Set.delete a s)

-- | Find the size of a set.
size :: forall a. NonEmptySet a -> Int
size (NonEmptySet s) = Set.size s

-- | The minimum value in the set.
min :: forall a. NonEmptySet a -> a
min (NonEmptySet s) = unsafePartial (fromJust (Set.findMin s))

-- | The maximum value in the set.
max :: forall a. NonEmptySet a -> a
max (NonEmptySet s) = unsafePartial (fromJust (Set.findMax s))

-- | Form the union of a set and the non-empty set.
unionSet :: forall a. Ord a => Set.Set a -> NonEmptySet a -> NonEmptySet a
unionSet s1 (NonEmptySet s2) = NonEmptySet (s1 <> s2)

-- | Form the set difference. `Nothing` if the first is a subset of the second.
difference :: forall a. Ord a => NonEmptySet a -> NonEmptySet a -> Maybe (NonEmptySet a)
difference (NonEmptySet s1) (NonEmptySet s2) = fromSet (Set.difference s1 s2)

-- | True if and only if every element in the first set is an element of the
-- | second set.
subset :: forall a. Ord a => NonEmptySet a -> NonEmptySet a -> Boolean
subset (NonEmptySet s1) (NonEmptySet s2) = Set.subset s1 s2

-- | True if and only if the first set is a subset of the second set and the
-- | sets are not equal.
properSubset :: forall a. Ord a => NonEmptySet a -> NonEmptySet a -> Boolean
properSubset (NonEmptySet s1) (NonEmptySet s2) = Set.properSubset s1 s2

-- | The set of elements which are in both the first and second set. `Nothing`
-- | if the sets are disjoint.
intersection :: forall a. Ord a => NonEmptySet a -> NonEmptySet a -> Maybe (NonEmptySet a)
intersection (NonEmptySet s1) (NonEmptySet s2) = fromSet (Set.intersection s1 s2)