Here is the context:
newtype A = A Integer deriving (Eq)
data PairOfA = PairA A A deriving (Eq)
mkPairOfA :: [A] -> A -> A -> Maybe PairOfA
mkPairOfA as a1 a2 =
if a1 `elem` as && a2 `elem` as
then Just (PairA a1 a2)
else Nothing
as1 = [A 1, A 2, A 3]
as2 = [A 1, A 2, A 4]
pair1 = mkPairOfA as1 (A 1) (A 2)
pair2 = mkPairOfA as2 (A 1) (A 2)
main :: IO ()
main = do print $ pair1 == pair2
Here is the output in the terminal:
ghci> main
True
Question: Is there a way to somehow distinguish pair1
and pair2
by the fact that they are "related" to different lists? I could've defined the PairA constructor to assume three parameters instead of two, i.e., with the list parameter. However, I don't want my library's potential users to have to store a possibly long list within each pair, especially if they will work with many "pairs."
In the imperative paradigm, for example, in some OOP languages like Java or C++, I would use a reference or the pointer and put it in each pair without a second thought. What could be an alternative in Haskell? If I were to pick one, I would be fascinated to capture it on the type level. But if it is impossible, I would appreciate any other conventional workarounds.
Edit: I've ended up with the following:
{-# LANGUAGE DataKinds #-}
import GHC.TypeLits (Symbol)
newtype A = A Integer deriving (Eq, Show)
data As (label :: Symbol) = As [A]
data PairOfA (label :: Symbol) = PairA A A deriving (Eq, Show)
mkPairOfA :: As label -> A -> A -> Maybe (PairOfA label)
mkPairOfA (As as) a1 a2 =
if a1 `elem` as && a2 `elem` as
then Just (PairA a1 a2 :: PairOfA label)
else Nothing
as1 = As [A 1, A 2, A 3] :: As "as1"
as2 = As [A 1, A 2, A 4] :: As "as2"
pair1 = mkPairOfA as1 (A 1) (A 2)
pair2 = mkPairOfA as2 (A 1) (A 2)
main :: IO ()
{-
• Couldn't match type ‘"as2"’ with ‘"as1"’
Expected: Maybe (PairOfA "as1")
Actual: Maybe (PairOfA "as2")
• In the second argument of ‘(==)’, namely ‘pair2’
In the second argument of ‘($)’, namely ‘pair1 == pair2’
In a stmt of a 'do' block: print $ pair1 == pair2
-}
main = do print $ pair1 == pair2
Put the list in the pair. Don't worry, just like what you would do in Java or C++, this just stores a reference to the real thing, so it's equally cheap (or equally expensive, if you prefer).
data PairWithSourceList = PWSL A A [A] deriving (Eq)
Of course, the Eq
instance will still have to compare the full lists for equality. But there really is no good way to avoid that.
The part of your question about tracking this at the type level generates the same answer as your other question about tracking such things at the type level.