Skip to content

Instantly share code, notes, and snippets.

@ch3pjw
Created May 12, 2018 23:41
Show Gist options
  • Save ch3pjw/0ab767bc9e6b4ac56e2b36b784d09598 to your computer and use it in GitHub Desktop.
Save ch3pjw/0ab767bc9e6b4ac56e2b36b784d09598 to your computer and use it in GitHub Desktop.
Mashing together type-class constraints inside a parameterised existential datatype
{-# LANGUAGE
ConstraintKinds
, ExistentialQuantification
, FlexibleInstances
, MultiParamTypeClasses
, TypeOperators
, UndecidableSuperClasses
#-}
module Test where
import Data.Proxy
-- Not needed, just FYI this is where you get the Constraint kind from:
import GHC.Exts (Constraint)
-- NB, ctx :: Constraint
data Thing ctx = forall a. ctx a => Thing a
-- | This is simply to show that parameterising Thing works. Without
-- passing the `Show` class, this function wouldn't compile.
doShow :: Thing Show -> String
doShow (Thing a) = show a
-- This is some slightly mind-bending magic that I got from
-- https://github.com/glaebhoerl/exists/blob/master/Control/Constraint/Combine.hs
-- I think it works simply because when you specify a constraint like
-- (Num :&: Show) a => ...
-- That just pulls in both the superclass constraints on the LHS of the below.
-- NB, it needs the expected MultiParamTeypClasses and FlexibleInstances extensions,
-- and _also_ the UndecidableSuperClasses one - scary!
class (c a, d a) => (c :&: d) a
instance (c a, d a) => (c :&: d) a
infixl 7 :&:
-- But now we can write this sexy beast:
doNumShow :: Thing (Num :&: Show) -> String
doNumShow (Thing num) = show num
-- Which will run like
-- > doNumShow 3
-- "3"
-- > doNumShow "hello"
-- No instance Num a...
-- NB we couldn't use a simple constraint synonym like
-- type Mash c1 c2 a = (c1 a, c2 a)
-- because type aliases cannot be partially applied!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment