miso-1.12.0.0: src/Miso/Data/Set.hs
-----------------------------------------------------------------------------
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-----------------------------------------------------------------------------
-- |
-- Module : Miso.Data.Set
-- Copyright : (C) 2016-2026 David M. Johnson
-- License : BSD3-style (see the file LICENSE)
-- Maintainer : David M. Johnson <code@dmj.io>
-- Stability : experimental
-- Portability : non-portable
--
-- = Overview
--
-- "Miso.Data.Set" is a Haskell wrapper around the JavaScript
-- <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set Set>
-- object. Values of type @'Set' a@ live in JavaScript memory; all
-- operations run in 'IO' and mutate the underlying JS set in place.
--
-- Unlike 'Data.Set.Set', elements do not require an 'Ord' instance —
-- any type with a 'Miso.DSL.ToJSVal' instance can be stored, and equality
-- is determined by JavaScript's
-- <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness SameValueZero>
-- algorithm.
--
-- Use this module when you need to share a set with a browser API or a
-- third-party JavaScript library. For pure Haskell processing, prefer
-- 'Data.Set.Set'.
--
-- Import qualified to avoid clashing with 'Prelude':
--
-- @
-- import qualified "Miso.Data.Set" as S
-- @
--
-- = Quick start
--
-- @
-- import qualified "Miso.Data.Set" as S
--
-- example :: IO ()
-- example = do
-- s <- S.'fromList' [1, 2, 3 :: Int]
-- S.'insert' 4 s
-- b <- S.'member' 2 s -- True
-- n <- S.'size' s -- 4
-- S.'delete' 1 s
-- pure ()
-- @
--
-- = Operations
--
-- * __Construction__: 'new', 'fromList'
-- * __Access__: 'member', 'size'
-- * __Mutation__: 'insert', 'delete', 'clear'
-- * __Set algebra__: 'union', 'intersection', 'difference', 'isSubset', 'isSuperset', 'isDisjoint'
--
-- = See also
--
-- * "Miso.Data.Array" — mutable JS 'Miso.Data.Array.Array'
-- * "Miso.Data.Map" — mutable JS 'Miso.Data.Map.Map'
-- * "Miso.DSL" — 'Miso.DSL.ToJSVal' \/ 'Miso.DSL.FromJSVal' used by element types
-----------------------------------------------------------------------------
module Miso.Data.Set
( -- * Type
Set
-- * Construction
, new
, fromList
-- * Operations
, insert
, member
, clear
, size
, delete
, union
, intersection
, difference
, isSubset
, isSuperset
, isDisjoint
) where
-----------------------------------------------------------------------------
import Control.Monad (void, forM_)
import Prelude hiding (lookup)
-----------------------------------------------------------------------------
import Miso.DSL (jsg, JSVal, ToJSVal, FromJSVal, (!))
import qualified Miso.DSL as DSL
import Miso.FFI (callFunction)
-----------------------------------------------------------------------------
newtype Set key = Set JSVal deriving (FromJSVal, ToJSVal)
-----------------------------------------------------------------------------
-- | Constructs a new JS [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) in t'IO'.
--
new :: IO (Set key)
new = Set <$> DSL.new (jsg "Set") ([] :: [JSVal])
-----------------------------------------------------------------------------
-- | Inserts a value into the t'Set'.
insert
:: ToJSVal key
=> key
-- ^ Element to add
-> Set key
-- ^ Set to mutate
-> IO ()
insert key (Set m) = do
_ <- callFunction m "add" [key]
pure ()
-----------------------------------------------------------------------------
-- | Empties the t'Set'.
clear :: Set key -> IO ()
clear (Set m) = void (callFunction m "clear" ())
-----------------------------------------------------------------------------
-- | Return the size of t'Set'.
size :: Set key -> IO Int
size (Set m) = DSL.fromJSValUnchecked =<< m ! "size"
-----------------------------------------------------------------------------
-- | Checks existence of 'key' in t'Set', returns t'Bool.
member
:: ToJSVal key
=> key
-- ^ Element to test for membership
-> Set key
-- ^ Set to query
-> IO Bool
member key (Set m) = DSL.fromJSValUnchecked =<< callFunction m "has" =<< DSL.toJSVal key
-----------------------------------------------------------------------------
-- | Removes an element from the t'Set', returns 'True' if it existed.
delete
:: ToJSVal key
=> key
-- ^ Element to remove
-> Set key
-- ^ Set to mutate
-> IO Bool
delete key (Set m) = DSL.fromJSValUnchecked =<< callFunction m "delete" =<< DSL.toJSVal key
-----------------------------------------------------------------------------
-- | Construct a t'Set' from a list of elements.
fromList
:: ToJSVal key
=> [key]
-- ^ Elements to populate the new set with
-> IO (Set key)
fromList xs = do
m <- new
forM_ xs $ \k ->
insert k m
pure m
-----------------------------------------------------------------------------
-- | The union of two t'Set'
union
:: ToJSVal key
=> Set key -- ^ First set
-> Set key -- ^ Second set
-> IO (Set key)
union (Set x) (Set y) = Set <$> callFunction x "union" [y]
-----------------------------------------------------------------------------
-- | The intersection of two t'Set'
intersection
:: ToJSVal key
=> Set key -- ^ First set
-> Set key -- ^ Second set
-> IO (Set key)
intersection (Set x) (Set y) = Set <$> callFunction x "intersection" [y]
-----------------------------------------------------------------------------
-- | The symmetric difference of two t'Set'
difference
:: ToJSVal key
=> Set key -- ^ First set
-> Set key -- ^ Second set
-> IO (Set key)
difference (Set x) (Set y) = Set <$> callFunction x "symmetricDifference" [y]
-----------------------------------------------------------------------------
-- | Checks if one t'Set' is a subset of another t'Set'
isSubset
:: ToJSVal key
=> Set key -- ^ Candidate subset
-> Set key -- ^ Potential superset
-> IO Bool
isSubset (Set x) (Set y) = DSL.fromJSValUnchecked =<<
callFunction x "isSubsetOf" [y]
-----------------------------------------------------------------------------
-- | Checks if one t'Set' is a superset of another t'Set'
isSuperset
:: ToJSVal key
=> Set key -- ^ Candidate superset
-> Set key -- ^ Potential subset
-> IO Bool
isSuperset (Set x) (Set y) = DSL.fromJSValUnchecked =<<
callFunction x "isSupersetOf" [y]
-----------------------------------------------------------------------------
-- | Checks if one t'Set' is disjoint from another t'Set'
isDisjoint
:: ToJSVal key
=> Set key -- ^ First set
-> Set key -- ^ Second set (disjoint means they share no elements)
-> IO Bool
isDisjoint (Set x) (Set y) = DSL.fromJSValUnchecked =<<
callFunction x "isDisjointFrom" [y]
-----------------------------------------------------------------------------