I may be naive in my thinking here but I think if the right hand value of a Reader is an instance of Monoid then a Monoid could be defined for Reader... Here is my implementation:
instance Monoid a => Monoid (Reader r a) where
mempty = pure mempty
mappend ra rb = (<>) <$> ra <*> rb
This however results in the following error:
• Illegal instance declaration for ‘Monoid (Reader r a)’
(All instance types must be of the form (T t1 ... tn)
where T is not a synonym.
Use TypeSynonymInstances if you want to disable this.)
• In the instance declaration for ‘Monoid (Reader r a)’
|
413 | instance Monoid a => Monoid (Reader r a) where
| ^^^^^^^^^^^^^^^^^^^
I am unsure what this error actually means, and why I am unable to implement Monoid for Reader though I presume it is something to do with Reader being a higher kinded type?
There are two problems. The first is this:
type Reader r = ReaderT r Identity
For historical reasons, type synonyms are not allowed in instance declarations. This is the
where T is not a synonym.
part of the error. Luckily we can just expand the synonym; this would give us
instance Monoid a => Monoid (ReaderT r Identity a)
but now we would fall afowl of the other part of the error, namely:
All instance types must be of the form (T t1 ... tn)
Specifically, Identity is not a type variable, so it doesn't fit this form. Again this restriction is in place primarily for historical reasons. You can remove both restrictions by enabling two language extensions:
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
However, in this case it's not needed. The preferable way is to actually use the prescribed form of instance declaration, so:
instance (Applicative f, Monoid m) => Monoid (ReaderT r f m) where
mempty = pure mempty
mappend = liftA2 mappend
This requires no extensions, and works not just for Reader but for ReaderT transforming any Applicative instance.
However it does make an orphan instance; hence you should consider writing another newtype wrapper.
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-- or, instead of GeneralizedNewtypeDeriving, write the obvious instances by hand
newtype App f m = App { getApp :: f m } deriving (Functor, Applicative)
instance (Applicative f, Monoid m) => Monoid (App f m) where
mempty = pure mempty
mappend = liftA2 mappend
Then you can use App (Reader r) a as a monoid whenever a is a monoid. I seem to recall this existed somewhere in the standard libraries already, but I can't find it any more...
The issue here is that your Reader is a type alias instead of a newtype.
Using the former is disallowed by Haskell2010 (which is very conservative in what it is allowed), but GHC allows using type aliases in instances if you turn on the extension reported in the error you posted. Note that in such case it would define an instance for the expansion of the alias, e.g.
instance Monoid a => Monoid (r -> a) where ...
For a Reader type, I'd prefer to use a newtype, even if one needs to wrap/unwrap it when it is used.
Related
In Monoid and Semigroup instances of Alternative Alt used.
Why we can't write instance without it?
{-# LANGUAGE FlexibleInstances #-}
instance Alternative f => Semigroup (f a) where
(<>) = <|>
instance Alternative f => Monoid (f a) where
mempty = empty
And if we can write that, can we then replace Alternative with (Monoid (f a), Applicative f) in functions?
You use it for deriving Monoid for any Alternative
{-# Language DerivingVia #-}
data F a = ..
deriving (Semigroup, Monoid)
via Alt F a
instance Functor F where ..
instance Applicative F where ..
instance Alternative F where ..
Alt is a newtype for good reason as there are many ways to describe Monoid behaviour for an applied type f a. For example Applicative lifting: Ap.
{-# Language DerivingVia #-}
data G a = ..
deriving (Semigroup, Monoid, Num, Bounded)
via Ap G a
instance Functor G where ..
instance Applicative G where ..
The instances you give are maximally overlapping, the Monoid instance of any applied type is now forced to be the Alternative instance, completely ignoring the a parameter.
There are many instances where this would not be correct, for example Semigroup a => Semigroup (Maybe a) is not the same as the Semigroup you would get from Alternative Maybe.
It is possible using a rather new feature QuantifiedConstraints to quantify over the argument of a type constructor forall x. Monoid (f x). This is not the same as Alternative but similar
{-# Language QuantifiedConstraints #-}
..
type Alternative' :: (Type -> Type) -> Constraint
class (forall x. Monoid (f x)) => Alternative' f
instance (forall x. Monoid (f x)) => Alternative' f
You use the (<|>) :: Alternative f => f a -> f a -> f a and empty :: Alternative f => f a. As the signatures suggest, these are defined for items of type Applicative f => f a. You thus can only use these functions to process such items, and this thus requires that if you work with an f a, for eample to define mempty :: f a with mempty = empty, then that requires that f is a member of the Alternative typeclass.
That being said, while a lot data types can have a Semigroup and Monoid instance defined as how the Alternative is implemented, this is not per se the best instance. Yes Alternative is a monoid on applicative functors, but that should not per se be the monoid instance on these data types.
If the instances you propose existed in that form, every type that matches f a would immediately defer to it. That includes types where it makes sense, but consider
newtype ResultsSum a = ResultsSum {funToSum :: a -> Int}
instance Semigroup (ResultsSum a) where
ResultsSum p <> ResultsSum q = ResultsSum $ \x -> p x + q x
Unfortunately though, ResultsSum a matches f a. But it is not an Alternative; it isn't even a functor, and can't be (rather, it is Contravariant). However, the compiler doesn't take that into account when resolving instances: it just sees two instance declarations whose heads both purport to enable ResultsSum being a semigroup, which triggers an ambiguous-instance error.
Granted, this example could be addressed with {-# OVERLAPPING #-} pragmas, but it's always best to avoid instance overlaps as they can lead to strange. It's unnecessary, since you can also also derive those instances via the Alternative one. Though I personally would actually rather do it the other way around: define the Monoid instance first and then Alternative in terms of it.
Are there combinators to write Haskell types in pointfree style?
I have a type synonym that is something like:
type FooT m a = StateT State (ReaderT (Params m) m)
I'd like to be able to write the right-hand-side of this in pointfree style in order to instantiate a typeclass that demands a parameter be a monad transformer. ie, there is some typeclass like:
class (MonadTrans t, Bar (t Monad)) => Baz t where -- where Bar is some other typeclass
...
And I would like to instantiate it with my stack of transformers. However, I need to provide it with something of kind (* -> *) -> * -> *, which means I would need to write a type-level function in order to pass my StateT State (ReaderT ...) transformer as a parameter to the typeclass.
I tried using type families but it seems like they need to be fully applied.
You must use newtype. Type synonyms have to be fully applied, so they do not actually give you a way to rearrange type expressions to fit certain kinds.
The simplest is to turn FooT into a newtype:
newtype FooT m a = FooT (StateT State (ReaderT (Params m) m) a)
It's possible to develop a combinator style, but every combinator must be a newtype so that's kind of a pain to unwrap.
{-# LANGUAGE PolyKinds, TypeOperators #-}
newtype (:.:) f g a = (:.:) (f (g a))
newtype Join f a = Join (f a a)
type FooT = StateT State :.: Join (ReaderT :.: Params)
-- Good luck untangling that.
I have a type defined like this:
newtype PrimeSet a = P Integer
deriving Eq
I have also defined a function which converts a prime set to a list, given that its type parameter is an Integral.
toList :: Integral a => PrimeSet a -> [a]
I now what to give PrimeSet a Foldable instance, so this was my first attempt (after importing fold from Data.Foldable):
instance Foldable PrimeSet where
foldMap f = fold . map f . toList
This didn't work, however, and the compiler told me that it Could not deduce (Integral a) arising from a use of ‘toList’. My understanding of this message is that toList requires its argument to be an Integral a => PrimeSet a type, but this isn't necessarily the case in the Foldable instance.
The message also said that a possible fix would be to add Integral a to the context of the type signature for my foldMap implementation, but of course I was then told that I'm not allowed to provide my own type definitons for class methods unless I use InstanceSigs, so I tried that but that didn't seem to work either.
So my question is this: is it possible to add a type constraint to a class instance if the type parameter of the type I'm writing the class instance for is hidden - or, to reiterate, can I do something like this?
instance (Integral a) => Foldable (PrimeSet a) where
(This of course doesn't work because PrimeSet a has the kind * whereas Foldable requires * -> *)
No, this is not possible. The whole point of higher-kinded types is to work over any parameter type. Whereas PrimeSet isn't really parametric at all – basically, it's always PrimeSet Integer. Why do you have that a parameter at all?
There is however a different class for types that are “kinda containers”, but not for arbitrary types: MonoTraversable, or actually MonoFoldable in this case.
{-# LANGUAGE FlexibleInstances, TypeFamilies #-}
import Data.MonoTraversable
type instance Element (PrimeSet a) = a
-- or, if `PrimeSet` is not parameterised,
-- type instance Element PrimeSet = Integer
instance (Integral a) => MonoFoldable (PrimeSet a) where
otoList = YourImplementation.toList
An alternative would be that you do use parameterised types, functors in fact, but not in the normal Hask category of all Haskell types but only in the subcategory whose types areis Integer. I have such a class in my constrained-categories package. But, especially for this type you have, this really doesn't seem to make any sense.
You can use a GADT to constrain the parameter type:
{-# LANGUAGE GADTs #-}
data PrimeSet a where
PrimeSet :: Integral a => Integer -> PrimeSet a
instance Foldable PrimeSet where
foldr f b (PrimeSet x) = f (fromInteger x) b
You could generalise a little with ConstraintKinds (and the monofoldable class):
data Monomorphic f c a where
Monomorphic :: c a => f -> Monomorphic f c a
instance (item ~ Item f, MonoFoldable f) => Foldable (Monomorphic f ((~) item)) where
foldr f b (Monomorphic xs) = ofoldr f b xs
data PrimeSet = PrimeSet Integer
instance Foldable (Monomorphic PrimeSet Integral) where
foldr f b (Monomorphic (PrimeSet i)) = f (fromInteger i) b
I have the following newtype:
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
newtype Wrap m a = Wrap {runWrap :: m a}
deriving (Functor, Applicative, Monad, MonadTrans)
I'm trying to derive MonadTrans automatically, but I get the following error:
• Can't make a derived instance of ‘MonadTrans Wrap’
(even with cunning GeneralizedNewtypeDeriving):
cannot eta-reduce the representation type enough
• In the newtype declaration for ‘Wrap’
However, writing the trivial instance for MonadTrans works just fine:
instance MonadTrans Wrap where
lift = Wrap
What is the reason for such an error message?
GeneralizedNewtypeDeriving uses the underlying instance for a class for the implementation of the class for the newtype. However, in this case that doesn't make any sense, because m isn't of the right kind to even be an instance of MonadTrans (recall that m :: * -> *, but MonadTrans wants (* -> *) -> * -> *).
I'm building a couple of DSLs which should be composable based on 'free monads' and 'datatypes a la carte', using the free and compdata packages (similar in spirit to Combining Free types).
Whilst this works for some simple DSLs, I'm stuck on one which has a type parameter, in the case of a constructor/command which doesn't depend on this type parameter, causing an ambiguous type parameter error from GHC.
To clarify, here's some code:
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeOperators #-}
module DSL where
import Data.Comp
import Control.Monad.Free
type Index = Int
data DSL a next = Read Index (a -> next)
| Write a (Index -> next)
| GetLastIndex (Index -> next)
deriving (Functor)
read :: (Functor f, DSL a :<: f, MonadFree f m) => Index -> m a
read idx = liftF (inj (Read idx id))
write :: (Functor f, DSL a :<: f, MonadFree f m) => a -> m Index
write a = liftF (inj (Write a id))
-- This works
getLastIndex' :: MonadFree (DSL a) m => m Index
getLastIndex' = liftF (GetLastIndex id)
-- This doesn't:
--
-- Could not deduce (Data.Comp.Ops.Subsume
-- (compdata-0.10:Data.Comp.SubsumeCommon.ComprEmb
-- (Data.Comp.Ops.Elem (DSL a0) f))
-- (DSL a0)
-- f)
-- from the context (Functor f, DSL a :<: f, MonadFree f m)
-- bound by the type signature for
-- getLastIndex :: (Functor f, DSL a :<: f, MonadFree f m) => m Index
-- at simple.hs:30:17-66
-- The type variable ‘a0’ is ambiguous
-- In the ambiguity check for the type signature for ‘getLastIndex’:
-- getLastIndex :: forall (m :: * -> *) (f :: * -> *) a.
-- (Functor f, DSL a :<: f, MonadFree f m) =>
-- m Index
-- To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
-- In the type signature for ‘getLastIndex’:
-- getLastIndex :: (Functor f, DSL a :<: f, MonadFree f m) => m Index
getLastIndex :: (Functor f, DSL a :<: f, MonadFree f m) => m Index
-- getLastIndex = liftF (inj (GetLastIndex id))
getLastIndex = _
Trying to get this to work with enabling the AllowAmbiguousTypes extension as hinted by GHC didn't get me any further. I tried adding some forall a-style things in the type signature, to no avail.
Is there any way to get this pattern to work?
This is a relatively well-known limitation of open sums "à la Carte".
In short, if we have an f functor that itself has one or more inner type indices, then type inference suffers significantly for open sums containing that functor.
Illustrating the reasons, suppose we have an open sum that contains a DSL () and a DSL Int. GHC must pick an instance for one of them, but it's impossible with getLastIndex, because the a parameter isn't mentioned in the arguments or the return type. GHC has essentially no information about a from the context.
This can be remedied in a somewhat clumsy way using Data.Proxy:
import Data.Proxy
getLastIndex ::
forall a f m.
(Functor f, DSL a :<: f, MonadFree f m)
=> Proxy a -> m Index
getLastIndex _ = liftF (inj (GetLastIndex id :: DSL a Index))
Alternatively, we can recover nice type inference and unambiguity if we require that there is only a single DSL in the open sum. However, this
involves rewriting the type-level lookup code for :<: for functors like DSL (those that have an inner type index). We can't really do this with compdata as it is, because it doesn't export the relevant type-level machinery.
I wrote a minimal example for what the implementation of the above looks like for your case. I don't paste it here because it's a bit long and unedifying. Note that the choice of the inner index becomes completely determined by the functor's constructor and the open sum. This also repairs type inference for other cases; for example with the old code we have to type-annotate every use of Write x f if x is a number literal or any polymorphic value, while in the new code its gets inferred.
Also, note that the example implementation is only for functors with a single inner index! If we'd like to have a DSL a b next, then we would have to write new code for that case too, or use DSL '(a, b) next instead.