I'm messing around with the SPECIALIZE pragma while trying to find a solution to this problem.
I came up with this example:
{-# LANGUAGE FlexibleContexts, GeneralizedNewtypeDeriving #-}
import Data.Vector
import qualified Data.Vector.Generic as V
class Foo a
newtype Phantom m = T Int deriving (Show)
instance (Foo m) => Num (Phantom m)
f :: (Num r, V.Vector v r) => v r -> v r -> v r
{-# SPECIALIZE f :: (Foo m) => Vector (Phantom m) -> Vector (Phantom m) -> Vector (Phantom m) #-}
f x y = V.zipWith (+) x y
main = print "hello"
which fails to compile (GHC 7.6.2) because
Forall'd constraint `Foo m' is not bound in RULE lhs.
Googling only turned up a couple of GHC bug reports from years ago. I didn't see anything about "forall'd constraints" while reading about SPECIALIZE or RULE. My specialize signature does seem less polymorphic than the original, and it satisfies the "if-and-only-if" rule.
replace with
{-# SPECIALIZE f :: (Num (Phantom m)) => Vector (Phantom m) -> Vector (Phantom m) -> Vector (Phantom m) #-}
and it will work. The r in Num r is Phantom m not m, thus you can't add the constraint Num m. This is logical--Num (Phantom m) does not imply Num m and you could get other instances under the open world assumption.
EDIT:
You actually don't need any constraint at all in this case
{-# SPECIALIZE f :: Vector (Phantom m) -> Vector (Phantom m) -> Vector (Phantom m) #-}
anyway, the basic problem if I understand what you are trying to do is that you can't constrain when you perform an optimization based on phantom type parameters.
Related
I'm trying to make my use of Finites completely safe and non-partial, by using Proxys in place of Integers like so:
-- SO test case, re: my use of ghc-typelits-natnormalise package.
--
-- David Banas <capn.freako#gmail.com>
-- February 9, 2018
{-# OPTIONS_GHC -Wall #-}
{-# OPTIONS_GHC -fplugin GHC.TypeLits.Normalise #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeOperators #-}
module Bogus.NewFin where
import GHC.TypeLits
import Data.Proxy
import Data.Finite
import Data.Finite.Internal (Finite(..))
import Data.Reflection
-- A safer form of `finite`.
finite' :: (KnownNat n, KnownNat m, n `CmpNat` m ~ 'GT) => Proxy m -> Finite n
finite' p = Finite $ natVal p
-- A safer form of `getFinite`.
getFinite' :: KnownNat n => Finite n -> (forall m. (KnownNat m, n `CmpNat` m ~ 'GT) => Proxy m -> r) -> r
getFinite' x f = reifyNat (getFinite x) f
And I'm getting this compiler error:
Davids-Air-2:test dbanas$ stack ghc -- -c so_natnorm.hs
so_natnorm.hs:28:41: error:
• Couldn't match type ‘CmpNat n n1’ with ‘'GT’
arising from a use of ‘f’
• In the second argument of ‘reifyNat’, namely ‘f’
In the expression: reifyNat (getFinite x) f
In an equation for ‘getFinite'’:
getFinite' x f = reifyNat (getFinite x) f
• Relevant bindings include
f :: forall (m :: Nat).
(KnownNat m, CmpNat n m ~ 'GT) =>
Proxy m -> r
(bound at so_natnorm.hs:28:14)
x :: Finite n (bound at so_natnorm.hs:28:12)
getFinite' :: Finite n
-> (forall (m :: Nat).
(KnownNat m, CmpNat n m ~ 'GT) =>
Proxy m -> r)
-> r
(bound at so_natnorm.hs:28:1)
I'm guessing that my problem is trying to relate a universally and an existentially quantified type, through the mechanisms provided by the ghc-typelits-natnormalise package. Is that correct?
It seems to me that this ought to be allowed, since the caller is responsible for assigning both:
the value of n, and
the maximum value of m.
Where is my reasoning about this faulty?
reifyNat takes as an argument a function which works for any natural. A function of type forall m. (KnownNat m, n `CmpNat` m ~ 'GT) => Proxy m -> r doesn't work on any natural; it only works on naturals less than some other n.
Since you are calling getFinite to produce the actual value, you know that value is less than n. Unfortunately, you have no way to prove this to the typechecker. Fortunately, you are allowed to tell the typechecker to trust you:
import Type.Reflection ((:~:)(..))
import Unsafe.Coerce
...
getFinite'' :: KnownNat n => Finite n -> (forall m. (KnownNat m) => Proxy m -> n `CmpNat` m :~: 'GT -> r) -> r
getFinite'' x f = reifyNat (getFinite x) $ \p -> f p (unsafeCoerce Refl)
getFinite' :: forall n r . KnownNat n => Finite n -> (forall m. (KnownNat m, n `CmpNat` m ~ 'GT) => Proxy m -> r) -> r
getFinite' x f = getFinite'' x $ \p Refl -> f p
Given the following code
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE PolyKinds #-}
type family Tagged (m :: * -> *) :: k
class Example (t :: k) (a :: *) where
type Return t a
a :: (Monad m, Tagged m ~ t) => a -> m (Return t a)
data A
data A' a
data B = B
instance Example A B where
type Return A B = ()
a B = return ()
-- This is why I want a PolyKinded 't'
instance Example A' B where
type Return A' B = ()
a B = return ()
I get the type error (pointing to the line a :: (Monad m ...)
• Could not deduce: Return (Tagged m) a ~ Return t a
from the context: (Example t a, Monad m, Tagged m ~ t)
bound by the type signature for:
a :: (Example t a, Monad m, Tagged m ~ t) =>
a -> m (Return t a)
...
Expected type: a -> m (Return t a)
Actual type: a -> m (Return (Tagged m) a)
NB: ‘Return’ is a type function, and may not be injective
The type variable ‘k0’ is ambiguous
• In the ambiguity check for ‘a’
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
When checking the class method:
a :: forall k (t :: k) a.
Example t a =>
forall (m :: * -> *).
(Monad m, Tagged m ~ t) =>
a -> m (Return t a)
In the class declaration for ‘Example’
I can introduce an argument to a with Proxy t and this will work provided I give the signature at the call site: test = a (Proxy :: Proxy A) B but this is what I'm looking to avoid. What I'd like is
newtype Test t m a = Test
{ runTest :: m a
} deriving (Functor, Applicative, Monad)
type instance Tagged (Test t m) = t
test :: Monad m => Test A m ()
test = a B
I want t to be found from the context Test A m () using the type instance. It seems that it should be possible given the module will compile after removing the kind annotations, PolyKinds, and the instance for A'. Where is k0 coming from?
I suppose the workaround would be to drop PolyKinds and use extra data types like data ATag; data A'Tag; data BTag etc.
This is a partial answer, only.
I tried to make the kind explicit.
type family Tagged k (m :: * -> *) :: k
class Example k (t :: k) (a :: *) where
type Return k (t :: k) (a :: *)
a :: forall m . (Monad m, Tagged k m ~ t) => a -> m (Return k t a)
And, after enabling many extensions, observed this:
> :t a
a :: (Example k (Tagged k m) a, Monad m) =>
a -> m (Return k (Tagged k m) a)
Hence, the compiler is complaining because the instance Example k (Tagged k m) a can not be determined by a,m alone. That is, we do not know how to choose k.
I guess that, technically, we might have different Example k (Tagged k m) a instances, e.g. one for k=* and another for k=(*->*).
Intuitively, knowing t should allow us to find k, but Return being non injective prevents us to find t.
I have a type alias for a monad transformer stack:
type KStat s a = ReaderT (KStatRoot s) (ExceptT KindError (ST s)) a
I need to abstract users away from this type, largely because the KStatRoot structure was causing cyclic dependencies. I therefore created a separate module and defined a typeclass for it:
class (Monad (m s), MonadError KindError (m s)) =>
MStat m s where
liftToST :: ST s a -> m s a
kstatNewRef :: a -> m s (STRef s a)
kstatReadRef :: STRef s a -> m s a
kstatWriteRef :: STRef s a -> a -> m s ()
This definition compiles OK (albeit needing {-# LANGUAGE MultiParamTypeClasses,FlexibleContexts #-} to work, but I can see why both of those are required), and I've been able to convert some use sites to the typeclass and have them type check, so everything seems OK there. But I'm struggling to work out how to define my instance for the class:
instance MStat (KStat s a) s where
liftToST = lift . lift
kstatNewRef = liftToST . newSTRef
kstatReadRef = liftToST . readSTRef
kstatWriteRef r v = liftToST $ writeSTRef r v
gives me the error:
src/KindLang/Data/KStat.hs:27:17:
The first argument of ‘MStat’ should have kind ‘* -> * -> *’,
but ‘KStat s a’ has kind ‘*’
In the instance declaration for ‘MStat (KStat s a) s’
which kind of makes sense, but then if I change KStat s a to KStat in the instance header I get this error:
src/KindLang/Data/KStat.hs:27:10:
Type synonym ‘KStat’ should have 2 arguments, but has been given none
In the instance declaration for ‘MStat KStat s’
which seems to basically saying the exact opposite.
I'm using these language extensions in the module I declare the instance:
{-# LANGUAGE RankNTypes, TypeSynonymInstances, FlexibleInstances, MultiParamTypeClasses #-}
How do I resolve these errors?
Complete file demonstrating the errors follows:
{-# LANGUAGE RankNTypes, TypeSynonymInstances, FlexibleContexts,
FlexibleInstances, MultiParamTypeClasses #-}
import Control.Monad.Except
import Control.Monad.ST
import Control.Monad.Reader
import Data.STRef
data KStatRoot s = KStatRoot
data KindError
class (Monad (m s), MonadError KindError (m s)) =>
MStat m s where
liftToST :: ST s a -> m s a
kstatNewRef :: a -> m s (STRef s a)
kstatReadRef :: STRef s a -> m s a
kstatWriteRef :: STRef s a -> a -> m s ()
type KStat s a = ReaderT (KStatRoot s) (ExceptT KindError (ST s)) a
instance MStat (KStat s m) s where
liftToST = lift . lift
kstatNewRef = liftToST . newSTRef
kstatReadRef = liftToST . readSTRef
kstatWriteRef r v = liftToST $ writeSTRef r v
The first error is "correct" (you need to use a type of two arguments in the instance declaration), and your attempted fix makes sense.
However, a type synonym doesn't really exist without its arguments. That is, after
type Foo a = ...
you can't use Foo by itself. Foo has to be applied to an argument in order to be processed by the type checker. This is the cause of your second error.
The only workaround I see is changing KStat to a newtype:
newtype KStat s a = KStat{ runKStat :: ReaderT (KStatRoot s) (ExceptT KindError (ST s)) a }
That will let you use KStat without arguments. You'll just have to add explicit runKStat/KStat conversions everywhere.
So I typed this up
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, FunctionalDependencies #-}
import Data.Monoid
class Monoid m => Mconcat m a | a -> m where
mcon :: m -> a
instance Monoid m => Mconcat m m where
mcon m = m
instance Mconcat m a => Mconcat m (m -> a) where
mcon m m' = mcon (m `mappend` m')
and I get
[1 of 1] Compiling Main ( pad.hs, interpreted )
pad.hs:8:10:
Functional dependencies conflict between instance declarations:
instance Monoid m => Mconcat m m -- Defined at pad.hs:8:10
instance Mconcat m a => Mconcat m (m -> a)
-- Defined at pad.hs:11:10
Failed, modules loaded: none.
The thing is though, m and m->a cannot be equal! Why is it giving me a conflict? (Also, any tips as to how to make a polyvariadic mconcat (in the style of the printf library)?
You can make this compile and do what you want by changing the second instance declaration and adding UndecidableInstances:
{-# LANGUAGE UndecidableInstances #-}
...
instance {-# OVERLAPS #-} (Mconcat m a, o ~ (m -> a)) => Mconcat m o where
or on earlier versions of GHC this should work (untested)
{-# LANGUAGE UndecidableInstances, OverlappingInstances #-}
...
instance (Mconcat m a, o ~ (m -> a)) => Mconcat m o where
This works because GHC only looks at the head of an instance to determine if it satisfies the coverage condition, but you can (almost) always achieve the same instance by making this transformation. It even handles polymorphic cases!
>:set +t
>mcon [1] [2] [34,34,2,53] [34,23,43]
[1,2,34,34,2,53,34,23,43]
it :: Num t => [t]
>mcon "a" "b" "c" "d" "e"
"abcde"
it :: [Char]
Question. Is there any way to make this code work without an explicit type signature?
Code. First, I have a in-practice-much-nicer alternate MonadTrans class, inspired by Data.Newtype. It looks like this,
{-# LANGUAGE FlexibleContexts, TypeFamilies #-}
module Alt.Control.Monad.Trans where
import Control.Monad
class (Monad 𝔪, Monad (BaseMonad 𝔪)) => MonadTrans (𝔪 :: * -> *) where
type BaseMonad 𝔪 :: * -> *
lift :: (BaseMonad 𝔪) α -> 𝔪 α
Then, I have a class A with method foo, and if some base monad M is an A, then any transformed monad T M is also an A. In code,
class A 𝔪 where
foo :: String -> 𝔪 ()
instance (A (BaseMonad 𝔪), MonadTrans 𝔪) => A 𝔪 where
foo n = lift $ foo n
However, if I now want to create a shortcut for foo with its first argument substituted, then I need an explicit type signature, or the compiler's context stack overflows.
minimize_call :: A 𝔪 => 𝔪 ()
minimize_call = foo "minimize"
Possible info to help inference. Let's say we have an associated type B :: * -> *. I'm thinking that I want to tell the compiler B satisfies B t /= t, B (B t) /= B t, etc. i.e. B is somehow "monotonic" -- that chasing associated types is equivalent to removing newtype wrappers, and it should know that it cannot remove newtype wrappers forever, hence adding the context A to the signature is necessary.
Yes, there is a way. Provide a grounded instance for A, and add NoMonomorphismRestriction to the language pragma (in addition to the also required FlexibleInstances and UndecidableInstances).
However, the A class will be unusable. There is no way for the compiler to know that there never will be a MonadTrans instance with BaseMonad m = m. Thus it cannot select an instance, ever, because it cannot know whether to use the instance from here or another one.
{-# LANGUAGE FlexibleContexts, TypeFamilies, FlexibleInstances, UndecidableInstances, NoMonomorphismRestriction #-}
module Trans (MonadTrans(..), A(..), minimize_call) where
import Control.Monad
class (Monad m, Monad (BaseMonad m)) => MonadTrans (m :: * -> *) where
type BaseMonad m :: * -> *
lift :: (BaseMonad m) α -> m α
class A m where
foo :: String -> m ()
data Foo a = Bork
instance Monad Foo where
return _ = Bork
_ >>= _ = Bork
instance A Foo where
foo _ = Bork
instance (A (BaseMonad m), MonadTrans m) => A m where
foo n = lift $ foo n
-- minimize_call :: A m => m ()
minimize_call = foo "minimize"
compiles with ghc 6.12, 7.0, 7.2 and 7.4. Without the signature, minimize_call must get a monomorphic type, unless the MR is turned off. That can't work anyway because the constraint A m is not defaultable. So therefore the MR must be turned off. But then the type checker still chases its own tail trying to prove the constraint is satisfiable. With only the lifting instance, it can't. If you provide an anchor, it can.
But providing a type signature is much much better.