haskellfunctional-programming

Is it possible in Haskell to derive a type from a value of another type so that any two different values result in various types?


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

Solution

  • 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.