I am having a play with TypeFamilies and the such in Haskell. I am trying to create a simple example whereby I have a function that takes in some type (Parsers ty
) below, and a function that takes a number of arguments dependant on that ty
, and outputs some result.
Here's the entire code snippet up front:
{-# LANGUAGE UndecidableInstances, TypeFamilies,GADTs #-}
data V a --"variable"
data S a --"static"
data C a b --combination
--define data type:
data Parsers ty where
ParVar :: a -> Parsers (V a)
ParStatic :: a -> Parsers (S a)
ParCombine :: Parsers ty1 -> Parsers ty2 -> Parsers (C ty1 ty2)
--basic type alias to below, output set to Bool
type ExtractVars parsers = GetVars parsers Bool
--we want to convert some combination of C/S/V
--types into a function taking, from left to right,
--each type inside S as parameter
type family GetVars parsers output where
GetVars (S a) output = output
GetVars (V a) output = a -> output
GetVars (C a b) output = GetVars a (GetVars b output)
--this function uses the above such that, hopefully,
--the second argument to be provided will be a function
--that will take each S type as an argument and return
--a bool. We then return that bool from the whole thing
getVars :: Parsers p -> ExtractVars p -> Bool
getVars (ParVar s) fn = fn s
getVars (ParStatic _) fn = fn
-- this is the offending line:
getVars (ParCombine a b) fn = getVars b (getVars a fn)
My Parsers ty
type seems to do the right thing; constructing things with it leads to types I would expect eg:
ParVar "hi"
:: Parsers (V [Char])
ParCombine (ParVar "hi") (ParStatic "woop")
:: Parsers (C (V [Char]) (S [Char]))
ParCombine (ParVar 'h') (ParCombine (ParStatic True) (ParVar "hello"))
:: Parsers (C (V Char) (C (S Bool) (V [Char])))
Equally, my type family GetVars
seems to do the right thing, performing conversions such as:
(C (V a) (V b)) out => a -> b -> out
(V a) out => a -> out
(S a) out => out
(C (S a) (V b) out => b -> out
Basically, correctly picking out the things tagged with V
in the correct order and ignoring the S
s
My getVars
function then uses this in its type signature:
getVars :: Parsers p -> ExtractVars p -> Bool
To say that whatever p
is, expect a function matching my type alias ExtractVars p
, and return a Bool
.
Of my getVars
function, these two definitions cause no issue:
getVars (ParVar s) fn = fn s
getVars (ParStatic _) fn = fn
but the final one:
getVars (ParCombine a b) fn = getVars b (getVars a fn)
Fails to typecheck, with the errors:
test.hs:74:42:
Could not deduce (GetVars ty2 Bool ~ Bool)
from the context (p ~ C ty1 ty2)
bound by a pattern with constructor
ParCombine :: forall ty1 ty2.
Parsers ty1 -> Parsers ty2 -> Parsers (C ty1 ty2),
in an equation for ‘getVars’
at test.hs:74:10-23
Expected type: ExtractVars ty2
Actual type: Bool
Relevant bindings include b :: Parsers ty2 (bound at test.hs:74:23)
In the second argument of ‘getVars’, namely ‘(getVars a fn)’
In the expression: getVars b (getVars a fn)
test.hs:74:52:
Could not deduce (GetVars ty1 Bool
~ GetVars ty1 (GetVars ty2 Bool))
from the context (p ~ C ty1 ty2)
bound by a pattern with constructor
ParCombine :: forall ty1 ty2.
Parsers ty1 -> Parsers ty2 -> Parsers (C ty1 ty2),
in an equation for ‘getVars’
at test.hs:74:10-23
NB: ‘GetVars’ is a type function, and may not be injective
Expected type: ExtractVars ty1
Actual type: ExtractVars p
Relevant bindings include
b :: Parsers ty2 (bound at test.hs:74:23)
a :: Parsers ty1 (bound at test.hs:74:21)
In the second argument of ‘getVars’, namely ‘fn’
In the second argument of ‘getVars’, namely ‘(getVars a fn)’
Failed, modules loaded: none.
(line 74 is the failing ParCombine definition of getVars)
I've been banging my head against the desk trying to figure out where I'm going wrong to no avail, and really want to get a firm grip on the ins and outs of type families as they seem awesome (the above example for instance- being able to expect some function type depending on input - would be incredibly cool!)
Could anyone point my in the right direction or correct my possibly very silly error?
Thanks!
(PS I'm using GHC 7.10.1 and running this in ghci)
Edit Just to say this example was somewhat motivated by a paper on type families, specifically the sprintf example here. Perhaps I have skipped something important in attempting to distill what I wanted from it!
getVars a fn
is aBool
butgetVars b
expects anExtractVars b
– gallaisParsers
GADT correct? what's thatty
doing? – jberrymangetVars a fn
is dependant on the type of a here? Eg ifa
isS a
then fn should be aBool
, but if it isV a
then fn should bea -> Bool
given the type ofgetVars
. – jsdwty
here is holding the 'label' (V a, S a or C a b) which depends on the constructor used. I then pass this label to the ExtractVars type alias (and thus GetVars type family) to have it transformed into the function signature I desire. That part, at least, seems to be fine :) – jsdw