1
votes

Is any way to have a list of pairs without the same types in Haskell and make a function traverse it. For example:

a = [(1, "uno"), (2, True), (3, 5)]

A want to apply a function depending on the type of the second value, something like a function that evaluates the combination of the pair f :: [(Int, #)] -> [a].

3
I don’t think this is currently possible without the ImpredicativeTypes extension, which currently doesn’t really work. - Alexis King
@AlexisKing ImpredicativeTypes won't help you here. Impredicativity allows you to parameterise types by forall-quantified types, eg to make a homogeneous list of polymorphic functions. OP wants to build a heterogeneous list of monomorphic tuples. - Benjamin Hodgson
@jonaprieto What are you trying to achieve? The simplest way is to build a sum type and do runtime case analysis on the values: a :: [(Int, Either Bool String)] - Benjamin Hodgson
@BenjaminHodgson after here the comments, I decide to use a sum type, because actually, I have six types. Then, yes, I'll do the case analysis. - Jonathan Prieto-Cubides
@BenjaminHodgson Sure, but I wasn’t thinking directly. OP specified “a function depending on the type of the second value”, which could become a typeclass with a method, which could then be used in a forall quantification using ImpredicativeTypes. - Alexis King

3 Answers

7
votes

To a first approximation, no, this is not possible. Normalize the values first, e.g. by applying some class-polymorphic function to each of the second arguments, before putting them in the tuples.

5
votes

Just wrap the values in a sum-type. E.g.,

data StringOrBoolOrInt =
  StringOrBoolOrInt_String String |
  StringOrBoolOrInt_Bool Bool |
  StringOrBoolOrInt_Int Int

a :: [(Int, StringOrBoolOrInt)]
a =
  [
    (1, StringOrBoolOrInt_String "uno"),
    (2, StringOrBoolOrInt_Bool True),
    (3, StringOrBoolOrInt_Int 5)
  ]
2
votes

As others have stated, as given, this problem has no solution. But your real problem is that your data is not described by a list of tuples - by definition, a list is homogenous (all elements contain the same type) while your data is heterogenous.

If you want write a function depending on the type, you must store the type on the type level somehow.

Your example data is actually described by the type Prod ((,) Integer) '[String, Bool, Integer], where Prod is the following type:

data Prod f (xs :: [a]) where 
  P0 :: Prod f '[] 
  (:>) :: f x -> Prod f xs -> Prod f (x ': xs) 

infixr 5 :>

or more generally Prod ((,) Integer) xs for some list of types xs.

Your example value is then

a = (1, "uno") :> (2, True) :> (3, 5) :> P0 

You can branch on these types using the normal methods - i.e. a type class. Supposing one has such a class:

class HasFoo x where 
  foo :: x -> Integer 

instance HasFoo String where foo = fromIntegral . length 
instance HasFoo Bool where foo = fromIntegral . fromEnum 
instance HasFoo Integer where foo = id 

you can apply such a foo function to every element of your product

type family All (c :: k -> Constraint) (xs :: [k]) :: Constraint where 
  All c '[] = () 
  All c (x ': xs) = (c x, All c xs) 

-- example operation: add everything 
fooProd :: All HasFoo xs 
        => Prod ((,) Integer) xs 
        -> Integer 
fooProd P0 = 0 
fooProd ((i, x) :> ps) = i + foo x + fooProd ps 

This requires some GHC extensions, at least TypeFamilies, GADTs, ConstraintKinds, DataKinds, PolyKinds.