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

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.

Related

How could I prove this type level Haskell theorem?

With respect to Listing 1, how would I go about proving the type level axiom
(t a) = (t (getUI (t a)))
holds?
Listing 1
data Continuant a = Continuant a deriving (Show,Eq)
class UI a where -- ...
instance UI Int where -- ...
class Category t where
getUI :: (UI a) => (t a) -> a
instance Category Continuant where
getUI (Continuant a) = a
-- Does axiom (t a) = (t (getUI(t a))) holds for given types?
test :: Int -> Bool
test x = (Continuant x) == (Continuant (getUI (Continuant x)))
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.
My current intuition is that the test function in some way satisfies the axiom, but I do not think that it amounts to a proof.
This question follows on from a previous question.
To prove this, just start with one side of the equation and rewrite until you get to the other side. I like to start with the more complicated side.
when x :: Int,
Continuant (getUI (Continuant x))
-- ^^^^^^^^^^^^^^^^^^^^
-- by definition of getUI in Category Continuant Int
= Continuant x
That was easy! This does count as a proof (mind, not a formally verified one -- Haskell is not powerful enough to express term-level proofs. But it's so trivial it wouldn't be worth the boilerplate in agda.).
I was a bit bewildered by the phrasing of this axiom, since it seems to be mixing up types and terms quite a lot. Skimming the paper, it seems like this is only intended to work for simple single-constructor newtypes, thus this mixing is justified (still odd). Anyway, it seems like the paper doesn't have the Category class parameterized on a: i.e. instead of
class Category t a where ...
it would be
class Category t where ...
which makes more sense to me, that the class describes polymorphic wrappers, rather than a possibly different description of how it wraps each individual type (especially since it appears that the axiom requires the implementation to be the same no matter what a you pick!).

Optimize Superclass Method Depending on Subclass

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!

Any advantage of using type constructors in type classes?

Take for example the class Functor:
class Functor a
instance Functor Maybe
Here Maybe is a type constructor.
But we can do this in two other ways:
Firstly, using multi-parameter type classes:
class MultiFunctor a e
instance MultiFunctor (Maybe a) a
Secondly using type families:
class MonoFunctor a
instance MonoFunctor (Maybe a)
type family Element
type instance Element (Maybe a) a
Now there's one obvious advantage of the two latter methods, namely that it allows us to do things like this:
instance Text Char
Or:
instance Text
type instance Element Text Char
So we can work with monomorphic containers.
The second advantage is that we can make instances of types that don't have the type parameter as the final parameter. Lets say we make an Either style type but put the types the wrong way around:
data Silly t errorT = Silly t errorT
instance Functor Silly -- oh no we can't do this without a newtype wrapper
Whereas
instance MultiFunctor (Silly t errorT) t
works fine and
instance MonoFunctor (Silly t errorT)
type instance Element (Silly t errorT) t
is also good.
Given these flexibility advantages of only using complete types (not type signatures) in type class definitions, is there any reason to use the original style definition, assuming you're using GHC and don't mind using the extensions? That is, is there anything special you can do putting a type constructor, not just a full type in a type class that you can't do with multi-parameter type classes or type families?
Your proposals ignore some rather important details about the existing Functor definition because you didn't work through the details of writing out what would happen with the class's member function.
class MultiFunctor a e where
mfmap :: (e -> ??) -> a -> ????
instance MultiFunctor (Maybe a) a where
mfmap = ???????
An important property of fmap at the moment is that its first argument can change types. fmap show :: (Functor f, Show a) => f a -> f String. You can't just throw that away, or you lose most of the value of fmap. So really, MultiFunctor would need to look more like...
class MultiFunctor s t a b | s -> a, t -> b, s b -> t, t a -> s where
mfmap :: (a -> b) -> s -> t
instance (a ~ c, b ~ d) => MultiFunctor (Maybe a) (Maybe b) c d where
mfmap _ Nothing = Nothing
mfmap f (Just a) = Just (f a)
Note just how incredibly complicated this has become to try to make inference at least close to possible. All the functional dependencies are in place to allow instance selection without annotating types all over the place. (I may have missed a couple possible functional dependencies in there!) The instance itself grew some crazy type equality constraints to allow instance selection to be more reliable. And the worst part is - this still has worse properties for reasoning than fmap does.
Supposing my previous instance didn't exist, I could write an instance like this:
instance MultiFunctor (Maybe Int) (Maybe Int) Int Int where
mfmap _ Nothing = Nothing
mfmap f (Just a) = Just (if f a == a then a else f a * 2)
This is broken, of course - but it's broken in a new way that wasn't even possible before. A really important part of the definition of Functor is that the types a and b in fmap don't appear anywhere in the instance definition. Just looking at the class is enough to tell the programmer that the behavior of fmap cannot depend on the types a and b. You get that guarantee for free. You don't need to trust that instances were written correctly.
Because fmap gives you that guarantee for free, you don't even need to check both Functor laws when defining an instance. It's sufficient to check the law fmap id x == x. The second law comes along for free when the first law is proven. But with that broken mfmap I just provided, mfmap id x == x is true, even though the second law is not.
As the implementer of mfmap, you have more work to do to prove your implementation is correct. As a user of it, you have to put more trust in the implementation's correctness, since the type system can't guarantee as much.
If you work out more complete examples for the other systems, you find that they have just as many issues if you want to support the full functionality of fmap. And this is why they aren't really used. They add a lot of complexity for only a small gain in utility.
Well, for one thing the traditional functor class is just much simpler. That alone is a valid reason to prefer it, even though this is Haskell and not Python. And it also represents the mathematical idea better of what a functor is supposed to be: a mapping from objects to objects (f :: *->*), with extra property (->Constraint) that each (forall (a::*) (b::*)) morphism (a->b) is lifted to a morphism on the corresponding object mapped to (-> f a->f b). None of that can be seen very clearly in the * -> * -> Constraint version of the class, or its TypeFamilies equivalent.
On a more practical account, yes, there are also things you can only do with the (*->*)->Constraint version.
In particular, what this constraint guarantees you right away is that all Haskell types are valid objects you can put into the functor, whereas for MultiFunctor you need to check every possible contained type, one by one. Sometimes that's just not possible (or is it?), like when you're mapping over infinitely many types:
data Tough f a = Doable (f a)
| Tough (f (Tough f (a, a)))
instance (Applicative f) = Semigroup (Tough f a) where
Doable x <> Doable y = Tough . Doable $ (,)<$>x<*>y
Tough xs <> Tough ys = Tough $ xs <> ys
-- The following actually violates the semigroup associativity law. Hardly matters here I suppose...
xs <> Doable y = xs <> Tough (Doable $ fmap twice y)
Doable x <> ys = Tough (Doable $ fmap twice x) <> ys
twice x = (x,x)
Note that this uses the Applicative instance of f not just on the a type, but also on arbitrary tuples thereof. I can't see how you could express that with a MultiParamTypeClasses- or TypeFamilies-based applicative class. (It might be possible if you make Tough a suitable GADT, but without that... probably not.)
BTW, this example is perhaps not as useless as it may look – it basically expresses read-only vectors of length 2n in a monadic state.
The expanded variant is indeed more flexible. It was used e.g. by Oleg Kiselyov to define restricted monads. Roughly, you can have
class MN2 m a where
ret2 :: a -> m a
class (MN2 m a, MN2 m b) => MN3 m a b where
bind2 :: m a -> (a -> m b) -> m b
allowing monad instances to be parametrized over a and b. This is useful because you can restrict those types to members of some other class:
import Data.Set as Set
instance MN2 Set.Set a where
-- does not require Ord
return = Set.singleton
instance Prelude.Ord b => MN3 SMPlus a b where
-- Set.union requires Ord
m >>= f = Set.fold (Set.union . f) Set.empty m
Note than because of that Ord constraint, we are unable to define Monad Set.Set using unrestricted monads. Indeed, the monad class requires the monad to be usable at all types.
Also see: parameterized (indexed) monad.

