Optimize Superclass Method Depending on Subclass - haskell

Can I provide a refined implementation (aka. override in OOP) of a method in a class instance, when the type is in another class, too? Or at least, if that other class is a subclass.
I have a class C with method m, a subclass S of C with method s and a type T a so there are instantiations
class C a where m :: [a] -> Bool
class C a => S a where s :: a -> a -> Bool
instance C a => C (T a) where m = ...
instance S a => S (T a) where s = ...
as usual.
Now it happens to be that when T a is in the subclass (which I cannot know as it depends on a), method m could be implemented much more efficient (quadratic vs. exponential time) using s.
I tried 'overriding' m in the implementation
instance S a => S (T a) where
s = ...
m = (all . uncurry) (=^=) . pairs -- override C.m
but the compiler errors basically because, m is not a public method of S. Well, it is not, but it's inherited in the OO sense.
For the specific purpose, the specialized version of m can be used for all instances; it's not a default to be overridden anywhere.
Edit: Because requested, the concrete code with a bit of explanation.
I have a class Model which has (among others) a method con that checks a list for consistency.
class Model a where
con :: [a] -> Bool
Two models can form an arrow model.
data Arrow a b = [a] :->: b
lhs w = [ a | (u :->: _) <- w, a <- u ]
rhs w = [ b | (_ :->: b) <- w ]
For the specific instance Model (Arrow a b), the general con implementation is very expensive (note powerset in the definition).
instance (Model a, Model b) => Model (Arrow a b) where
con w = all (\w' -> con (lhs w') `implies` con (rhs w')) (powerset w)
There is a subclass CoherentModel of Model which has a method (=^=) that checks consistency for two objects. The condition for coherent models is that a list is consistent iff all pairs are.
class Model a => CoherentModel a where
(=^=) :: a -> a -> Bool
a =^= b = con [a, b]
The class CoherentModel is at this point more documentation than a feature.
So, given that a model is coherent, consistency is much more efficient to check.
instance (Model a, CoherentModel b) => CoherentModel (Arrow a b) where
(u :->: a) =^= (v :->: b) = con (u ++ v) `implies` a =^= b
And in this case, con can be implemented using
con = (all . uncurry) (=^=) . pairs
where
pairs :: [a] -> [(a,a)]
pairs [] = []
pairs [_] = []
pairs [x,y] = [(x,y)]
pairs (x:xs) = map ((,) x) xs ++ pairs xs
but I find no way to specify this. It's not only for Arrow, it's relevant for all models with parameter. I chose Arrow because the improvement is significant.

It's a good question. One thing to remember is that whether a data type is an instance of a typeclass is compile-time only information -- i.e. we are always able to choose which instance to use using statically available information at the use site, and polymorphism comes from being able to choose an instance from the context. In general, if you ask "is a a member of typeclass B?", the only answers you can get are "yes" and "compile error". (This second observation is changed a bit by OverlappingInstances, but it doesn't seem to help in your case)
So the answer to your immediate question is no. You can't make a decision about a type's membership in a type class unless you are a method of that type class. What we can do is add this decision as a method (using the constraints package)
import Data.Constraint
class Model a where
con :: [a] -> Bool
isCoherent :: Maybe (Dict (CoherentModel a))
isCoherent = Nothing
Which you can define trivially for any type you have instantiated CoherentModel at:
instance Model Foo where
con = ...
isCoherent = Just Dict
Now you can implement your decision like this (w/ extensions ScopedTypeVariables and TypeApplications):
instance (Model a, Model b) => Model (Arrow a b) where
con | Just Dict <- isCoherent #b = -- efficient implementation
| otherwise = -- inefficient implementation
In the body of the first case we will have a local CoherentModel b in the context. It's kind of cool.
Too bad we have a sort of expression problem here where all the different implementations of con need to be collected up into one place. Also too bad isCoherent needs to be implemented manually on each coherent Model instance, separate from where its CoherentModel instance is.
There is a lot to explore here but I have to go. Good luck!

