1. 11
  1.  

  2. 3

    It’s interesting how the tuple notation seems to perform a boolean union of the type constraints (resulting type needs to match all constraints) vs union types denoted with |, performing different type of union (resulting type needs to match any). I wonder what the equivalent infix operator would be then.

    1. 3

      If you wanted to have alternates you have to manually encode it. Something like this:

      {-# LANGUAGE RankNTypes, ConstraintKinds, KindSignatures #-}
      
      import GHC.Exts (Constraint)
      
      data Has (c :: * -> Constraint) = forall x. (c x) => Has x
      
      showAnyNumber :: Either (Has RealFrac) (Has Integral) -> String
      showAnyNumber (Left (Has real)) = show (toRational real)
      showAnyNumber (Right (Has int)) = show (toInteger int)
      
      main = do
          let numbers = [ Left (Has 3.14), Right (Has 73) ]
          mapM_ (putStrLn . showAnyNumber) numbers
      

      In showAnyNumber, when you match on the Has constructor it brings the constraint “into scope” so you can use the methods on it (toRational with RealFrac/toInteger with Integral).

      However, existential types like this aren’t that useful in practice—here you’d be better calling show when you know the concrete type.