Haskell partial type comparison - haskell

Ive been trying to make a function for displaying the reduced data type of simple lambda calculus given a context. For this I need to compare a type of format
a :-> b
to a type of format
a
I havent been able to find a way of partially comparing the types of data like this, i.e. checking whether the type of one thing contains the type of another. If there's a simple way to do this I cant seem to find it online.
These types are generic and can be anything (e.g. a could be (Foo :-> Bar) :-> Int or anything really, which is why I cant just use a base type)

I'm not 100% clear on exactly what you want, but suppose it were like this:
data Type baseTypes = Type baseTypes :-> Type baseTypes | Base baseTypes
Then you could write something like:
funCallCompatible :: Eq b => Type b -> Type b -> Maybe (Type b)
funCallCompatible (a :-> b) a' = if a == a' then Just b else Nothing
funCallCompatible _ _ = Nothing
Is that the kind of thing you're talking about?
For a more advanced treatment of typechecking, you'll want to read about unification. Google should help get you started. I can also strongly recommend the book Types and Programming Languages.

Related

Abstract data types in a type class definition

I'm trying to understand what's happening with the type s below:
class A a where
f :: a -> s
data X = X
instance A X where
f x = "anything"
I expected this to work, thinking that since type s isn't bound to anything, it could be anything. But the compiler says that it "Couldn't match expected type ‘s’ with actual type ‘[Char]’", as if type s was a fixed type like Int, Char…
So my second interpretation was to say that, since we don't know anything about s in the type class declaration, we cannot tell when making X an instance of A if the return value of the function f we give matches type s or not. But there are type classes that use abstract data types that are not bound to anything without problems, like Functor:
class Functor f where
fmap :: (a -> b) -> f a -> f b
Why is the type s above a problem when types a and b here aren't?
You're trying to express this:
f :: a -> ∃s . s
...but what the signature you've written says is actually
f :: a -> ∀s . s
What does all of that mean?
The existential type ∃s . s means, the functions may return a value of some type, i.e. “there exists a type s such that the function returns an s value”.This is not supported by the Haskell language, because it turns out to be pretty useless.
The universal type ∀s . s means, the function is able to produce a value of any type, i.e. “for all types s, the function can return an s value”.
The latter is very useful; fmap is actually a good example: that function works, no matter what types a and b are, and the user is always guaranteed that the result will actually have the desired type, namely f b.
But that means you can't just pick some particular type in the implementation, like you did with String. ...Well, actually you can do that, but only by wrapping the existential in a data type:
{-# LANGUAGE ExistentialQuantification, UnicodeSyntax #-}
data Anything = ∀ s . Anything s
class A a where
f :: a -> Anything
instance A X where
f x = Anything "anything"
...but as I said, this is almost completely useless, because when somebody wants to use that instance they'll have no way to know what particular type the wrapped result value has. And there is nothing you can do with a value of completely unknown type.

Data families vs Injective type families

Now that we have injective type families, is there any remaining use case for using data families over type families?
Looking at past StackOverflow questions about data families, there is this question from a couple years ago discussing the difference between type families and data families, and this answer about use cases of data families. Both say that the injectivity of data families is their greatest strength.
Looking at the docs on data families, I see reason not to rewrite all uses of data families using injective type families.
For example, say I have a data family (I've merged some examples from the docs to try to squeeze in all the features of data families)
data family G a b
data instance G Int Bool = G11 Int | G12 Bool deriving (Eq)
newtype instance G () a = G21 a
data instance G [a] b where
G31 :: c -> G [Int] b
G32 :: G [a] Bool
I might as well rewrite it as
type family G a b = g | g -> a b
type instance G Int Bool = G_Int_Bool
type instance G () a = G_Unit_a a
type instance G [a] b = G_lal_b a b
data G_Int_Bool = G11 Int | G12 Bool deriving (Eq)
newtype G_Unit_a a = G21 a
data G_lal_b a b where
G31 :: c -> G_lal_b [Int] b
G32 :: G_lal_b [a] Bool
It goes without saying that associated instances for data families correspond to associated instances with type families in the same way. Then is the only remaining difference that we have less things in the type-namespace?
As a followup, is there any benefit to having less things in the type-namespace? All I can think of is that this will become debugging hell for someone playing with this on ghci - the types of the constructors all seem to indicate that the constructors are all under one GADT...
type family T a = r | r -> a
data family D a
An injective type family T satisfies the injectivity axiom
if T a ~ T b then a ~ b
But a data family satisfies the much stronger generativity axiom
if D a ~ g b then D ~ g and a ~ b
(If you like: Because the instances of D define new types that are different from any existing types.)
In fact D itself is a legitimate type in the type system, unlike a type family like T, which can only ever appear in a fully saturated application like T a. This means
D can be the argument to another type constructor, like MaybeT D. (MaybeT T is illegal.)
You can define instances for D, like instance Functor D. (You can't define instances for a type family Functor T, and it would be unusable anyway because instance selection for, e.g., map :: Functor f => (a -> b) -> f a -> f b relies on the fact that from the type f a you can determine both f and a; for this to work f cannot be allowed to vary over type families, even injective ones.)
You're missing one other detail - data families create new types. Type families can only refer to other types. In particular, every instance of a data family declares new constructors. And it's nicely generic. You can create a data instance with newtype instance if you want newtype semantics. Your instance can be a record. It can have multiple constructors. It can even be a GADT if you want.
It's exactly the difference between the type and data/newtype keywords. Injective type families don't give you new types, rendering them useless in the case where you need that.
I understand where you're coming from. I had this same issue with the difference initially. Then I finally ran into a use case where they're useful, even without a type class getting involved.
I wanted to write an api for dealing with mutable cells in a few different contexts, without using classes. I knew I wanted to do it with a free monad with interpreters in IO, ST, and maybe some horrible hacks with unsafeCoerce to even go so far as shoehorning it into State. This wasn't for any practical purpose, of course - I was just exploring API designs.
So I had something like this:
data MutableEnv (s :: k) a ...
newRef :: a -> MutableEnv s (Ref s a)
readRef :: Ref s a -> MutableEnv s a
writeRef :: Ref s a -> a -> MutableEnv s ()
The definition of MutableEnv wasn't important. Just standard free/operational monad stuff with constructors matching the three functions in the api.
But I was stuck on what to define Ref as. I didn't want some sort of class, I wanted it to be a concrete type as far as the type system was concerned.
Then late one night I was out for a walk and it hit me - what I essentially want is a type whose constructors are indexed by an argument type. But it had to be open, unlike a GADT - new interpreters could be added at will. And then it hit me. That's exactly what a data family is. An open, type-indexed family of data values. I could complete the api with just the following:
data family Ref (s :: k) :: * -> *
Then, dealing with the underlying representation for a Ref was no big deal. Just create a data instance (or newtype instance, more likely) whenever an interpreter for MutableEnv is defined.
This exact example isn't really useful. But it clearly illustrates something data families can do that injective type families can't.
The answer by Reid Barton explains the distinction between my two examples perfectly. It has reminded me of something I read in Richard Eisenberg's thesis about adding dependent types to Haskell and I thought that since the heart of this question is injectivity and generativity, it would be worth mentioning how DependentHaskell will deal with this (when it eventually gets implemented, and if the quantifiers proposed now are the ones eventually implemented).
What follows is based on pages 56 and 57 (4.3.4 Matchability) of the aforementioned thesis:
Definition (Generativity). If f and g are generative, then f a ~ g b implies f ~ g
Definition (Injectivity). If f is injective, then f a ~ f b implies a ~ b
Definition (Matchability). A function f is matchable iff it is generative and injective
In Haskell as we know it now (8.0.1) the matchable (type-level) functions consist exactly of newtype, data, and data family type constructors. In the future, under DependentHaskell, one of the new quantifiers we will get will be '-> and this will be used to denote matchable functions. In other words, there will be a way to inform the compiler a type-level function is generative (which currently can only be done by making sure that function is a type constructor).

Haskell type keyword used with signature

In the code from Scrap Your Zippers, what does the following line mean:
type Move a = Zipper a -> Maybe (Zipper a)
Type is a synonym for a type and uses the same data constructors, so this make no sense. How is it used here?
type allows us to make synonyms, as you say. This means we can make shortened versions of long and complicated types. Here is the definition of the String base type. Yes, this is how it's defined:
type String = [Char]
This allows us to make types more readable when we write them; everyone prefers seeing String to [Char].
You can also have type arguments like in the data keyword. Here are some Examples:
type Predicate t = t -> Bool
type Transform t = t -> t
type RightFoldSignature a b = (a -> b -> b) -> b -> [a] -> b
type TwoTuple a b = (a,b)
type ThreeTuple a b c = (a,b,c)
... And so on. So, there's nothing particularly strange going on with the declaration you have there - the author is making a type synonym to make things easier to write and clearer to read, presumably to be used in the types of the functions the author wants to create.
Learn you a Haskell has it's own little section on this, a list of the different declarations can be found here, and an article here.

Are type synonyms with typeclass constraints possible?

Feel free to change the title, I'm just not experienced enough to know what's really going on.
So, I was writing a program loosely based on this, and wrote this (as it is in the original)
type Row a = [a]
type Matrix a = [Row a]
Nothing special there.
However, I found myself writing a couple of functions with a type like this:
Eq a => Row a -> ...
So I thought that perhaps I could write this constraint into the type synonym definition, because to my mind it shouldn't be that much more complicated, right? If the compiler can work with this in functions, it should work as a type synonym. There are no partial applications here or anything or some kind of trickery (to my eyes).
So I tried this:
type Row a = Eq a => [a]
This doesn't work, and the compiler suggested switching on RankNTypes. The option made it compile, but the functions still required that I leave the Eq a => in their type declarations. As an aside, if I tried also having a type synonym like type Matrix a = [Row a] like before, it results in an error.
So my question(s) are thus:
Is it possible to have a type synonym with a typeclass constraint in its definition?
If not, why?
Is the goal behind this question achievable in some other way?
Constraints on a type variable can not be part of any Haskell type signature.
This may seem a bit of a ridiculous statement: "what's (==) :: Eq a => a -> a -> a then?"
The answer is that a doesn't really exist, in much the same way there is not really an x in the definition f x = x * log x. You've sure enough used the x symbol in defining that function, but really it was just a local tool used in the lambda-abstraction. There is absolutely no way to access this symbol from the outside, indeed it's not required that the compiler even generates anything corresponding to x in the machine code – it might just get optimised away.
Indeed, any polymorphic signature can basically be read as a lambda expression accepting a type variable; various writing styles:
(==) :: forall a . Eq a => a -> a -> a
(==) :: ∀ a . Eq a => a -> a -> a
(==) :: Λa. {Eq a} -> a -> a -> a
This is called System F.
Note that there is not really a "constraint" in this signature, but an extra argument: the Eq-class dictionary.
Usually you want to avoid having constraints in your type synonyms unless it's really necessary. Take the Data.Set API from containers for example.
Many operations in Data.Set require the elements of the set to be instances of Ord because Set is implemented internally as a binary tree. member or insert both require Ord
member :: Ord a => a -> Set a -> Bool
insert :: Ord a => a -> Set a -> Set a
However the definition of Set doesn't mention Ord at all.
This is because some operations on Set dont require an Ord instance, like size or null.
size :: Set a -> Int
null :: Set a -> Bool
If the type class constraint was part of the definition of Set, these functions would have to include the constraint, even though it's not necessary.
So yes, it is possible to have constraints in type synonyms using RankNTypes, but it's generally ill-advised. It's better to write the constraint for the functions that need them instead.

Why aren't there existentially quantified type variables in GHC Haskell

There are universally quantified type variables, and there are existentially quantified data types. However, despite that people give pseudocode of the form exists a. Int -> a to help explain concepts sometimes, it doesn't seem like a compiler extension that there's any real interest in. Is this just a "there isn't much value in adding this" kind of thing (because it does seem valuable to me), or is there a problem like undecidability that's makes it truly impossible.
EDIT:
I've marked viorior's answer as correct because it seems like it is probably the actual reason why this was not included. I'd like to add some additional commentary though just in case anyone would want to help clarify this more.
As requested in the comments, I'll give an example of why I would consider this useful. Suppose we have a data type as follows:
data Person a = Person
{ age: Int
, height: Double
, weight: Int
, name: a
}
So we choose parameterize over a, which is a naming convention (I know that it probably makes more sense in this example to make a NamingConvention ADT with appropriate data constructors for the American "first,middle,last", the hispanic "name,paternal name,maternal name", etc. But for now, just go with this).
So, there are several functions we see that basically ignore the type that Person is parameterized over. Examples would be
age :: Person a -> Int
height :: Person a -> Double
weight :: Person a -> Int
And any function built on top of these could similarly ignore the a type. For example:
atRiskForDiabetes :: Person a -> Bool
atRiskForDiabetes p = age p + weight p > 200
--Clearly, I am not actually a doctor
Now, if we have a heterogeneous list of people (of type [exists a. Person a]), we would like to be able to map some of our functions over the list. Of course, there are some useless ways to map:
heteroList :: [exists a. Person a]
heteroList = [Person 20 30.0 170 "Bob Jones", Person 50 32.0 140 3451115332]
extractedNames = map name heteroList
In this example, extractedNames is of course useless because it has type [exists a. a]. However, if we use our other functions:
totalWeight :: [exists a. Person a] -> Int
totalWeight = sum . map age
numberAtRisk :: [exists a. Person a] -> Int
numberAtRisk = length . filter id . map atRiskForDiabetes
Now, we have something useful that operates over a heterogeneous collection (And, we didn't even involve typeclasses). Notice that we were able to reuse our existing functions. Using an existential data type would go as follows:
data SomePerson = forall a. SomePerson (Person a) --fixed, thanks viorior
But now, how can we use age and atRiskForDiabetes? We can't. I think that you would have to do something like this:
someAge :: SomePerson -> Int
someAge (SomePerson p) = age p
Which is really lame because you have to rewrite all of your combinators for a new type. It gets even worse if you want to do this with a data type that's parameterized over several type variables. Imagine this:
somewhatHeteroPipeList :: forall a b. [exists c d. Pipe a b c d]
I won't explain this line of thought any further, but just notice that you'd be rewriting a lot of combinators to do anything like this using just existential data types.
That being said, I hope I've give a mildly convincing use that this could be useful. If it doesn't seem useful (or if the example seems too contrived), feel free to let me know. Also, since I am firstly a programmer and have no training in type theory, it's a little difficult for me to see how to use Skolem's theorum (as posted by viorior) here. If anyone could show me how to apply it to the Person a example I gave, I would be very grateful. Thanks.
It is unnecessary.
By Skolem's Theorem we could convert existential quantifier into universal quantifier with higher rank types:
(∃b. F(b)) -> Int <===> ∀b. (F(b) -> Int)
Every existentially quantified type of rank n+1 can be encoded as a universally quantified type of rank n
Existentially quantified types are available in GHC, so the question is predicated on a false assumption.

Resources