Related

Generics : run-time ADT for types with instances

Is it possible with Haskell / GHC, to extract an algebraic data type representing all types with Eq and Ord instances ? This would probably need Generics, Typeable, etc.
What I would like is something like :
data Data_Eq_Ord = Data_String String
| Data_Int Int
| Data_Bool Bool
| ...
deriving (Eq, Ord)
For all types known to have instances for Eq and Ord. If it makes the solution easier, we can limit our scope to Ord instances, since Eq is implied by Ord. But is would be interesting to know if constraints intersection is possible.
This data type would be useful because it gives the possibility to use it where Eq and Ord constraints are required, and pattern-match at runtime to refine on types.
I would need this to implement a generic Map Key Value, where Key would be this type, in a Document Indexing library, where the keys and their type is known at run-time. This library is here. For the moment I worked around the issue by defining a data DocIndexKey, and a FieldKey class, but this is not fully satisfactory since it requires boilerplate and can't cover all legit candidates.
Any good alternative approach to this situation is welcome. For some reasons, I prefer to avoid Template Haskell.
Well, it's not an ADT, but this definitely works:
data Satisfying c = forall a. c a => Satisfy a
class (l a, r a) => And l r a where
instance (l a, r a) => And l r a where
ex :: [Satisfying (Typeable `And` Show `And` Ord)]
ex = [ Satisfy (7 :: Int)
, Satisfy "Hello"
, Satisfy (5 :: Int)
, Satisfy [10..20 :: Int]
, Satisfy ['a'..'z']
, Satisfy ((), 'a')]
-- An example of use, with "complicated" logic
data With f c = forall a. c a => With (f a)
-- vvvvvvvvvvvvvvvvvvvvvvvvvv QuantifiedConstraints chokes on this, which is probably a bug...
partitionTypes :: (forall a. c a => TypeRep a) -> [Satisfying c] -> [[] `With` c]
partitionTypes rep = foldr go []
where go (Satisfy x) [] = [With [x]]
go x'#(Satisfy (x :: a)) (xs'#(With (xs :: [b])) : xss) =
case testEquality rep rep :: Maybe (a :~: b) of
Just Refl -> With (x : xs) : xss
Nothing -> xs' : go x' xss
main :: IO ()
main = traverse_ (\(With xs) -> print (sort xs)) $ partitionTypes typeRep ex
Exhaustivity is much harder. Perhaps with a plugin, you could get GHC to do it, but why bother? I don't believe GHC actually tries to keep track of what types it has seen. In particular, you'd have to scan all modules in the project and its dependencies, even those that haven't been loaded by the module containing the type definition. You'd have to implement it from the ground-up. And, as this answer shows, I very much doubt you would actually be able to use such exhaustivity for anything that you can't already do by just taking the open universe as it is.

Does this Haskell program provide an example of "Theorems for free!"?

