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).
Related
The following does not work in Haskell-
{-# LANGUAGE GADTs, DataKinds, TypeFamilies, UndecidableInstances,
RankNTypes, PolyKinds #-}
import Data.Kind
data Payload :: (f :: a -> Type) -> (e :: a) -> Type where
MkPayload :: (e :: a) -> (t :: f e) -> Payload f e
payload :: Payload f e -> f e
payload (MkPayload e t) = t
• Expecting one more argument to ‘f :: a -> Type’
Expected a type, but ‘f :: a -> Type’ has kind ‘a -> Type’
• In the kind ‘(f :: a -> Type) -> (e :: a) -> Type’
In the data type declaration for ‘Payload’
|
6 | data Payload :: (f :: a -> Type) -> (e :: a) -> Type where
| ^^^^^^^^^^^^^^
Is there any other way someone can define dependent types in Haskell?
You cannot use that _ :: _ notation in types as you might be used to from Agda. Instead just leave the names out and just write the types:
{-# LANGUAGE GADTs, DataKinds, TypeFamilies, UndecidableInstances,
RankNTypes, PolyKinds #-}
import Data.Kind ( Type )
data Payload :: (a -> Type) -> a -> Type where
MkPayload :: a -> f e -> Payload f e
payload :: Payload f e -> f e
payload (MkPayload e t) = t
That CUSK (complete user specified kind) notation is discouraged, instead you should use standalone kind signatures:
{-# LANGUAGE GADTs, DataKinds, TypeFamilies, UndecidableInstances,
RankNTypes, PolyKinds, StandaloneKindSignatures #-}
import Data.Kind ( Type )
type Payload :: (a -> Type) -> a -> Type
data Payload f e where
MkPayload :: a -> f e -> Payload f e
payload :: Payload f e -> f e
payload (MkPayload e t) = t
Leaving out the names does mean you lose some expressivity, but that isn't required for this example. There are techniques to recover most of that expressivity such as singletons that David Young mentioned.
Haskell hobbyist here - is it possible to map a polymorphic constant over a list of types in a generic way?
More precisely, consider this snippet:
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
module Main where
import Data.Kind( Type )
class HasName a where name :: String
instance HasName Int where name = "Int"
instance HasName Double where name = "Double"
class AllNames (ts :: [Type]) where
allNames :: [String]
instance AllNames '[] where
allNames = []
instance (HasName t, AllNames rest) => AllNames (t ': rest) where
allNames = name #t : allNames #rest
main :: IO ()
main = print $ allNames #'[Int, Double]
Unsurprisingly, this works as expected, i.e., it prints ["Int","Double"]. However, if I try to generalize the pattern above so that the code can be used with any name-like polymorphic constant, then I run into problems.
Here's my attempt at generalization:
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
module Main where
import Data.Kind( Constraint, Type )
--
-- Intended to be the generalized version of 'name'
--
type PolymorphicConstant (c :: Type -> Constraint) (a :: Type) = forall t . c t => a
--
-- Intended to be the generalized version of 'AllNames'
--
class AllPolymorphicConstants (c :: Type -> Constraint) (ts :: [Type]) (a :: Type) where
allPolymorphicConstants :: PolymorphicConstant c a -> [ a ]
instance AllPolymorphicConstants c '[] a where
allPolymorphicConstants _ = []
instance (c t, AllPolymorphicConstants c rest a) => AllPolymorphicConstants c (t ': rest) a where
allPolymorphicConstants f = f #t : allPolymorphicConstants #c #rest #a f
Alas, this doesn't compile (btw, I'm using GHC 8.10.7):
Main.hs:31:74: error:
• Could not deduce: c t0 arising from a use of ‘f’
from the context: (c t, AllPolymorphicConstants c rest a)
bound by the instance declaration at Main.hs:30:10-91
or from: c t1
bound by a type expected by the context:
PolymorphicConstant c a
at Main.hs:31:74
• In the fourth argument of ‘allPolymorphicConstants’, namely ‘f’
In the second argument of ‘(:)’, namely
‘allPolymorphicConstants #c #rest #a f’
In the expression: f #t : allPolymorphicConstants #c #rest #a f
• Relevant bindings include
f :: PolymorphicConstant c a (bound at Main.hs:31:27)
allPolymorphicConstants :: PolymorphicConstant c a -> [a]
(bound at Main.hs:31:3)
|
31 | allPolymorphicConstants f = f #t : allPolymorphicConstants #c #rest #a f
| ^
I'm probably missing something basic that makes this sort of generalization impossible - but what is it exactly?
I wish someone can prove me wrong, but this is one of the few corner cases where we meet a limitation of current GHC and we can not get away from using Proxy, Tagged or similar "relics" of the past.
A minimal example
Let's consider a simpler example:
class C a where
-- Ambiguous type
type T a = forall t. C t => a
Note that, if we have a value x :: T a, there is no unambiguous way to use it except with an explicit type argument x #t. That's the price to pay for using ambiguous types, but that's fine.
The following code type-checks as expected.
foo :: forall t a. (C t) => T a -> a
foo f = f #t
Instead, this does not.
-- Error: Could not deduce (C t0) arising from a use of `foo'
foo2 :: forall t a. (C t) => T a -> a
foo2 = foo
This might be surprising at first. Indeed, foo2 has exactly the same type of foo, so foo2=foo is obviously OK! Yet, it fails. The reason is, once again, ambiguous types, and the thumb rule still is: if something has an ambiguous type, we can not use it if we don't pass additional #t #a arguments.
Doing that makes everything work.
foo3 :: forall t a. (C t) => T a -> a
foo3 = foo #t #a
The above is a little strange since we can't write (and have not to write) foo3 #t #a = foo #t #a. I guess that if GHC forced us to do that, then it could also allow us to "eta-contract the type arguments" everything and write foo3 = foo.
Now, what if we eta-expand the value argument (not the type!). We get an error:
-- Error: Could not deduce (C t0) arising from a use of `x'
foo4 :: forall t a. (C t) => T a -> a
foo4 x = foo #t #a x
Groan. This is simply foo3 = foo #t #a eta-expanded. What's going wrong here? Well, the issue is the same: now we introduced x :: T a, and that's an ambiguous type, so we can not use x without #... arguments. Even if foo expects a polymorphic value!
Here we find ourselves unable to escape. GHC sees the polymorphic arguments and adds an implicit type argument abstraction over x, adding (\#t0 -> ... in front. But that's a kind of syntax we are not allowed to use, and there is no way to capture the fresh type variable t0. In other words, we would like to write
foo4 :: forall t a. (C t) => T a -> a
foo4 x = foo #t #a (\#t0 -> x #t0)
but we can only write
foo4 :: forall t a. (C t) => T a -> a
foo4 x = foo #t #a (x #something)
and there is no way to mention t0 there. Sigh.
Using proxies
The only "solution" I can see it to use Proxy (or similar relics), and avoid ambiguous types.
-- No longer ambiguous
type T a = forall t. C t => Proxy t -> a
foo :: forall t a. (C t) => T a -> a
foo f = f (Proxy #t)
foo4 :: forall t a. (C t) => T a -> a
foo4 x = foo #t #a x
Now we can use x as-is since it has no longer an ambiguous type.
Using Tagged or wrapping the polymorphic value inside a newtype or data would also work, since it makes the type no longer ambiguous.
The original code, proxy-fied
type PolymorphicConstant (c :: Type -> Constraint) (a :: Type)
= forall t . c t => Proxy t -> a
--
-- Intended to be the generalized version of 'AllNames'
--
class AllPolymorphicConstants (c :: Type -> Constraint) (ts :: [Type]) (a :: Type) where
allPolymorphicConstants :: PolymorphicConstant c a -> [ a ]
instance AllPolymorphicConstants c '[] a where
allPolymorphicConstants _ = []
instance (c t, AllPolymorphicConstants c rest a)
=> AllPolymorphicConstants c (t ': rest) a where
allPolymorphicConstants f = f (Proxy #t) : allPolymorphicConstants #c #rest #a f
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 am using makeFields from lens to generate fields overloaded for various structures. I would like to use these fields at one with multiple structures while having to state which field I want to use only once. It would look like this:
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-}
import Control.Lens
data A = A
{ _aX :: String
, _aY :: String
}
makeFields ''A
data B = B
{ _bX :: String -> Char
, _bY :: String -> Bool
}
makeFields ''B
-- x can get _aX from an A and _bX from a B
a :: A
a = undefined
b :: B
b = undefined
q :: (Getter A String) AND (Getter B (String -> a)) -> a
q lens = (b^.lens) (a^.lens)
Which type should I give q? I tried letting GHC infer the types, but that failed.
To decide what is to be done, we need to know what the types of your (makeField-generated) fields are:
GHCi> :t x
x :: (HasX s a, Functor f) => (a -> f a) -> s -> f s
So the abstraction covering all your x-bearing types (the abstraction I was whining about before noticing you were using makeFields) is a multi-parameter type class HasX, and similarly for the other fields. That gives us enough to use x with different types in a single implementation:
-- Additional extension required: FlexibleContexts
-- Note that GHC is able to infer this type.
qx :: (HasX t (a -> b), HasX s a) => t -> s -> b
qx t s = (t ^. x) (s ^. x)
GHCi> import Data.Maybe
GHCi> let testA = A "foo" "bar"
GHCi> let testB = B (fromMaybe 'ø' . listToMaybe) null
GHCi> qx testB testA
'f'
That, however, is not quite what you asked for. You wanted something like:
q xOrY b a = (b^.xOrY) (a^.xOrY)
Achieving that, however, requires abstracting over the classes HasX, HasY, etc. Doing so is, in fact, somewhat feasible thanks to the ConstraintKinds extension, as demonstrated in Could we abstract over type classes? Here it goes:
-- Additional extensions required: ConstraintKinds, ScopedTypeVariables
-- Additional import required: Data.Proxy
-- GHC cannot infer this type.
q :: forall h s t a b. (h t (a -> b), h s a) => Proxy a -> Proxy h
-> (forall u c. h u c => Getting c u c) -> t -> s -> b
q _ _ l t s =
(t ^. (l :: Getting (a -> b) t (a -> b))) (s ^. (l :: Getting a s a))
GHCi> q (Proxy :: Proxy String) (Proxy :: Proxy HasX) x testB testA
'f'
The first proxy, which determines the intermediate type, is necessary unless you give up this bit of generality and replace a by String. Additionally, you have to specify the field twice, both by passing the getter as an argument and through the second proxy. I am not at all convinced that this second solution is worth the trouble -- the extra boilerplate of having to define qx, qy, etc. looks quite a bit less painful than all the circuitousness involved here. Still, if any of you who are reading this would like to suggest an improvement, I'm all ears.
I have some contrived type:
{-# LANGUAGE DeriveFunctor #-}
data T a = T a deriving (Functor)
... and that type is the instance of some contrived class:
class C t where
toInt :: t -> Int
instance C (T a) where
toInt _ = 0
How can I express in a function constraint that T a is an instance of some class for all a?
For example, consider the following function:
f t = toInt $ fmap Left t
Intuitively, I would expect the above function to work since toInt works on T a for all a, but I cannot express that in the type. This does not work:
f :: (Functor t, C (t a)) => t a -> Int
... because when we apply fmap the type has become Either a b. I can't fix this using:
f :: (Functor t, C (t (Either a b))) => t a -> Int
... because b does not represent a universally quantified variable. Nor can I say:
f :: (Functor t, C (t x)) => t a -> Int
... or use forall x to suggest that the constraint is valid for all x.
So my question is if there is a way to say that a constraint is polymorphic over some of its type variables.
Using the constraints package:
{-# LANGUAGE FlexibleContexts, ConstraintKinds, DeriveFunctor, TypeOperators #-}
import Data.Constraint
import Data.Constraint.Forall
data T a = T a deriving (Functor)
class C t where
toInt :: t -> Int
instance C (T a) where
toInt _ = 0
f :: ForallF C T => T a -> Int
f t = (toInt $ fmap Left t) \\ (instF :: ForallF C T :- C (T (Either a b)))