Why context is not considered when selecting typeclass instance in Haskell?

I understand that when having
instance (Foo a) => Bar a
instance (Xyy a) => Bar a
GHC doesn't consider the contexts, and the instances are reported as duplicate.
What is counterintuitive, that (I guess) after selecting an instance, it still needs to check if the context matches, and if not, discard the instance. So why not reverse the order, and discard instances with non-matching contexts, and proceed with the remaining set.
Would this be intractable in some way? I see how it could cause more constraint resolution work upfront, but just as there is UndecidableInstances / IncoherentInstances, couldn't there be a ConsiderInstanceContexts when "I know what I am doing"?
This breaks the open-world assumption. Assume:
class B1 a
class B2 a
class T a
If we allow constraints to disambiguate instances, we may write
instance B1 a => T a
instance B2 a => T a
And may write
instance B1 Int
Now, if I have
f :: T a => a
Then f :: Int works. But, the open world assumption says that, once something works, adding more instances cannot break it. Our new system doesn't obey:
instance B2 Int
will make f :: Int ambiguous. Which implementation of T should be used?
Another way to state this is that you've broken coherence. For typeclasses to be coherent means that there is only one way to satisfy a given constraint. In normal Haskell, a constraint c has only one implementation. Even with overlapping instances, coherence generally holds true. The idea is that instance T a and instance {-# OVERLAPPING #-} T Int do not break coherence, because GHC can't be tricked into using the former instance in a place where the latter would do. (You can trick it with orphans, but you shouldn't.) Coherence, at least to me, seems somewhat desirable. Typeclass usage is "hidden", in some sense, and it makes sense to enforce that it be unambiguous. You can also break coherence with IncoherentInstances and/or unsafeCoerce, but, y'know.
In a category theoretic way, the category Constraint is thin: there is at most one instance/arrow from one Constraint to another. We first construct two arrows a : () => B1 Int and b : () => B2 Int, and then we break thinness by adding new arrows x_Int : B1 Int => T Int, y_Int : B2 Int => T Int such that x_Int . a and y_Int . b are both arrows () => T Int that are not identical. Diamond problem, anyone?
This does not answer you question as to why this is the case. Note, however, that you can always define a newtype wrapper to disambiguate between the two instances:
newtype FooWrapper a = FooWrapper a
newtype XyyWrapper a = XyyWrapper a
instance (Foo a) => Bar (FooWrapper a)
instance (Xyy a) => Bar (XyyWrapper a)
This has the added advantage that by passing around either a FooWrapper or a XyyWrapper you explicitly control which of the two instances you'd like to use if your a happens to satisfy both.
Classes are a bit weird. The original idea (which still pretty much works) is a sort of syntactic sugar around what would otherwise be data statements. For example you can imagine:
data Num a = Num {plus :: a -> a -> a, ... , fromInt :: Integer -> a}
numInteger :: Num Integer
numInteger = Num (+) ... id
then you can write functions which have e.g. type:
test :: Num x -> x -> x -> x -> x
test lib a b c = a + b * (abs (c + b))
where (+) = plus lib
(*) = times lib
abs = absoluteValue lib
So the idea is "we're going to automatically derive all of this library code." The question is, how do we find the library that we want? It's easy if we have a library of type Num Int, but how do we extend it to "constrained instances" based on functions of type:
fooLib :: Foo x -> Bar x
xyyLib :: Xyy x -> Bar x
The present solution in Haskell is to do a type-pattern-match on the output-types of those functions and propagate the inputs to the resulting declaration. But when there's two outputs of the same type, we would need a combinator which merges these into:
eitherLib :: Either (Foo x) (Xyy x) -> Bar x
and basically the problem is that there is no good constraint-combinator of this kind right now. That's your objection.
Well, that's true, but there are ways to achieve something morally similar in practice. Suppose we define some functions with types:
data F
data X
foobar'lib :: Foo x -> Bar' x F
xyybar'lib :: Xyy x -> Bar' x X
bar'barlib :: Bar' x y -> Bar x
Clearly the y is a sort of "phantom type" threaded through all of this, but it remains powerful because given that we want a Bar x we will propagate the need for a Bar' x y and given the need for the Bar' x y we will generate either a Bar' x X or a Bar' x y. So with phantom types and multi-parameter type classes, we get the result we want.
More info: https://www.haskell.org/haskellwiki/GHC/AdvancedOverlap
Adding backtracking would make instance resolution require exponential time, in the worst case.
Essentially, instances become logical statements of the form
P(x) => R(f(x)) /\ Q(x) => R(f(x))
which is equivalent to
(P(x) \/ Q(x)) => R(f(x))
Computationally, the cost of this check is (in the worst case)
c_R(n) = c_P(n-1) + c_Q(n-1)
assuming P and Q have similar costs
c_R(n) = 2 * c_PQ(n-1)
which leads to exponential growth.
To avoid this issue, it is important to have fast ways to choose a branch, i.e. to have clauses of the form
((fastP(x) /\ P(x)) \/ (fastQ(x) /\ Q(x))) => R(f(x))
where fastP and fastQ are computable in constant time, and are incompatible so that at most one branch needs to be visited.
Haskell decided that this "fast check" is head compatibility (hence disregarding contexts). It could use other fast checks, of course -- it's a design decision.

Using Haskell's type system to specify that a class obeys extra properties (i.e. type classes for type classes)

When we create a type class, we usually assume that its functions must obey some properties. Thus we have the Monoid and Monad laws for their respective type classes. But, what if there is some law, like associativity, that I want to specify that multiple classes either may or may not obey that law? Is there a way to do that in Haskell's type system? Is this sort of type classes for type classes idea even feasible in practice?
Here's a motivating example from algebra:
class Addition x where
add :: x -> x -> x
class Multiplication x where
mult :: x -> x -> x
instance Addition Int where
add = (+)
instance Multiplication Int where
add = (*)
Now, if I want to specify that addition over Int's is associative and commutative, I can create the classes and instances:
class (Addition x) => AssociativeAddition x where
class (Addition x) => CommutativeAddition x where
instance AssociativeAddition Int where
instance CommutativeAddition Int where
But this is cumbersome because I have to create all possible combinations for all classes. I can't just create Associative and Commutative classes, because what if addition is commutative, but multiplication is not (like in matrices)?
What I would like to be able to do is say something like:
class Associative x where
instance (Associative Addition, Commutative Addition) => Addition Int where
add = (+)
instance (Commutative Multiplication) => Multiplication Int where
mult = (*)
Can this be done?
(Haskell's abstract algebra packages, like algebra and constructive-algebra, do not currently do this, so I'm guessing not. But why not?)
You actually can do this with some recent GHC extensions:
{-# LANGUAGE ConstraintKinds, KindSignatures, MultiParamTypeClasses #-}
import GHC.Exts (Constraint)
class Addition (a :: *) where
plus :: a -> a -> a
instance Addition Integer where
plus = (+)
class (c a) => Commutative (a :: *) (c :: * -> Constraint) where
op :: a -> a -> a
instance Commutative Integer Addition where
op = plus

Resources