instance constraints compare type-family constraints - haskell

Usually I reckon type-families are similarly expressive as compared with typeclasses/instances -- the difference is awkwardness/ergonomics of the code. In this case I have code working with type-families to raise a constraint, but the equivalent typeclass code won't compile. (* Could not deduce (Eq a) ... when (Eq a) is exactly the constraint I'm supplying.) Is this a case typeclasses just can't express, or am I doing something wrong?
data Set a = NilSet | ConsSet a (Set a) deriving (Eq, Show, Read)
-- fmap over a Set, squishing out duplicates
fmapSet :: (Eq a, Eq b ) => (a -> b) -> Set a -> Set b
fmapSet f NilSet = NilSet
fmapSet f (ConsSet x xs) = uqCons (f x) (fmapSet f xs)
uqCons fx fxs | sElem fx fxs = fxs
| otherwise = ConsSet fx fxs
sElem fx NilSet = False
sElem fx (ConsSet fy fys) = fx == fy || sElem fx fys
I want to call that fmap via a Functor-like class, with a constraint that the data-structure is well-formed. Either of these approaches with type-families work (based on this answer, but preferring a standalone family).
{-# LANGUAGE ConstraintKinds, TypeFamilies #-}
import Data.Kind (Type, Constraint)
type family WFTF (f :: * -> *) a :: Constraint
type instance WFTF Set a = Eq a
class WFTFFunctor f where
wftFfmap :: (WFTF f a, WFTF f b) => (a -> b) -> f a -> f b
instance WFTFFunctor Set where
wftFfmap = fmapSet
type family WFTF2 c_a :: Constraint
type instance WFTF2 (Set a) = Eq a
class WFTF2Functor f where
wftF2fmap :: (WFTF2 (f a), WFTF2 (f b)) => (a -> b) -> f a -> f b
instance WFTF2Functor Set where
wftF2fmap = fmapSet
The equivalent (I think) typeclass at least compiles providing I don't give an implementation for the method:
class WFT c_a where
instance Eq a => WFT (Set a)
class WFTFunctor f where
wftfmap :: (WFT (f a), WFT (f b)) => (a -> b) -> f a -> f b
instance WFTFunctor Set where wftfmap f xss = undefined -- s/b fmapSet f xss
Inferred :t (\ f (xss :: Set a) -> wftfmap f xss) :: (Eq a, Eq b) => (a -> b) -> Set a -> Set b -- which is exactly the type of fmapSet. But if I put that call to fmapSet f xss in place of undefined, rejected:
* Could not deduce (Eq a) arising from a use of `fmapSet'
from the context: (WFT (Set a), WFT (Set b))
bound by the type signature for:
wftfmap :: forall a b.
(WFT (Set a), WFT (Set b)) =>
(a -> b) -> Set a -> Set b
at ...
Possible fix:
add (Eq a) to the context of
the type signature for:
wftfmap :: forall a b.
(WFT (Set a), WFT (Set b)) =>
(a -> b) -> Set a -> Set b
WFT (Set a) implies raises Wanted (Eq a), so I shouldn't need to add it. (And if I do via InstanceSignatures, rejected because it's not as general as the inferred constraint.) [In response to #dfeuer's answer/my comment] True that there's nothing in the instance decl to Satisfy (Eq a), but fmapSet's sig also Wants (Eq a) so (why) doesn't that ensure the constraint gets satisfied at the call site?
I've tried decorating everything with ScopedTypeVariables/PatternSignatures to make the constraints more explicit. I've tried switching on ImpredicativeTypes (GHC 8.10.2). I sometimes get different rejection messages, but nothing that compiles.
If I take away the WFT (Set a) and (Eq a) from fmapSet's signature, I get a similar rejection * Could not deduce (Eq b) .... Yes I know that rejection message is a FAQ. In the q's I've looked through, the constraint is indeed unsatisfiable. But then in this case
a) why does the version with implementation undefined typecheck;
b) isn't the constraint wanted from WFT (Set a) getting satisfied
by fmapSet having the (Eq a)?)
Addit: To explain a bit more about what I'm expecting in terms of Wanted/Satisfied constraints:
There's no signature given for uqCons, nor for sElem, which it calls. In sElem there's a call to (==), that raises Wanted (Eq b) in sElem's sig, which gets passed as a Wanted in the sig for uqCons, which gets passed as a Wanted in the sig for fmapSet, which does have a sig given including (Eq b).
Similarly the Set instance for method wftfmap raises Wanted (Eq a, Eq b); I expect it can use that to Satisfy the Wanted arising from the call to fmapSet.

There's a huge difference between superclass constraints and instance constraints. A superclass constraint is something required to form any instance of the class, and is available whenever the subclass constraint is in force. An instance constraint is required to form a specific instance, and is not automatically available when the class constraint is in force. This difference is pretty deeply wired into the system, and is reflected in the Core representations. In Core:
A class is a type of records of class methods and superclasses.
An instance is a value of a class type or a function from its instance constraints to such a value.
Once a "dictionary function" is called to produce an instance dictionary, you only have the dictionary, not the arguments that were used to create it.

[re #Ben's comment to #dfeuer's answer] not something you have on the inside to implement wftfmap.
OK I can have the right sig on the inside:
-- class/instance WFT, funcs fmapSet, uqCons, sElem as before
{-# LANGUAGE InstanceSigs, QuantifiedConstraints #-}
class WFTQFunctor f where
wftQfmap :: (WFT (f a), WFT (f b)) => (a -> b) -> f a -> f b
instance (forall b. (WFT (Set b) => Eq b)) => WFTQFunctor Set where
wftQfmap :: (Eq a, Eq b) => (a -> b) -> (Set a) -> (Set b) -- yay
wftQfmap f xss = fmapSet f xss
Compiles with a warning that I can suppress with -XMonoLocalBinds:
* The constraint `WFT (Set b)' matches
instance Eq a => WFT (Set a)
This makes type inference for inner bindings fragile;
either use MonoLocalBinds, or simplify it using the instance
* In the instance declaration for `WFTQFunctor Set'
I appreciate that QuantifiedConstraint is a fib. I might have instance {-# OVERLAPPING #-} WFT (Set (b -> c)) for which Eq does not hold; but I don't; and/or at least if I use a Set element for which Eq holds, I'll get away with it(?)
But no:
*> wftQfmap toUpper mySet -- mySet :: Set Char
<interactive>:2:1: error:
* Could not deduce (Eq b) arising from a use of `wftQfmap'
from the context: WFT (Set b)
bound by a quantified context at <interactive>:2:1-22
Possible fix: add (Eq b) to the context of a quantified context
* In the expression: wftQfmap toUpper mySet
In an equation for `it': it = wftQfmap toUpper mySet
So why does that instance WFTQFunctor Set compile? And can it ever do anything useful?

OK I have something working. It's ugly and clunky, and not scalable, but answers the q as put:
class WFT c_a where
isWF :: c_a -> Bool -- is c_a well-formed?
mkWF :: c_a -> c_a -- edit c_a to make it well-formed
mkWF = id
instance Eq a => WFT (Set a) -- instance types same as q
isWF NilSet = True
isWF (ConsSet x xs) = not (sElem x xs) && isWF xs -- sElem decl'd in the q
mkWF = fmapSet id -- fmapSet also
class WFTFunctor f where -- class as decl'd in the q
wftfmap :: (WFT (f a), WFT (f b)) => (a -> b) -> f a -> f b
instance WFTFunctor Set where wftfmap f xss = mkWF $ fmap f xss
instance Functor Set where -- conventional/unconstrained fmap
fmap f NilSet = NilSet
fmap f (ConsSet x xs) = ConsSet (f x) (fmap f xs)
If I'm using fmap, generalise:
class Functor f => WFTFunctor f where
wftfmap :: (WFT (f a), WFT (f b)) => (a -> b) -> f a -> f b
wftfmap f xss = mkWF $ fmap f xss
This is not far off H2010 compliant. (Needs FlexibleConstraints.) So I can get the Eq constraint effective 'inside' the WFTFunctor instance; it needs a method call from WFT to pull it through.
I could have heaps of other methods in class WFT, but I say "not scalable" because you couldn't in general 'edit' an ill-formed structure to well-formed. Hmm since it's a Functor: could unload to a List then load back to the structure.

Related

Is it possible to derive a Traversable instance via another type constructor?

Suppose we have some class Foo such that an instance of Foo f gives us everything necessary to implement Functor f, Foldable f and Traversable f. To avoid overlapping instances, can witness this relationship between Foo and Functor, Foldable, Traversable under a newtype wrapper:
type Foo :: (Type -> Type) -> Constraint
class Foo f
where
{- ... -}
type FoonessOf :: (Type -> Type) -> Type -> Type
newtype FoonessOf f a = FoonessOf (f a)
instance Foo f => Functor (FoonessOf f)
where
fmap = _
instance Foo f => Foldable (FoonessOf f)
where
foldMap = _
instance Foo f => Traversable (FoonessOf f)
where
traverse = _
Now suppose we have some type constructor:
data Bar a = Bar {- ... -}
such that there is an:
instance Foo Bar
where
{- ... -}
We'd like to equip Bar with the instances implied by its "Foo-ness". Since Bar a is Coercible to FoonessOf Bar a, we'd expect to be able to derive the instances via the FoonessOf Bar:
deriving via (FoonessOf Bar) instance Functor Bar
deriving via (FoonessOf Bar) instance Foldable Bar
And this works handily for typeclasses such as Functor and Foldable
Unfortunately when we try to do the same thing with Traversable, things go awry:
[typecheck -Wdeferred-type-errors] [E] • Couldn't match representation of type ‘f1 (Foo Bar a1)’
with that of ‘f1 (Bar a1)’
arising from a use of ‘ghc-prim-0.6.1:GHC.Prim.coerce’
NB: We cannot know what roles the parameters to ‘f1’ have;
we must assume that the role is nominal
• In the expression:
ghc-prim-0.6.1:GHC.Prim.coerce
#(Foo Bar (f a) -> f (Foo Bar a)) #(Bar (f a) -> f (Bar a))
(sequenceA #(Foo Bar)) ::
forall (f :: TYPE ghc-prim-0.6.1:GHC.Types.LiftedRep
-> TYPE ghc-prim-0.6.1:GHC.Types.LiftedRep)
(a :: TYPE ghc-prim-0.6.1:GHC.Types.LiftedRep).
Applicative f => Bar (f a) -> f (Bar a)
In an equation for ‘sequenceA’:
sequenceA
= ghc-prim-0.6.1:GHC.Prim.coerce
#(Foo Bar (f a) -> f (Foo Bar a)) #(Bar (f a) -> f (Bar a))
(sequenceA #(Foo Bar)) ::
forall (f :: TYPE ghc-prim-0.6.1:GHC.Types.LiftedRep
-> TYPE ghc-prim-0.6.1:GHC.Types.LiftedRep)
(a :: TYPE ghc-prim-0.6.1:GHC.Types.LiftedRep).
Applicative f => Bar (f a) -> f (Bar a)
When typechecking the code for ‘sequenceA’
in a derived instance for ‘Traversable Bar’:
To see the code I am typechecking, use -ddump-deriv
In the instance declaration for ‘Traversable Bar’
——————————————————————————————————————————————————————————————————————————————
...
So the questions I have are:
Is it possible to come up with some other scheme for deriving-via the instance Traversable Bar?
Is it possible to come up with some modification of the Traversable class that can be derived via a newtype?
I suspect the answer to 1. is: no, the situation cannot be salvaged, and it is impossible to obtain an instance of Traversable using DerivingVia.
As far as 2. goes, it's useful to try and reproduce the problem in a simpler context. Consider the following:
-- Remember to turn on ScopedTypeVariables!
data A = A
newtype B = B A
a :: forall f. f A -> f A
a = id
b :: forall f. f B -> f B
b = coerce $ a #f
It seems like this should work, but alas:
[typecheck -Wdeferred-type-errors] [E] • Couldn't match representation of type ‘f A’ with that of ‘f B’
arising from a use of ‘coerce’
NB: We cannot know what roles the parameters to ‘f’ have;
we must assume that the role is nominal
• In the expression: coerce $ a #f
In an equation for ‘b’: b = coerce $ a #f
• Relevant bindings include
b :: f B -> f B
The problem has to do with the "roles" of type constructors' parameters, and the way role inference works. For our purposes, roles come in two varieties: "representational" and "non-representational". Also for our purposes, the difference between the two can be approximated to the following: a type constructor F :: Type -> Type has a parameter of a "representational" role if there is an instance of Representational F, where:
type Representational :: (Type -> Type) -> Constraint
type Representational f = forall x y. Coercible x y => Coercible (f x) (f y)
Otherwise, the parameter of F is non-representational.
The typechecker lets you annotate the roles of type parameters in various places (although strangely enough, not in the kind). Sadly, there is no way to annotate the roles of a higher kinded type variable. What we can do however is just ask for Representational f directly:
b' :: forall f. Representational f => f B -> f B
b' = coerce $ a #f
which now typechecks. This suggests a possible way to tweak the Traversable typeclass to make it derivable via coercions.
Now let's look at the type of the Traversable operation sequenceA:
class Traversable t
where
sequenceA :: forall f. Applicative f => forall a. t (f a) -> f (t a)
{- ... -}
NB: There's that pesky forall f again, meaning f is taken to have a type parameter of a nominal role.
What DerivingVia is going to do is attempt to coerce between:
sequenceA #T1 :: forall f. Applicative f => forall a. T1 (f a) -> f (T2 a)
and:
sequenceA #T2 :: forall f. Applicative f => forall a. T2 (f a) -> f (T2 a)
Despite T1 (FoonessOf Bar) and T2 (Bar) being "parametrically" coercible, this coercion will fail, because the coercion of the entire operation will decompose eventually to the coercion the typechecker complained about:
Couldn't match representation of type
‘f1 (Foo Bar a1)’
with that of
‘f1 (Bar a1)’
This doesn't work because of f's parameter being considered to have a nominal role, as we discussed.
As with our simplified example above, the fix is straightforward: just ask for Representational f:
type Traversable' :: (Type -> Type) -> Constraint
class Traversable' t
where
traverse :: (Representational f, Applicative f) => (a -> f b) -> t (f a) -> f (t b)
And now at last we can derive an instance of Traversable' via the FoonessOf Bar:
instance Foo f => Traversable' (FoonessOf f)
where
traverse = _
deriving via (FoonessOf Bar) instance Traversable' Bar

Matching higher-kinded types in SYB

In general, I'm wondering if there's a way to write a generic fold that generalizes a function that applies a forall type like:
f :: forall a. Data (D a) => D a -> b
given some datatype D for which instance Data (D a) (possibly with constraints on a). To be concrete, consider something even as simple as False `mkQ` isJust, or generally, a query on the constructor of a higher-kinded datatype. Similarly, consider a transformation mkT (const Nothing) that only affects one particular higher-kinded type.
Without explicit type signatures, they fail with No instance for Typeable a0, which is probably the monomorphism restriction at work. Fair enough. However, if we add explicit type signatures:
t :: GenericT
t = mkT (const Nothing :: forall a. Data a => Maybe a -> Maybe a)
q :: GenericQ Bool
q = False `mkQ` (isJust :: forall a. Data a => Maybe a -> Bool)
instead we are told that the forall type of the outer signatures are ambiguous:
Could not deduce (Typeable a0)
arising from a use of ‘mkT’
from the context: Data a
bound by the type signature for:
t :: GenericT
The type variable ‘a0’ is ambiguous
I can't wrap my head around this. If I'm really understanding correctly that a0 is the variable in t :: forall a0. Data a0 => a0 -> a0, how is it any more ambiguous than in say mkT not? If anything, I would've expected mkT to complain because it is the one that interacts with isJust. Additionally, these functions are more polymorphic than the branching on concrete types.
I'm curious to know if this is a limitation of proving the inner constraint isJust :: Data a => ... — my understanding is that any type of instance Data inhabited with Maybe a must also have Data a to be valid by the instance constraint instance Data a => Data (Maybe a).
tldr: You need to create a different function.
mkT has the following signature:
mkT :: (Typeable a, Typeable b) => (a -> a) -> (b -> b)
And you want to apply it to a polymorphic function of type (forall x. Maybe x -> Maybe x). It is not possible: there is no way to instantiate a in (a -> a) to obtain (forall x. Maybe x -> Maybe x).
It's not just a limitation of the type system, the implementation of mkT wouldn't support such an instantiation either.
mkT simply compares concrete types a and b for equality at run time. But what you want is to be able to test whether b is equal to Maybe x for some x. The logic this requires is fundamentally more involved. But it is certainly still possible.
Below, mkT1 first matches the type b against the App pattern to know whether b is some type application g y, and then tests equality of g and f:
{-# LANGUAGE ScopedTypeVariables, RankNTypes, TypeApplications, GADTs #-}
import Type.Reflection
-- N.B.: You can add constraints on (f x), but you must do the same for b.
mkT1 :: forall f b. (Typeable f, Typeable b) => (forall x. f x -> f x) -> (b -> b)
mkT1 h =
case typeRep #b of
App g y ->
case eqTypeRep g (typeRep #f) of
Just HRefl -> h
_ -> id
_ -> id
Compilable example with mkQ1 as well:
{-# LANGUAGE ScopedTypeVariables, RankNTypes, TypeApplications, GADTs #-}
import Type.Reflection
mkT1 :: forall f b. (Typeable f, Typeable b) => (forall x. f x -> f x) -> (b -> b)
mkT1 h =
case typeRep #b of
App g y ->
case eqTypeRep g (typeRep #f) of
Just HRefl -> h
_ -> id
_ -> id
mkQ1 :: forall f b q. (Typeable f, Typeable b) => (forall x. f x -> q) -> (b -> q) -> (b -> q)
mkQ1 h =
case typeRep #b of
App g y ->
case eqTypeRep g (typeRep #f) of
Just HRefl -> const h
_ -> id
_ -> id
f :: Maybe x -> String
f _ = "matches"
main :: IO ()
main = do
print (mkQ1 f (\_ -> "doesn't match") (Just 3 :: Maybe Int)) -- matches
print (mkQ1 f (\_ -> "doesn't match") (3 :: Int)) -- doesn't match

Super classes with All from generics-sop

I'm trying to use All from generics-sop to constrain a list of types. Everything works as expected with simple classes like All Typeable xs, but I'd like to be able to do something like the following:
class (Typeable a) => TestClass (a :: k)
instance (Typeable a) => TestClass a
foo :: (All Typeable xs) => NP f xs -> z
foo = undefined
bar :: (All TestClass xs) => NP f xs -> z
bar = foo
This gives the error
Could not deduce: Generics.SOP.Constraint.AllF Typeable xs
arising from a use of ‘foo’
from the context: All TestClass xs
The generics-sop documentation states that
"All Eq '[ Int, Bool, Char ]
is equivalent to the constraint
(Eq Int, Eq Bool, Eq Char)
But in this case it doesn't seem to be, since
foo2 :: (Typeable a, Typeable b) => NP f '[a,b] -> z
foo2 = undefined
bar2 :: (TestClass a, TestClass b) => NP f '[a,b] -> z
bar2 = foo2
compiles fine.
My questions
1) Is this the expected behaviour?
2) If so, is there any workaround?
My use case for this is that I want to pass around a type level list of types constrained by a bunch of different classes under a single class name (like class (Typeable a, Eq a, Show a) => MyClass a) but also be able to call less specialised functions that only require some subset of those classes.
Searching for answers turned up superclasses aren't considered, but I don't think that is the issue here - I think it is something to do with the way the All constraint is put together in generics-sop. It is as if the compiler is simply comparing the two All constraints, rather than expanding them both and then type checking.
All f xs is actually equivalent to (AllF f xs, SListI xs). AllF is a type family:
type family AllF (c :: k -> Constraint) (xs :: [k]) :: Constraint where
AllF _ '[] = ()
AllF c (x:xs) = (c x, All c xs)
You see that it cannot reduce unless xs is in WHNF, so it gets stuck in your case. You can use mapAll:
import Generics.SOP.Dict
mapAll :: forall c d xs.
(forall a. Dict c a -> Dict d a) ->
Dict (All c) xs -> Dict (All d) xs
-- ::ish forall f g xs. (forall a. f a -> g a) -> All f xs -> All g xs
-- stores a constraint in a manipulatable way
data Dict (f :: k -> Constraint) (a :: k) where
Dict :: f a => Dict f a
bar :: forall xs f z. (All TestClass xs) => NP f xs -> z
bar = case mapAll #TestClass #Typeable #xs (\Dict -> Dict) Dict of
Dict -> foo
-- TestClass a -> Typeable a pretty trivially:
-- match Dict to reveal TestClass a
-- put the Typeable part of the TestClass instance into another Dict
-- We already know All TestClass xs; place that into a Dict
-- mapAll magic makes a Dict (All Typeable) xs
-- match on it to reveal
-- foo's constraint is satisfied

The use of Forall in Haskell

Data.Constraint.Forall provides some quantification over constraints, however I fail to see how it can be used. Consider the following:
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
module Forall where
import Prelude
import Data.Constraint.Forall
class Monoid (f a) => MonoidalFunctor f a
testfun :: Forall (MonoidalFunctor f) => (a -> f a) -> [a] -> f a
testfun = foldMap
testfun' :: Monoid (f a) => (a -> f a) -> [a] -> f a
testfun' = foldMap
I thought testfun would typecheck because Forall (MetaMonoid f) would work like forall a. Metamonoid f a, implying forall a. Monoid (f a) because of superclass constraint, but it doesn't.
Why does it not work and is there any workaround? I want to avoid having to write a lot of constraints like MyClass (f MyData) for different MyData types in my function where I know that any useful f will have instances for any f MyData anyway.
Use inst
inst :: forall p a. Forall p a :- p a
inst witness that, if you have forall a. p a, then you can set a to whatever you please and get p a out.
An entailment (:-) is
newtype a :- b = Sub (a => Dict b)
data Dict a = a => Dict
so, by pattern-matching on it, you can reveal the instance within it:
testfun :: forall f a. Forall (MonoidalFunctor f) => (a -> f a) -> [a] -> f a
testfun = case inst #(MonoidalFunctor f) #a of Sub Dict -> foldMap
(type applications/signatures necessary) or you can use (\\):
(\\) :: a => (b => r) -> a :- b -> r
testfun = foldMap \\ inst #(MonoidalFunctor f) #a
which reads "given that a is true and a value of r that also needs b to be true, followed by a value that can prove that b is true given a, make an r. If you rearrange a bit
(\\) :: (b => c) -> (a :- b) -> (a => c)
it looks quite a bit like function composition.
The reason for this dance is simply because it's beyond the reach of GHC to infer that Forall c means it can derive c a for any a; after all, that's why constraints exists. So, you have to be a bit more explicit about it.

When does GHCI not deduce the required context?

Why does GHCI add constraint for the required context here:
> let f = fmap show
> :t f
f :: (Functor f, Show a) => f a -> f String
But not here?
> :t over
over :: Lens s t a b -> (a -> b) -> s -> t
> :t _all'
_all' :: (Applicative f, Eq a) => a -> (a -> f a) -> [a] -> f [a]
> :t over (_all' 2)
<interactive>:1:7: error:
• Could not deduce (Applicative f) arising from a use of ‘_all'’
Is there any major difference between these two cases?
Note that isn't the type of over as defined by the lens library. That being said, your version doesn't work because Lens is
type Lens = forall f. Functor f => (a -> f b) -> s -> f t
and
_all' 2 :: forall f. Applicative f => ..
In other words, over is demanding a function which works for any Functor, but you've provided one which only works for Applicative (which is a stronger constraint, in that Functor doesn't imply Applicative).
Comment by #user2407038.
As alluded to in the comment, you can fix your problem by fixing the type signature for over. It should either be the complicated fancy thing in lens using distributive functors or the simpler
type Setter s t a b = (a -> Identity b) -> s -> Identity t
over :: Setter s t a b -> (a -> b) -> s -> t
As the identity type is both a functor and an applicative, it unifies with lenses and (I assume in your case) traversals.

Resources