With respect Listing 1, it is required that the type level axiom
(t a) = (t (getUI(t a)))
should derivable as a theorem for every specific type class instance.
Does the compilation of the test function prove the type level axiom holds for the particular types in the program? Does the compilation provide an example of Theorems for free!?
Listing 1
{-# LANGUAGE MultiParamTypeClasses #-}
data Continuant a = Continuant a deriving (Show,Eq)
class UI a where
instance UI Int where
class Category t a where
getUI :: (UI a) => (t a) -> a
instance Category Continuant Int where
getUI (Continuant a) = a
-- axiom (t a) = (t (getUI(t a))) holds for given types?
test :: Int -> Bool
test x = (Continuant x) == (Continuant (getUI (Continuant x)))
Additional Context
The code is based on a paper where it is stated:
For all implementations of getUI one may require that the axiom (t a)
= (t (getUI (t a))) holds. This must be proven to hold for every specific type class instance declaration. For finite types this can be
done by a program that enumerates all possibilities. For infinite
types this must be done manually via proofs by induction.
Listing 1 was my attempt at a proof. In the light of Solution 1, perhaps I mistakenly thought this required Theorems for free!
No, the fact that it's a class gives you far too much leeway for the type alone to guarantee that axiom. For example, the following alternate instance typechecks but violates your axiom:
instance Category Continuant Int where
getUI _ = 42
(Being completely explicit, our adversary could choose for example t=Continuant and a=6*9 to violate the axiom.) This abuses the fact that the class instantiator gets to choose the contained type. We can remove that ability by removing that argument to the class:
class Category t where
getUI :: UI a => t a -> a
Alas, even this is not enough. We can write
data Pair a = Pair a a
and then the free theorem tells us that for instance Category Pair, we must write one of the following two definitions:
getUI (Pair x y) = x
-- OR
getUI (Pair x y) = y
Whichever we choose, our adversary can choose a t which shows us that our axiom is wrong.
Our choice Adversary's choice
getUI (Pair x y) = x t y = Pair 42 y; a = 6*9
getUI (Pair x y) = y t x = Pair x 42; a = 6*9
Okay, this abuses the fact that the class instantiator gets to choose t. What if we removed that ability...?
class Category where
getUI :: UI a => t a -> a
This restricts the instantiator of Category quite a lot. Too much, in fact: getUI can't be implemented except as an infinite loop or the like.
I suspect it will be very difficult to encode the axiom you wish for as a type which can only be inhabited by things that satisfy it.

Generic solution to (Eq, Show) overlapping instances issue when defining class (* -> *)

Stack has many threads on overlapping instances, and while these are helpful in explaining the source of the problem, I am still not clear as to how to redesign my code for the problem to go away. While I will certain invest more time and effort in going through the details of existing answers, I will post here the general pattern which I have identified as creating the problem, in the hope that a simple and generic answer exists: I typically find myself defining a class such as:
{-# LANGUAGE FlexibleInstances #-}
class M m where
foo :: m v -> Int
bar :: m v -> String
together with the instance declarations:
instance (M m) => Eq (m v) where
(==) x y = (foo x) == (foo y) -- details unimportant
instance (M m) => Show (m v) where
show = bar -- details unimportant
and in the course of my work I will inevitably create some data type:
data A v = A v
and declare A as an instance of class M:
instance M A where
foo x = 1 -- details unimportant
bar x = "bar"
Then defining some elements of A Integer:
x = A 2
y = A 3
I have no issue printing x and y or evaluating the Boolean x == y, but if I attempt to print the list [x] or evaluate the Boolean [x] == [y], then the overlapping instance error occurs:
main = do
print x -- fine
print y -- fine
print (x == y) -- fine
print [x] -- overlapping instance error
if [x] == [y] then return () else return () -- overlapping instance error
The cause of these errors is now very clear I think: they stem from the existing instance declarations instance Show a => Show [a] and instance Eq a => Eq [a] and while it is true that [] :: * -> * has not yet been declared as an instance of my class M, there is nothing preventing someone doing so at some point: so the compiler ignores the context of instance declarations.
When faced with the pattern I have described, how can it be re-engineered to avoid the problem?
There's no backtracking in instance search. Instances are matched purely based on the syntactic structure of the instance head. That means instance contexts are not accounted for during instance resolution.
So, when you write
instance (M m) => Show (m v) where
show = bar
you're saying "Here is an instance for Show, for any type of the form m v". Since [x] :: [] (A Int) is indeed a type of the form m v (set m ~ [] and v ~ A Int), instance search for Show [A Int] turns up two candidates:
instance Show a => Show [a]
instance M m => Show (m v)
Like I said, the type checker doesn't look at the instances' contexts when selecting an instance, so these two instances are overlapping.
The fix is to not declare instances like Show (m v). As a general rule, it's a bad idea to declare instances whose head is composed purely of type variables. Every instance you write should start with an honest-to-goodness type constructor, and you should approach instances which don't fit that pattern with suspicion.
Supplying a newtype for your default instances is a fairly standard design (see, for example, WrappedBifunctor's Functor instance),
newtype WrappedM m a = WrappedM { unwrapM :: m a }
instance M m => Show (WrappedM m a) where
show = bar . unwrapM
as is giving a default implementation of the function at the top level (see eg foldMapDefault):
showDefault = bar

Haskell: functor from this data type?

Sorry about bad title. I have a problematic data type, which I'm trying to define as instance of functor.
So basically, what I have is something, which has
sample_logp :: s a -> a
and it should be transformed using
(a -> b)
to
sample_logp :: s b -> b
. The following code does not quite accomplish this, and succeeds only in
sample_logp :: s a -> b
.
data Model s a = Model {
sample_logp :: s a -> a
}
instance Functor (Model s) where
fmap f m = Model {
sample_logp = sample_logp'
} where sample_logp' x = (f . (sample_logp m)) x
Is what I'm trying even possible? If so, how could this code be updated to achieve this?
The standard approach here is to add more type variables.
data Model s a b = Model {
sample_logp :: s a -> b
}
Once you have split the type variables, you have access to more tools. The Profunctor class is appropriate here. (Not typechecked since I don't have ghc on this system - comment or just fix it if my implementation is off.)
instance (Functor s) => Profunctor (Model s) where
dimap f g (Model h) = Model $ g . h . fmap f
lmap f (Model h) = Model $ h . fmap f
rmap g (Model h) = Model $ g . h
Now, given that you have a Model s a a, which is the equivalent of your Model s a, you can convert it to a Model s b b by using dimap bToA aToB.
As the comments say, your original data type is invariant because it uses the same type variable in positive and negative positions. This means you need to supply conversion functions in each direction. Adding an extra type variable lets you take advantage of existing tools for doing that, like Profunctor.
Note that all of the above is based on the assumption that you are using covariant types for s. If s is contravariant, then you can write a direct Functor instance for your original type, as chi's comment says. That's a much less common situation, though.

The "reader" monad

OK, so the writer monad allows you to write stuff to [usually] some kind of container, and get that container back at the end. In most implementations, the "container" can actually be any monoid.
Now, there is also a "reader" monad. This, you might think, would offer the dual operation - incrementally reading from some kind of container, one item at a time. In fact, this is not the functionality that the usual reader monad provides. (Instead, it merely offers easy access to a semi-global constant.)
To actually write a monad which is dual to the usual writer monad, we would need some kind of structure which is dual to a monoid.
Does anybody have any idea what this dual structure might be?
Has anybody written this monad? Is there a well-known name for it?
The dual of a monoid is a comonoid. Recall that a monoid is defined as (something isomorphic to)
class Monoid m where
create :: () -> m
combine :: (m,m) -> m
with these laws
combine (create (),x) = x
combine (x,create ()) = x
combine (combine (x,y),z) = combine (x,combine (y,z))
thus
class Comonoid m where
delete :: m -> ()
split :: m -> (m,m)
some standard operations are needed
first :: (a -> b) -> (a,c) -> (b,c)
second :: (c -> d) -> (a,c) -> (a,d)
idL :: ((),x) -> x
idR :: (x,()) -> x
assoc :: ((x,y),z) -> (x,(y,z))
with laws like
idL $ first delete $ (split x) = x
idR $ second delete $ (split x) = x
assoc $ first split (split x) = second split (split x)
This typeclass looks weird for a reason. It has an instance
instance Comonoid m where
split x = (x,x)
delete x = ()
in Haskell, this is the only instance. We can recast reader as the exact dual of writer, but since there is only one instance for comonoid, we get something isomorphic to the standard reader type.
Having all types be comonoids is what makes the category "Cartesian" in "Cartesian Closed Category." "Monoidal Closed Categories" are like CCCs but without this property, and are related to substructural type systems. Part of the appeal of linear logic is the increased symmetry that this is an example of. While, having substructural types allows you to define comonoids with more interesting properties (supporting things like resource management). In fact, this provides a framework for understand the role of copy constructors and destructors in C++ (although C++ does not enforce the important properties because of the existence of pointers).
EDIT: Reader from comonoids
newtype Reader r x = Reader {runReader :: r -> x}
forget :: Comonoid m => (m,a) -> a
forget = idL . first delete
instance Comonoid r => Monad (Reader r) where
return x = Reader $ \r -> forget (r,x)
m >>= f = \r -> let (r1,r2) = split r in runReader (f (runReader m r1)) r2
ask :: Comonoid r => Reader r r
ask = Reader id
note that in the above code every variable is used exactly once after binding (so these would all type with linear types). The monad law proofs are trivial, and only require the comonoid laws to work. Hence, Reader really is dual to Writer.
I'm not entirely sure of what the dual of a monoid should be, but thinking of dual (probably incorrectly) as the opposite of something (simply on the basis that a Comonad is the dual of a Monad, and has all the same operations but the opposite way round). Rather than basing it on mappend and mempty I would base it on:
fold :: (Foldable f, Monoid m) => f m -> m
If we specialise f to a list here, we get:
fold :: Monoid m => [m] -> m
This seems to me to contain all of the monoid class, in particular.
mempty == fold []
mappend x y == fold [x, y]
So, then I guess the dual of this different monoid class would be:
unfold :: (Comonoid m) => m -> [m]
This is a lot like the monoid factorial class that I have seen on hackage here.
So on this basis, I think the 'reader' monad you describe would be a supply monad. The supply monad is effectively a state transformer of a list of values, so that at any point we can choose to be supplied with an item from the list. In this case, the list would be the result of unfold.supply monad
I should stress, I am no Haskell expert, nor an expert theoretician. But this is what your description made me think of.
Supply is based on State, which makes it suboptimal for some applications. For example, we might want to make an infinite tree of supplied values (e.g. randoms):
tree :: (Something r) => Supply r (Tree r)
tree = Branch <$> supply <*> sequenceA [tree, tree]
But since Supply is based on State, all the labels will be bottom except for the ones one the leftmost path down the tree.
You need something splittable (like in #PhillipJF's Comonoid). But there is a problem if you try to make this into a Monad:
newtype Supply r a = Supply { runSupply :: r -> a }
instance (Splittable r) => Monad (Supply r) where
return = Supply . const
Supply m >>= f = Supply $ \r ->
let (r',r'') = split r in
runSupply (f (m r')) r''
Because the monad laws require f >>= return = f, so that means that r'' = r in the definition of (>>=).. But, the monad laws also require that return x >>= f = f x, so r' = r as well. Thus, for Supply to be a monad, split x = (x,x), and thus you've got the regular old Reader back again.
A lot of monads that are used in Haskell aren't real monads -- i.e. they only satisfy the laws up to some equivalence relation. E.g. many nondeterminism monads will give results in a different order if you transform according to the laws. But that's okay, that's still monad enough if you're just wondering whether a particular element appears in the list of outputs, rather than where.
If you allow Supply to be a monad up to some equivalence relation, then you can get nontrivial splits. E.g. value-supply will construct splittable entities which will dole out unique labels from a list in an unspecified order (using unsafe* magic) -- so a supply monad of value supply would be a monad up to permutation of labels. This is all that is needed for many applications. And, in fact, there is a function
runSupply :: (forall r. Eq r => Supply r a) -> a
which abstracts over this equivalence relation to give a well-defined pure interface, because the only thing it allows you to do to labels is to see if they are equal, and that doesn't change if you permute them. If this runSupply is the only observation you allow on Supply, then Supply on a supply of unique labels is a real monad.

Resources