Given the following code, removing forall a r from the type of go fails with "Overlapping instances for Typeable (D r)". I wonder why?
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
module M where
import Data.Typeable (Proxy, Typeable, cast)
class C r where
data D r :: *
deriving instance Typeable D
data A = forall r . A (D r)
go :: forall r a . (Typeable a, Typeable (D r)) => a -> Proxy r -> A
go a _ = case cast a of
Just (b :: D r) -> A b
Nothing -> error "fail to cast"
The error also says "The choice depends on the instantiation of r" - but isn't that pinned by the supplied Proxy r?
This is how scoped type variables work in Haskell. Note that you are re-using r here:
go :: forall r a . (Typeable a, Typeable (D r)) => a -> Proxy r -> A
go a _ = case cast a of
Just (b :: D r) -> A b -- this r is the same as the r above
Without the explicit forall, type variables are interpreted to be local to the signature. That is, your code is read as:
go :: (Typeable a1, Typeable (D r1)) => a1 -> Proxy r1 -> A -- renaming local variables
go a _ = case cast a of
Just (b :: D r) -> A b -- r bears no relation to r1
Hence the type error.
(It is confusing to get an Overlapping instances error, though.)
Related
I'm writing a distributed programming DSL and I'd like to allow implementations to choose their serialization method (if any, as it might not even be needed for a simulated execution).
Trying to solve this by adding a type family led to the problem below for a standard function I have. I imagine that it would work if I could require, and have the type checker understand, that if two values are serializable their pairing is also serializable. However, adding that as a quantified constraint doesn't seem to work. Can this be solved or is there a better solution for the problem?
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE TypeFamilies #-}
import Data.Kind
class (Monad (DistrM t)) => Distributed (t :: *) where
type Sendable t :: * -> Constraint
type DistrM t :: * -> *
-- ...
data FromSendable t a where
FromSendable :: (Sendable t b)
=> (b -> DistrM t a)
-> b
-> FromSendable t a
pairWith :: ( Sendable t a
, Distributed t
, forall a b. (Sendable t a, Sendable t b) => Sendable t (a,b)
)
=> a
-> FromSendable t b
-> FromSendable t (a,b)
pairWith a (FromSendable f b) =
FromSendable (\(a,b) -> (a,) <$> f b) (a,b)
-- >>> Could not deduce: Sendable t (a1, b1) ...
Edit 1
It type checks if I do
pairWith :: ( Sendable t a
, Distributed t
, st ~ Sendable t
, forall a b. (st a, st b) => st (a,b)
)
=> ...
It would get cumbersome to have to repeat these types of constraints, so I tried a type synonym but that doesn't work:
type Cs t = forall (st :: * -> Constraint).
(Sendable t ~ st, forall a b. (st a, st b) => st (a,b))
-- >>> Expected a constraint, but ‘st (a, b)’ has kind ‘*’
This looks weird. I only have a partial answer, but I'll post it anyway.
I simplified your code to
class C t where -- (*)
data T t where
T :: C t => (a -> t) -> a -> T t
foo ::
( C u
, forall a b . (C a , C b) => C (a, b) )
=> u -> T t -> T (u, t)
foo i (T f x) = T (\(a,b) -> (a, f b)) (i, x)
and, in this version, it compiles fine. However, if we replace
class C t where
with
type instance C :: * -> Constraint
then we get an error telling us that C (a, b) can not be deduced.
I can't completely understand what's going on here, but it looks like quantified constraints do not mix well with type families.
It looks like the above type family is treated like it were
type instance C (t :: *) :: Constraint
and in such case, I can't understand what's wrong. Since C now does not refer to a single type class, it is impossible to implement a quantified constraint like forall a b . (C a , C b) => C (a, b) by (say) passing a pointer to a specific instance, since the three C constraints could be anything at all, in an open world.
I still do not understand why type family C :: * -> Constraint is handled in the same way.
Perhaps GHC should reject quantified constraints involving type families ... -> Constraint in such way? I not sure.
I think you've pushed your code to the edges of GHC's type system here. You can fix the kind error on Cs by writing:
type Cs t = (forall (st :: * -> Constraint).
(Sendable t ~ st, forall a b. (st a, st b) => st (a,b))) :: Constraint
but then you run up against "GHC doesn't yet support impredicative polymorphism". Until GHC adds support for class families as per issue 14860, you're maybe out of luck with this approach.
However, you did ask about alternative approaches. Doesn't making Sendable t a a multiparameter type class accomplish basically the same thing?
Certainly, the following type-checks:
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE TypeFamilies #-}
import Data.Kind
class (Monad (DistrM t)) => Distributed (t :: *) where
type DistrM t :: * -> *
-- ...
class Sendable t a where
data FromSendable t a where
FromSendable :: (Sendable t b)
=> (b -> DistrM t a)
-> b
-> FromSendable t a
type Cs t = forall a b. (Sendable t a, Sendable t b) => Sendable t (a,b) :: Constraint
pairWith :: ( Sendable t a
, Distributed t
, Cs t
)
=> a
-> FromSendable t b
-> FromSendable t (a,b)
pairWith a (FromSendable f b) =
FromSendable (\(a,b) -> (a,) <$> f b) (a,b)
I was reading https://www.reddit.com/r/haskell/comments/gb7f9l/design_trade_offs_for_different_application/fp5kmjv and trying something out. I ran into an error I don't understand.
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableSuperClasses #-}
import Control.Monad
import Control.Monad.Reader.Class
import Data.Kind
newtype Y = Y { unY :: Int }
data Services c = Services
{ service1 :: forall m. c m => m Int
, object :: forall m. c m => m Y
}
myServices :: c ~ (MonadReader (Services c)) => Services c
myServices = Services
{ service1 = unY <$> join (asks object)
, object = pure $ Y 2
}
The errors all go eg.
* Could not deduce (Functor m) arising from a use of `<$>'
from the context: c ~ MonadReader (Services c)
bound by the type signature for:
myServices :: forall (c :: (* -> *) -> Constraint).
(c ~ MonadReader (Services c)) =>
Services c
or from: c m
bound by a type expected by the context:
forall (m :: * -> *). c m => m Int
However, it seems to me that
(c m, c ~ (MonadReader (Services c))
should mean
MonadReader (Services c) m
and as
Monad m => MonadReader r m where ...
then
Monad m -- implies Applicative m implies Functor m
What have I misunderstood? Is the fact that the two contexts are presented separately relevant somehow - why doesn't it have access to both contexts?
In my application I have a type definition that looks like this:
{-# LANGUAGE ExistentialQuantification #-}
class C a where
data A = forall a. C a => A { unA :: a }
I most definitely want the kind signature A :: Type.
I would like to generalize over the class constraint, like so:
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
import Data.Kind (Type, Constraint)
class C a where
data A' (c :: Type -> Constraint) where
A' :: forall a. c a => { unA :: a } -> A' c
type A = A' C
Note that A should be isomorphic to the A as defined in the beginning.
However GHC (8.6.5) rejects the generalized definition:
generalize.hs:11:19: error: Not in scope: type variable ‘c’
|
11 | A' :: forall a. c a => { unA :: a } -> A' c
| ^
generalize.hs:11:45: error: Not in scope: type variable ‘c’
|
11 | A' :: forall a. c a => { unA :: a } -> A' c
| ^
I don't understand the error, since I did enable ScopedTypeVariables.
Am I missing something obvious, or is what I'm trying to do not possible? If so, why not?
You got it almost right, what you want is
data HasConstraint (c :: Type -> Constraint) where
Pack :: forall (c :: Type -> Constraint) a . c a => { unPack :: a } -> HasConstraint c
so you have to include c as inside your constructor type signature as well because it's completely separate from the data declaration for GADTs.
I imagine you'd want to use it like this:
instance Show (HasConstraint Show) where show (Pack x) = show x
show (Pack 10 :: HasConstraint Show) -- => "10"
You could also write
withPacked :: forall (c :: Type -> Constraint) b. (forall a. c a => a -> b) -> HasConstraint c -> b
withPacked f (Pack x) = f x
withPacked #Show show (Pack 10) -- => "10"
I'm not sure if there's much else you can do with this though.
(Note that the "getter" unPack here actually isn't usable with GADTs either, you'll always have to pattern match on the constructor if you want to actually unpack things).
This is more or the less the functionality I want to implement:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE TypeInType #-}
type family ReturnType arr where
ReturnType (a -> b) = ReturnType b
ReturnType a = a
type family ReplaceReturnType t r where
ReplaceReturnType (a -> b) r = a -> ReplaceReturnType b r
ReplaceReturnType _ r = r
class CollectArgs f where
collectArgs :: ((forall r. ReplaceReturnType f r -> r) -> ReturnType f) -> f
instance CollectArgs f => CollectArgs (a -> f) where
collectArgs :: ((forall r. (a -> ReplaceReturnType f r) -> r) -> ReturnType f) -> a -> f
collectArgs f a = collectArgs (\ap -> f (\k -> ap (k a)))
instance (ReturnType a ~ a, ReplaceReturnType a dummy ~ dummy) => CollectArgs a where
collectArgs :: ((forall r. ReplaceReturnType a r -> r) -> a) -> a
collectArgs f = f id
What I eventually want to do with this is to write functions which are polymorphic in the number of incoming arguments, while they don't have to be part of a type class definition (which would correspond to printf var args style). So, for example:
wrapsVariadicFunction :: (CollectArgs f) => f -> Int -> f
wrapsVariadicFunction f config = collectArgs $ \apply ->
if odd config
then error "odd config... are you nuts?!"
else apply f
Only that the return type of f might not coicide with that of wrapsVariadicFunction.
Now, in a perfect world where I can associate a type class with a closed type family (a closed type class, so to speak), this would be easy to implement, because the connection ReplaceReturnType a r ~ r would be clear.
Since I can't state that connection, it is, quite understandably, not clear to GHC 8.2.1:
* Could not deduce: ReplaceReturnType a r ~ r
from the context: (ReturnType a ~ a,
ReplaceReturnType a dummy ~ dummy)
bound by the instance declaration
`r' is a rigid type variable bound by
a type expected by the context:
forall r. ReplaceReturnType a r -> r
Expected type: ReplaceReturnType a r -> r
Actual type: r -> r
* In the first argument of `f', namely `id'
In the expression: f id
In an equation for `collectArgs': collectArgs f = f id
* Relevant bindings include
f :: (forall r. ReplaceReturnType a r -> r) -> a
collectArgs :: ((forall r. ReplaceReturnType a r -> r) -> a) -> a
|
29 | collectArgs f = f id
|
A solution here would be universally quantifying over dummy in the instance context, but that's not possible (yet, judging from what I saw at ICFP). Also it's really cumbersome.
So, the actual question here is: How do I associate a value-level definition with a closed type family, much like a closed type class? Or is this impossible because types cannot be erased anymore? If so, is there some other workaround?
The standard trick to have these type classes that look like they are overlapping is to add a second parameter to the typeclass which will be distinct in each instance and whose value can be computed from the other ones.
The idea distilled to its very core is as follows (we need some scary extensions like UndecidableInstances but that's fine: we're writing total programs):
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
type family IsBase arr :: Bool where
IsBase (a -> b) = 'False
IsBase a = 'True
class SillyId a b where
sillyId :: IsBase a ~ b => a -> a
instance SillyId b (IsBase b) => SillyId (a -> b) 'False where
sillyId f = \x -> sillyId (f x)
instance SillyId b 'True where
sillyId t = t
Now, in your case it's a bit more complicated because you not only want this extra argument to do the dispatch, you also want other type level functions to reduce based on it. The trick is simply... to define these functions in terms of that dispatch!
Of course a type level Bool won't do anymore: you'll need to keep all of the information around. So instead of IsBase you'll have IsArrow:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE UndecidableInstances #-}
type family IsArrow arr :: Either (*, *) * where
IsArrow (a -> b) = 'Left '(a, b)
IsArrow a = 'Right a
type family ReturnType arr where
ReturnType ('Left '(a, b)) = ReturnType (IsArrow b)
ReturnType ('Right a) = a
type family ReplaceReturnType t r where
ReplaceReturnType ('Left '(a, b)) r = a -> ReplaceReturnType (IsArrow b) r
ReplaceReturnType _ r = r
class CollectArgs f (f' :: Either (*, *) *) where
collectArgs :: IsArrow f ~ f' => ((forall r. ReplaceReturnType f' r -> r) -> ReturnType f') -> f
instance CollectArgs f (IsArrow f) => CollectArgs (a -> f) ('Left '(a, f)) where
collectArgs :: ((forall r. (a -> ReplaceReturnType (IsArrow f) r) -> r) -> ReturnType (IsArrow f)) -> a -> f
collectArgs g a = collectArgs (\ap -> g (\k -> ap (k a)))
instance CollectArgs a ('Right a) where
collectArgs :: IsArrow a ~ 'Right a => ((forall r. ReplaceReturnType (IsArrow a) r -> r) -> a) -> a
collectArgs f = f id
And voilà. You can of course define type synonyms for ReplaceReturnType (IsArrow a) r to make the notations a bit lighter but that's the gist of it.
Is it possible to define a instance constrain for "not a monad", in order to define two non-overlapping instances, one for monadic values, other for non-monadic values?
A simplified example:
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverlappingInstances #-}
class WhatIs a b | a -> b where
whatIs :: a -> b
instance (Show a) => WhatIs a String where
whatIs = show
instance (Monad m, Functor m, Show a) => WhatIs (m a) (m String) where
whatIs x = fmap show x
main :: IO ()
main = do
let x = 1 :: Int
putStrLn "----------------"
{-print $ whatIs (1::Int)-}
print $ whatIs (Just x)
putStrLn "--- End --------"
So, I use the FunctionalDependencies to avoid type annotations, but of course, the compiler complains with
Functional dependencies conflict between instance declarations:
instance [overlap ok] Show a => WhatIs a String
-- Defined at test.hs:10:10
instance [overlap ok] (Monad m, Functor m, Show a) =>
WhatIs (m a) (m String)
-- Defined at test.hs:13:10
Because a can assume the value m a, and thus a conflict arises.
However, if I could replace the first instance with something like:
instance (NotAMonad a, Show a) => WhatIs a String where
whatIs = show
This problem would not present itself.
So far I've found this very old email that seems to propose a somewhat related solution, but I'm not sure if there are new techniques to address this...
I also found the constraints package, which I'm sure has useful functions for this case, but it is sorely lacking in (simple) examples.
Any clues?
Edit: after user2407038 correct answer.
So, I tried user2407038 answer below, and indeed I managed to compile the provided example. The conclusion? I should not have simplified the example so much. After some thinkering with my actual example, I was able to reduce it to this:
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module IfThenElseStackExchange where
class IfThenElse a b c d | a b c -> d where
ifThenElse :: a -> b -> c -> d
instance (Monad m) => IfThenElse (m Bool) (m b) (m b) (m b) where
ifThenElse m t e = do
b <- m
if b then t else e
instance (Monad m) => IfThenElse (m Bool) b b (m b) where
ifThenElse ma t e = do
a <- ma
return $ if a then t else e
But I still getting the dreaded Functional dependencies conflict between instance declarations error. Why? The part after the => (the instance head, as user2407038 promptly noted) is in fact quite different, thus it does not even qualify for OverlappingInstances, as the compiler can choose the most specific one.
Then what?
The error is, as always, hinted by the error message. The a b c d | a b c -> d part is not being respected by the code above. So I finally tried this instead:
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module IfThenElseStackExchange where
class IfThenElse a b c d | a b c -> d where
ifThenElse :: a -> b -> c -> d
instance (Monad m, c ~ b) => IfThenElse (m Bool) (m b) (m b) (m c) where
ifThenElse m t e = do
b <- m
if b then t else e
instance (Monad m, c ~ b) => IfThenElse (m Bool) b b (m c) where
ifThenElse ma t e = do
a <- ma
return $ if a then t else e
Et voilà!
By using (m b) in the last parameter, I was trying to indicate that the final result has the same type as the second and third parameter. But the problem seems to be that the FunctionalDependencies extension does not make the same kind of instance choosing on types as OverlappingInstances, and thus considers b and (m b) "the same" for its purposes. Is this interpretation correct, or am I still missing something?
I can still 'tell' the compiler that c is of the same type as b using the constrain c ~ b, and thus reaching the intended result.
After reading some more material about this, I highly recomend reading this article by Oleg where he generalizes his former solutions that both I and user2407038 linked. I found it quite accessible.
If my interpretation of the FunctionalDependencies above is correct, and TypeFamilies being presented as a more flexible solution for the same problem domain, I wonder if could use them to solve this in another way. Oleg solution mentioned above sure uses them, of course.
You don't need a NotAMonad class, or rather, WhatIs is exactly this class already.
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances,
TypeFamilies, UndecidableInstances, IncoherentInstances #-}
class WhatIs a b | a -> b where
whatIs :: a -> b
instance (Show a, b ~ String) => WhatIs a b where
whatIs = show
instance (Monad m, Functor m, Show a) => WhatIs (m a) (m String) where
whatIs x = fmap show x
You don't strictly need IncoherentInstances but if you want things like whatIs (1 :: Num a => a) to work you need it.
This probably isn't the best way of doing what you want but your use case isn't clear.
Edit: more explanation:
First of all: these instances are not overlapping! I included the full list of language pragmas. The error you got said that "Functional dependencies conflict between instance declarations". Say you have the following:
class C a b | a -> b
Supposed you have two instances of C: C a b, C c d (here a is not a rigid type variable; it is just any haskell type). If a is an instantiation of c (or vice versa) then b must be an instantiation of d. This general rule may be somewhat abstract so lets look at your code:
instance (Show a) => WhatIs a String where
instance (Monad m, Functor m, Show a) => WhatIs (m a) (m String) where
Since a is any type, you are declaring that 'for any type a, whatIs a == String'.
The second instance declares that 'for any type (m a), whatIs (m a) == (m String)'. Obviously m a is an instantiation of a (any type is an instantiation of a free type variable) but String is never an instantiation of m String.
Why does any of this matter? When the compiler checks to see if fundeps conflict, it only looks at the instance head; that is, the portion to the right of =>. Therefore,
instance (Show a, b ~ String) => WhatIs a b where
is saying 'for any types a,b, whatIs a == b'. Obviously, since a and b are both free variables, they can be instantiated with any other type. So if a == (m a0) you can freely say that b == (m String). The fact that b must be a string becomes known if and only if the first instance is the one that is matched.
Since any types match a and b, WhatIs (IO ()) b matches the first instance. The second instance is used because the compiler will try to match the instances in order of specificity. You can find the 'rules' for determining specificity here.. The simple explanation is that WhatIs a b matches more things so it is more general, and will be used later. (In fact, the instance C a0 a1 a2 .. an where a* is a distinct type variable, is the most general instance and will always be tried last)
Edit: Here is the general solution to your problem. With this code, whatIs = f_map show.